[Spring/Java] @Builder 패턴 사용시 @AllArgsConstructor를 사용하는 이유

resilient

·

2023. 3. 22. 00:49

728x90
반응형

현재 개발을 할 때, 일반적으로 Entity 객체를 생성하는 경우 Lombok 라이브러리를 사용해서 빌더 패턴을 적용하고 있습니다.
 
Entity 클래스에 적용된 어노테이션 중 @Builder라는 어노테이션이 있는데요. @Builder 어노테이션을 사용할 경우, 기본 생성자를 생성해 주는 @NoArgsConstructor 어노테이션을 사용하고 있습니다.
 
일반적으로 기본 생성자에 대한 접근 제어를 위해 @NoArgsConstructor(access=AccessLevel.PROTECTED)와 같이 사용하곤 합니다. 이유는 무분별하게 생성되는 객체들을 한 번 더 체크해 줌으로써 의도하지 않은 엔티티 객체를 만드는 행위를 방지할 수 있습니다.
 
@Builder와 @NoArgsConstructor 어노테이션을 동시에 사용하려고 보니! 아래와 같이 컴파일 에러가 발생합니다.

@Builder에 피가 납니다.

 
찾아보니 @AllArgsConstructor를 같이 써주면 해결이 된다고 해서
@Builder,@NoArgsConstructor,@AllArgsConstructor 총 세 개의 어노테이션을 동시에 사용하고 있었는데 이유가 궁금해졌습니다.
 
예전에 @AllArgsConstructor 사용에 대한 추측을 해본 적이 있는데 다음과 같습니다.
 
부모 클래스의 생성자에 매개 변수가 필요한 경우 상속받은 자식 클래스 객체가 생성될 때 부모 클래스의 생성자도 실행이 됩니다.
 
부모 클래스의 생성자가 매개 변수를 받는다면 이때는 super() 메서드를 사용해서 부모 클래스의 생성자에 매개변수를 넘겨주면 되죠.
 
이때 주의할 점은 자식 클래스의 생성자에 super() 메서드가 없다면 Java에서는 부모 클래스(상위 클래스)의 기본 생성자를 찾습니다. 하지만 지금의 경우와 같이 매개 변수를 받는 생성자만 있고, 기본 생성자가 없는 경우 오류가 발생하게 됩니다. 따라서 기본 생성자도 만들어주고, 매개 변수를 받는 생성자를 만들어놔야 하기 때문에 @Builder에서 요구를 하나? 였죠.
 
약간 비슷한 이유긴 하지만 제대로 알고 넘어가기 위해서 Builder와 관련된 어노테이션들을 약간 더 깊게 학습을 해봤습니다.
(Builder패턴에 대한 내용은 이 글에서 다루지 않습니다. 정리가 잘 되어있는 블로그를 공유해 놓겠습니다.)
 

@Builder와 @NoArgsConstructor을 동시에 사용할 경우 에러가 발생하는 이유

 
@Builder와 @NoArgsConstructor을 동시에 사용할 경우 에러가 발생하는 이유는 당연합니다. 전체 생성자가 없기 때문이죠.
 
@Builder를 통해서 필요한 파라미터만 받아서 객체를 만들 수도 있고, 전체 파라미터를 받아서 객체를 만들 수도 있는데 전체 생성자가 없으면 당연히 에러가 발생합니다.
 
그렇다면 @NoArgsConstuctor 없이 @Builder만 사용하면 에러가 발생해야 하는데요.
 
@Builder만 사용할 경우에는 에러가 발생하지 않습니다. 그 이유를 알아보도록 하겠습니다.
 

@Builder

 
@Builder 어노테이션 내부를 살펴보겠습니다.
 

@Builder 내부 description

 
@~ArgsConstructor 주석을 추가하지 않은 경우에만 @AllArgsConstructor 어노테이션의 역할과 같이 모든 필드를 파라미터로 사용하는 생성자가 생성이 됩니다.
 
정리해 보면 @NoArgsConstructor 어노테이션이나 다른 생성자들이 존재하지 않을 경우 전체 생성자를 자동을 생성해 주고, 기본 생성자나 다른 생정자들이 존재하면 @AllArgsConstructor을 사용해서 직접 전체 생성자를 만들어줘야 하는 것이죠.
 
결과적으로 가장 효율적으로 필요한 객체만 생성해서 사용하기 위한 좋은 방법인 @NoArgsConstructor와 @Builder를 함께 쓰려면 @AllArgsConstructor을 이용하면 됩니다.
 
물론 다른 방법도 존재합니다. 아래와 같이 생성자를 직접 만들어서 사용하고 그 위에 @Builder를 사용해 주면 됩니다.
 

@Entity
@Table(name = "circle")
@Builder
@Getter
public class Circle extends BaseTimeEntity {

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    private Long id;

    private String circleName;
    
    private String description;
    
    @Builder
    public Circle(String circleName){
        this.circleName = circleName;
    }
    
    @Builder
    public Circle(String description,String circleName){
        this.circleName = circleName;
        this.description = description;
    }
  
}

 
저는 '@AllArgsConstructor을 사용함으로써 필요하지 않은 기능을 추가하지는 않기 때문에 첫 번째 방법처럼 @AllArgsConstructor을 사용하는 것이 더 좋지 않을까?' 라고 생각했고 현재 동일한 방법으로 사용 중입니다.
 

정리

 
Builder 패턴은 생성자와 수정자로 관리하는 패턴과 비교했을 때 굉장히 가독성을 높이고 변경 가능성을 최소화할 수 있는 좋은 패턴이고 객체를 생성할 때 꼭 사용해야 하는 패턴이라고 생각합니다.
 
@Builder어노테이션을 사용할 상황에서 무지성으로 사용하지 않고 왜 @AllArgsConstructor와 @NoArgsConstructor을 함께 사용하는지에 대해서 알아봤고 앞으로도 많은 어노테이션들을 사용할 때 왜 이렇게 사용하는지에 대해서 학습을 한 뒤에 사용해보려고 합니다.
 
감사합니다.
 
 
 
 
 
 

반응형