Giter Club home page Giter Club logo

spring-guide's Introduction

CircleCI Coverage Status Hits

Spring Guide

소개

Spring Boot 기반 Rest API를 개발할 때 유지 보수하기 좋은 코드를 만들기 위해서 평소 생각했던 가이드를 연하고 있습니다. 테스트 코드, 예외처리, 올바른 서비스의 크기, 프로젝트 구조 등에 대해서 주로 다룰 예정입니다. Start, Watching 버튼을 누르시면 구독 신청받으실 수 있습니다. 저의 경험이 여러분에게 조금이라도 도움이 되기를 기원합니다.

프로젝트 실행 방법

$ git clone [email protected]:cheese10yun/spring-guide.git
$ cd spring-guide
$ docker-compose up -d
$ ./mvnw clean package
$ java -jar -Dspring.profiles.active=local api-service/target/api-service-0.0.1-SNAPSHOT.jar

목차

  1. Test 전략 가이드
  2. Exception 전략 가이드
  3. Domain 객체 가이드
  4. 외부 API 가이드
  5. Service 적절한 크기 가이드
  6. Directory 가이드

spring-guide's People

Contributors

cheese10yun avatar fleta avatar hejow avatar jacobis avatar jchyng avatar kgh2120 avatar kiyoungyi avatar sarguments avatar ssung0810 avatar zieunx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spring-guide's Issues

안녕하세요. 궁금한게 있어서 여쭤보고 싶은데 Issue를 통해서 문의 드리는게 맞을까 싶네요 ^^;;

친절하면서 멋지게 작성된 소스 내용을 보던 중 의도가 너무 궁금해서 이렇게 문의 드립니다.
만약 불쾌함을 드렸다면 정말 사과 드립니다.

질문1. MemberFindDao에서 @repository가 아닌 @service를 사용하신 이유가 뭘까요?

질문2. MemberFindDao에 있는 두 개의 findBy* 메소드는�MemberRepository에서 제공이 가능 한데도 불구하고 DAO 클래스를 선언하신 이유는 뭔가요? (추측하기로 리턴값이 Optional.empty인 경우 적절한 exception 사용을 위한게 아닐까 생각했습니다.)

질문3. 만약 [질문2]에 대한 저의 추측이 맞다면, 비슷한 기능(?)을 하는 findById, findByEmail의 메소드는 두 군데(Dao, Repository)에 존재 하게 될테고 메소드가 사용되는 관점에서 봤을때 같은 혹은 비슷한 기능의 메소드가 중복으로 존재하는게 될텐데 의도하신 건가요?
(물론 리턴값(Optional, Member)이나 리턴되는 exception은 차이가 있지만 비즈니스는 유사해 보입니다.)

여기까지 읽어 주신것 만으로도 감사 드립니다 ^^
(후에 더 여쭤보고 싶은데 이메일이라도 알려주시면 감사하겠습니다 [email protected])

Directory Guide 관련 내용 공유드립니다.

안녕하세요. 먼저 정말 좋은 자료 공유해 주셔서 감사합니다! 😊
항상 블로그나 공유해 주신 자료들 통해 정말 많은 도움을 받고 있습니다.

Directory Guide 관련하여 여러 가지 시도도 해보고 공부해보면서 접한 방식이 있는데, Hexagonal Architecture를 적용하여 패키지를 나누는 방식이고, 실제로 적용해봤을 때도 의존의 방향을 도메인으로 집중시킬 수 있는 좋은 방법이라고 생각되더라구요!

[2019] DDD Lite@Spring
공부하면서 접하게된 영상이고, 약 35분에 헥사고날 아키텍처 소개, 약 38분 30초에 패키지 구조에 대해서 설명하십니다.

혹시라도 시간 괜찮으시다면 영상 확인해보시고, 내용이 괜찮다면 내용 추가해주시거나 PR 요청 보내드릴테니 검토해주시면 영광이겠습니다..!

다시 한번 좋은 내용 공유 감사드립니다!

Error message globalization

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
@Getter
public enum ErrorCode {

    // Common
    INVALID_INPUT_VALUE(400, "C001", "Invalid input value"),
    METHOD_NOT_ALLOWED(405, "C002", "Method not allowed"),
    ENTITY_NOT_FOUND(400, "C003", "Entity is not found"),
    INTERNAL_SERVER_ERROR(500, "C004", "Server error"),
    INVALID_TYPE_VALUE(400, "C005", "Invalid type value"),
    HANDLE_ACCESS_DENIED(403, "C006", "Access is denied"),


    // Admin
    ADMIN_NOT_FOUND(400, "A001", "Admin account is not found"),
    ADMIN_PASSWORD_MISMATCHED(400, "A002", "Admin password is mismatched"),
    ADMIN_AND_GROUP_NOT_MAPPED(400, "A003", "Admin and root group is not mapped."),
    
    ;

    private final String code;
    private final String message;
    private final int status;

    ErrorCode(final int status, final String code, final String message) {
        this.status = status;
        this.message = message;
        this.code = code;
    }
}

안녕하세요. 쓰신 글 너무 감명깊게 읽었습니다. 그래서 제가 현재 진행중인 프로젝트에 @cheese10yun 님의 global exception 전략을 적용하고 싶은데 한 가지 문제가 생겨서 여쭤보고 싶어서 이렇게 글 남깁니다.
먼저 제가 이해한 바로는 @Valid로 예외가 발생한 경우에만 errors에 해당 FieldError가 담기고 그 이외에 개발자가 의도적으로 발생시켜준 예외(BussinessException을 상속받은 예외)에는 FieldError가 담기지 않는 거로 저는 이해했습니다.

현재 저는 웹 프로젝트를 개발하고 있는데 해당 프로젝트는 4개 국어(한국어, 영어, 일본어, 스페인어)를 지원하기 때문에 에러 메시지 역시 국제화를 해줘야 합니다. 하나의 언어만 지원한다면 에러 메시지를 ErrorCode의 message에 적어주면 좋겠지만, 4개 국어를 지원하기 때문에 에러 메시지가 동적으로 바뀌어야 합니다. 그래서 messages(messages_ko.properties, messages_en.properties, messages_ja.properties, messages_es.properties)에 각각의 언어로 적힌 에러메시지를 정의해놓고, 해당 메시지를 locale 값을 기준으로 동적으로 ErrorCode의 메시지 부분에 바인딩 해주고 싶습니다. FieldError 같은 경우는 필드에 메시지를 정의해주면 알아서 번역이 되지만(e.g. @NotNull(message = "{login.password.notnull}")) 필드에서 발생하는 에러가 아닌 개발자가 던지는 에러에 에러 메시지는 어떻게 동적을 바인딩할 수 있는지 잘 감이 안 옵니다... 제가 하고 싶은 방법은 2가지 중에 하나입니다.

  • ErrorCode enum class에서 -> INVALID_INPUT_VALUE(400, "c001", "{input.invalid_value}") 같이 작성을 하면 Locale 값에 의해 동적으로 메시지 바인딩
  • 에러를 던지는 부분 코드에서, throw new AdminUserNotFoundException("{input.invalid_value}", ErrorCode.ADMIN_NOT_FOUND);

사실 2번은 제가 첨부한 코드에서 이미 error message를 정의한 상태에서 메시지 부분만 오버라이팅한다는 점이 조금 어색하지만 enum class 내에서 resouces의 메시지를 동적으로 바인딩할 수 없다면 2번도 나쁘지 않다고 생각합니다. @cheese10yun 님 같으면 어떻게 하실지 궁금합니다.

그리고 웹 프로젝트다 보니, 예외가 발생하면 사용자에게 에러 메시지를 보여주려고 하는데, 만약 field error 값이 담겨있다면 field error의 reason 값을 에러 메시지로 보여주고 그 이외에 개발자가 발생시킨 에외는 field error가 없다보니 message 필드 값을 보여주려고 하는데, 그냥 일관성 있게 예외가 발생하면 message 부분만 보여주는 게 더 나을려나요?

너무 두서없이 글을 써서 죄송하고 답변해주시면 정말 감사하겠습니다!!

ErrorResponse를 ResponseEntity로 감싸는 구조

안녕하세요, @cheese10yun
spring-guide 프로젝트를 보다가 GlobalExceptionHandler의 Return 값을 ResponseEntity로 감싸는 구조에 대해서 의문이 들어서 @cheese10yun 님의 의견을 듣고 싶어 이렇게 여쭤봅니다... 왜냐하면 ErrorResponse 자체의 status 값이 ResponseEntity 값의 status 값과 유사한 느낌이 듭니다. GloblaException에서 ResponseEntity 로 해주는 것보다 ErrorCode의 status 값을 status 값을 HttpStatus.INTERNAL_SERVER_ERROR 이런 식으로 변경하고 ErrorResponse 자체만 넘기는 게 낫지 않을까요?

또한, MemberResponse, CouponResponse 이런 reponse dto는 왜 ResponseEntity로 감싸지 않고 그냥 해당 객체만 return 하신 건지도 궁금합니다. 정상적으로 반환이 이뤄질 때는 status code나 message가 따로 필요하지 않다고 생각하셔서 이렇게 하신 건가요?

오탈자 제보

https://cheese10yun.github.io/spring-guide-test-1/ 에서 '것보다 모' 부분이 글자가 누락된 것 같습니다.

모든 response에 대한 andExpect를 작성합니다. 간혹 .andExpect(content().string(containsString(""))) 이런 테스트를 진행하는데 특정 문자열이 들어 있는지 없는지 확인하는 것은 것보다 모
response에 하나라도 빠지거나 변경되면 API 변경이 이루어진 것이고 그 변경에 맞게 테스트 코드도 변경되어야 합니다.

domain의 결합도

안녕하세요. 너무 질문을 많이 드리는 건 아닌지 죄송하네요 ㅠㅠ
저도 이번 프로젝트를 @cheese10yun 님이 작성하신 방법으로 Domain 별로 패키징을 해서 코드를 작성하고 있는데요, 하지만 제가 하고 있는 프로젝트의 경우, db를 읽어오는 Repository 단에서 해당 도메인이 아닌 타 도메인에 의존을 할 수밖에 없어서 @cheese10yun 님이 작성하신 것처럼 깔끔한 도메인 구조를 가져가는 데 제약이 있습니다.

상황은 다음과 같습니다.

비즈니스 로직이 항상 그룹 단위로 이루어져 있어서, Group Entity에 의존할 수밖에 없습니다. 예를 들어, Owner 정보를 가져오는 데도 현재 currentGroup에 속한 owner를 가져와야 하고, activity 정보를 가져온다고 해도, 현재 currentGroup에 속한 activity만 가져와야 해서. DB에서 데이터를 가져올 때, 반드시 Group entity와 join을 해서 가져와야 합니다. Group 자체적으로 진행되는 API는 없어서 Group만 도메인을 만들기도 참 애매합니다. 그래서 제가 생각한 방법은 아래 두 가지입니다.

  1. Group 도메인을 만들어서 각 도메인의 서비스 단에서 GroupRepository를 이용해 Group 정보를 가져온 다음 각 도메인 서비스 단에서 필요한 정보를 가공하는 방법. 하지만 이렇게 하면 DB 단에서 join하면 되는 걸, Service 단에서 자바 코드를 필터링을 해야하니, 많이 비효율적일 거 같습니다.
  2. Group entity는 그냥 각 도메인 Repository 단에서 join하는 데만 사용하고 별도의 Group 도메인은 두지 않는다.

두 방법 모두 어느 정도의 결합도가 있는 거 같지만, 제 생각에는 2번 방법이 조금 나은 거 같은데 @cheese10yun 님 생각은 어떠신지 궁금합니다.

감사합니다.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.