문득 @Builder를 사용하면서 intelliJ가 시키는 대로 @NoArgsConstructor, @AllArgsConstructor를 추가했는데요
이게 왜 필요할까 궁금해져서 @Builder에 대해 알아보았습니다.
@Builder
* If a member is annotated, it must be either a constructor or a method. If a class is annotated,
* then a package-private constructor is generated with all fields as arguments
* (as if {@code @AllArgsConstructor(access = AccessLevel.PACKAGE)} is present
* on the class), and it is as if this constructor has been annotated with {@code @Builder} instead.
* Note that this constructor is only generated if you haven't written any constructors and also haven't
* added any explicit {@code @XArgsConstructor} annotations. In those cases, lombok will assume an all-args
* constructor is present and generate code that uses it; this means you'd get a compiler error if this
* constructor is not present.
위의 설명은 @Builder의 javadoc이다. @Builder가 클래스에 적용될 때의 동작을 설명하고 있다. 여기서 중요한 점을 정리하면 다음과 같다.
- 멤버 주석: @Builder는 클래스의 멤버(생성자나 메서드)에 적용될 수 있다. 이는 특정 생성자나 메서드에 대해 Builder 패턴을 적용할 수 있음을 의미한다.
- 클래스 주석: 클래스 자체에 @Builder를 적용하면 Lombok은 모든 필드를 인수로 가지는 package-private (access level이 package 수준인) 생성자를 자동으로 생성한다.
이는 기본적으로 @AllArgsConstructor(access = AccessLevel.PACKAGE)를 추가한 것과 동일한 효과를 준다. 이 생성자는 외부에서 직접 접근할 수 없지만, 동일 패키지 내에서는 사용할 수 있다. - 조건부 생성:
- Lombok이 이러한 생성자를 자동으로 생성하는 것은 해당 클래스에 다른 생성자가 명시적으로 정의되어 있지 않을 때와 @XArgsConstructor와 같은 생성자 관련 주석이 추가되지 않았을 때에 한정된다.
- 따라서 클래스에 이미 다른 생성자가 있거나, 명시적으로 생성자를 정의하는 Lombok 주석(@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor 등)이 있는 경우 Lombok은 생성자를 자동으로 생성하지 않는다.
- 컴파일러 오류: 모든 필드를 인수로 가지는 생성자가 필요하다. 클래스에 이러한 생성자가 존재하지 않으면 컴파일러 오류가 발생한다. 즉, @Builder는 기본적으로 모든 필드를 초기화할 수 있는 생성자를 필요로 하며, 이 생성자가 없을 경우 Lombok은 이를 사용하여 인스턴스를 생성할 수 없기 때문에 오류가 발생하게 된다.
결론적으로, @Builder를 사용하여 클래스의 인스턴스를 생성할 때는 필드 초기화를 위한 생성자가 반드시 필요하며, Lombok은 특정 조건에서 이를 자동으로 생성해 주지만, 명시적으로 정의된 생성자가 있으면 이를 자동으로 생성하지 않는다.
생성자의 접근 수준을 Private으로 설정해야 하는 이유
@Builder를 사용할 때, 빌더가 호출하는 생성자는 종종 인스턴스가 빌더 패턴을 통해서만 생성되도록 private으로 설정된다. 이는 캡슐화를 강화하고 직접 인스턴화하는 것을 방지하기 위함이다.
- 캡슐화: 생성자를 private으로 설정하면 모든 클래스 인스턴스가 빌더를 사용하여 생성되도록 보장한다. 이는 일관된 객체 초기화를 가능하게 하고, 복잡한 초기화 로직이나 검증 과정을 빌더 내부에 캡슐화할 수 있다.
- 통제된 인스턴스화: 외부 클래스가 Builder를 우회하여 직접 인스턴스를 생성하지 못하도록 방지한다. 이는 초기화 로직이 빌더에 포함되어 있거나 추가적인 생성 로직이 필요할 때 필요할 수 있다.
번외: @NoArgsConstructor는 왜 필요한가?
Class 'User' should have [public, protected] no-arg constructor
이는 Spring 프레임워크 요구사항과 관련있다.
- JPA/Hibernate: JPA와 Hibernate 같은 많은 Java 프레임워크는 객체를 reflection를 통해 인스턴스화하기 위해 no-argument 생성자가 필요하다. 이 생성자는 최소한 protected 또는 package-private이어야 하며, 프레임워크가 접근할 수 있어야 한다.
- Reflection이란 java 런타임 시에 클래스, 메서드, 필드를 동적으로 처리해준다. 예를 들어, Spring은 Reflection을 통해 의존성 주입(Dependency Injection)을 구현한다.
- 직렬화 라이브러리: Jackson과 같은 직렬화 및 역직렬화(Serialization/Deserialization) 라이브러리도 클래스 인스턴스를 생성하기 위해 no-args 생성자를 필요로 한다. 생성자가 private이면 이러한 라이브러리가 객체를 제대로 생성할 수 없다.
참고
'Development > Spring' 카테고리의 다른 글
[Spring] HttpSession과 Session Clustering (0) | 2024.08.06 |
---|---|
[Spring] MSA와 Spring Cloud (1) | 2024.07.31 |
[Spring] MVC 패턴과 Spring MVC (0) | 2024.07.22 |
[Spring] 서버 모니터링을 위한 Spring Actuator, Prometheus, Grafana 추가하기 (0) | 2024.07.12 |
[Spring] Redisson을 활용한 캐시 사용하기 (0) | 2024.07.11 |