[Java] Lombok @Builder 와 @XArgsConstructor

나르·2022년 10월 16일
0

JAVA

목록 보기
12/18
post-thumbnail

본인은 회사에서도 그렇고, 개발할 때 최근에는 주로 NoSQL을 사용하고 있다.
(그리고 RBDMS를 사용하더라도 JPA를 사용하고있지는 않아서 JPA는 문외한이기도 하다)

최근 사이드 프로젝트를 진행하는데, 팀원이 Document 클래스에 @Builder, @RequiredArgsConstructor, @NoArgsConstructor 을 주렁주렁 달아둔 것을 보고 실제로는 빌더만 사용하니 나머지는 지워도 될 듯 하다고 피드백한 적이 있다.
그런데 되려 왜 지워도 되냐는 질문을 받았고, 내친김에 이에 대해 찾아보게 되었다.

Mongo에서는 아래같이 빌더만 사용해도 잘 작동하는데, MySQL에서는 안되는 이유가 무엇일까?

// MongoDB
@Getter
@Builder
@Document
public class Member {
}

// MySQL
@Getter
@Builder
@RequiredArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Member {
}

@XArgsConstructor

롬복의 생성자 어노테이션은 다음과 같이 3가지가 있다.

  • @NoArgsConstructor : 파라미터가 없는 디폴트 생성자 생성
  • @AllArgsConstructor : 모든 필드를 받는 생성자 생성
  • @RequiredArgsConstructor : 필수(final) 필드만 받는 생성자 생성

그리고 이 생성자 어노테이션에 속한 속성은 다음과 같다.

  • staticName : @Data의 staticConstructor 속성과 같은 역할을 한다. (=static한 생성자를 만들어준다.)
  • access : 'PUBLIC', 'MODULE', 'PROTECTED', 'PACKAGE', 'PRIVATE' 의 값으로 접근 제한자를 설정할 수 있다.
  • onConstructor : 생성자에 어노테이션을 작성할 수 있다.
  • force : NoArgsConstructor에만 존재하는 속성으로, final 등 필수 필드를 0/null/false 로 강제 초기화는 기본생성자를 생성한다.

결론을 말하자면, RDBMS가 아니라 JPA와 관련된 문제이다.

JPA 에서는 영속성 컨텍스트를 만드려면 Entity는 public / protected 레벨의 생성자를 가지고 있어야한다.
@Builder는 package-private 레벨의 생성자를 만들기 때문에, jpa를 사용할 경우 해당 레벨의 다른 생성자가 없으면 에러가 발생하는 것이다.


그렇다고 냅다 @NoArgsConstructor 만 추가해버리면 빌더에서 컴파일 에러가 발생한다.
@AllArgsConstructor 를 추가하면 해결되는 것은 알고 있었는데, 그 이유에 대해서도 찾아보았다.

@NoArgsConstructor 만 추가했을 시 발생하는 에러 메세지는 다음과 같다.
모든 멤버변수를 받는 생성자가 없기 때문이다.

롬복 빌더는 특정 매개변수를 받는 생성자든, 롬복 생성자든 정의한 생성자가 있는 상황이라면 그 중 @Builder 어노테이션이 붙어있는 생성자를 사용하게 된다.

생성자에 @Builder 를 붙이지 않고 클래스에 @Builder 를 붙인 경우, 기본적으로 Lombok은 @AllArgsConstructor(access = AccessLevel.PACKAGE) 를 만들고, 그 위에 @Builder를 붙였다고 간주하고 빌더를 만든다.

Finally, applying @Builder to a class is as if you added @AllArgsConstructor(access = AccessLevel.PACKAGE) to the class and applied the @Builder annotation to this all-args-constructor. This only works if you haven't written any explicit constructors yourself.

하지만 클래스 내에 특정 생성자가 있다면, 롬복은 암묵적으로 @AllArgConstructor 를 만들지 않는다.

따라서 @NoArgsConstructor를 정의하고, 클래스 위치에 @Builder 를 붙인 상황이라면,
빌더는 모든 매개변수를 받는 생성자를 호출하게 되는데 실제로는 모든 매개변수를 받는 생성자가 없는 상황이 발생하게 된다.

해결책으로는
1. 클래스 위에 @AllArgsConstructor 를 이용해서 모든 매개변수를 받는 생성자를 정의하던지
2. 클래스 내에 특정 생성자 또는 팩토리 패턴의 객체 생성 메서드를 정의 후, @Builder 를 추가해야한다.

Ref. https://projectlombok.org/api/lombok/Builder

profile
💻 + ☕ = </>

0개의 댓글