@Builder 패턴, Dto

sally·2022년 6월 25일
1

JAVA

목록 보기
2/4

@Builder 패턴과 Dto

  • IssueTracker 클론 프로젝트 진행 중 PR 보낸 코드 일부에 리뷰가 달렸다. GitHub
    @Builder
    @Data
    public class MilestoneResponse {
        private final Long id;
        private final String title;
        private final String description;
        private final String completionDate;
    }

from. 리뷰어 왕민
dto 클래스에 builder 패턴은 오버스펙으로 보입니다.
빌더패턴 사용 이유에 대해서 알아봅시다 :)

빌더 패턴 사용 이유

  1. 필요한 데이터만 설정할 수 있음
  2. 유연성을 확보할 수 있음
  3. 가독성을 높일 수 있음
  4. 불변성을 확보할 수 있음
  • @Data 안에 Setter, RequiredArgsConstructor가 있는데, @Builder 까지 선언하니 오버스팩이라 생각 됐다.
    • 오버스팩을 생각하니 @Data도 안쓸 것들을 많이 들고 있는 것 같았다.
    • 그러면 쓸 것만 남겨야지.

@Data
Getter, Setter, RequiredArgsConstructor, ToString, EqualsAndHashCode, Value

😶 @Data ➜ @Getter, @RequiredArgsConstructor

    @RequiredArgsConstructor
    @Getter
    public class MilestoneResponse {
        private final Long id;
        private final String title;
        private final String description;
        private final String completionDate;
    }
  • 이번에는 예전과 다르게 dto 패키지를 별도로 둬서 작업하고 있었다.
    • 로직관련 클래스들만 도메인 패키지 내에서 볼 수 있어서 좋을 것 같았다.
    • 패키지 하나 옮겨두면, 접근제어자 고민이 새로워 진다.
  • 그래서, 새롭게 하여 새 문제를 만들었다.🙃
    • 패키지가 다르니 접근제어나 default로 안되고 getter를 쓰지 않고자 함이었는데....

from. 리뷰어 왕민
toDto 메소드에서는 엔티티가 dto클래스를 알고있어야 하네요.
dto는 도메인 객체에서 알고 있을 필요는 없다고 생각합니다.
현재 구조는 dto 클래스 변경이 있을 경우 entity객체도 변경이 필요하죠.
domain.toDto가 아닌 dto.from(entity)같은 형식으로 도메인 객체에서 dto 의존성을 제거하는건 어떨까요?

  • 맞아요.😳
    @Entity
    public class Milestone {
        MilestoneResponse toDto() {
            return MilestoneResponse.builder()
                .id(this.milestoneId)
                .title(this.milestoneTitle)
                .description(this.description)
                .completionDate(this.completionDate.toString())
                .build();
        }
    }
  • ...접근 제어자 고민이 새로워 졌다.
    • public과 getter
      • 최대한 엔티티 노출을 적게 해보려면, 생성을 제한하고, 필수 값들만 전달하게 해보자.
      • 변수가 3개 이상이니, Builder 패턴이 가독성도 좋아보인다.
	@Getter
	@Builder(access = AccessLevel.PRIVATE)
	@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
    public class MilestoneResponse {
        private final Long id;
        private final String title;
        private final String description;
        private final String completionDate;
    }
    
    public static MilestoneResponse from(Milestone milestone) {
		return MilestoneResponse.builder()
			.id(milestone.getId())
			.title(milestone.getTitle())
			.description(milestone.getDescription())
			.completionDate(milestone.getCompletionDate())
			.build();
	}
  • 근데, @Builder 를 알고 있을까? 😃

@Builder

If a certain field/parameter is never set during a build session, then it always gets 0 / null / false.

  • @Builder.Default - @Builder 사용하면서 필드별로 기본값으로 초기화 시킬 수 있다.

    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public class Pojo {

        @Builder.Default
        private String name = "짱구엄마";
        private String nickname;
        private List<PojoTwo> pojoTwos = new ArrayList<PojoTwo>();
    }
    
      public static void main(String[] args) {
        Pojo pojo = Pojo.builder().nickname("짱구친구").build();
        System.out.println(pojo.toString());

        Pojo pojo1 = new Pojo();
        System.out.println(pojo1.toString());

    }
    Pojo(name=짱구엄마, nickname=짱구친구, pojoTwos=null)
    Pojo(name=짱구엄마, nickname=null, pojoTwos=[])
  • 리스트로 초기화 한 pojoTwos는 대해, 빌더를 이용해 생성한 pojo의 pojoTwos는 null 의 결과가 나왔다.
    • @Builder 클래스를 들어가면, 어떻게 패턴이 적용되는지 예시가 나오는데, 초기화를 하지 않다보니, 객체의 기본값 null 로 결과가 나왔다.
  • @Builder(toBuilder = true) ref. baeldung
    • 만일 모든 필드가 초기화 되어 있다면, 간단하게 생성할 수 있다.
    @Builder(toBuilder = true)
    public class Pojo {
        private String name = "foo";
        private boolean original = true;
    }
  • 위에서 언급했던 @Builder 패턴은 왜 사용할까 중 아래 2가지에 대해 다시 생각해보면...
    1. 필요한 데이터만 설정할 수 있음
    4. 불변성을 확보할 수 있음
  • 0 / null / false
    • Dto를 통해 임의로 값이 들어가고, DB 저장되게 한다면
    • 중간에서 빠져버린 필드에 의해 하나의 값이 들어가지 않아 null로 변환 된다면
    • 이런 경우들을 조심해야 될 것 같다. (더 많은 정보들을 알려주세요...)

👍 refernce

profile
sally의 법칙을 따르는 bug Duck

0개의 댓글