oguri's garage

Lombok 어노테이션(@Builder, @NoArgsConstructor, @AllArgsConstructor, @RequiredArgsConstructor, @Data) 본문

개발하다/Spring

Lombok 어노테이션(@Builder, @NoArgsConstructor, @AllArgsConstructor, @RequiredArgsConstructor, @Data)

oguri 2025. 10. 2. 23:07

1. 기본 생성자 어노테이션

 

@NoArgsConstructor

  • 매개변수가 없는 기본 생성자를 생성
@NoArgsConstructor
public class User {
    private String name;
    private String email;
}

// 생성되는 코드
public User() {}

 

접근 제어자 설정

@NoArgsConstructor(access = AccessLevel.PROTECTED)  // protected 생성자
@NoArgsConstructor(access = AccessLevel.PRIVATE)    // private 생성자

[!info] 왜 접근 제어자를 설정하는가?
핵심: 누가, 어떻게 이 객체를 만들 수 있는지를 통제하기 위해

 // public 생성자 - 누구나 접근 가능 (대문이 활짝 열린 상태)
 User user = new User(); // 어디서든 가능

 // protected 생성자 - 같은 패키지나 상속받은 클래스만 접근 (가족만 출입 가능)
 User user = new User(); // 같은 패키지 내에서만 가능

 // private 생성자 - 클래스 내부에서만 접근 (본인만 출입 가능)
 User user = new User(); // 컴파일 에러! 외부에서 접근 불가

[!info] JPA를 사용할 때 생성자의 PROTECTED접근 제어자를 사용할 것을 권장하는데 이는 JPA가 reflection을 사용해 객체를 생성하기 때문이다. 리플렉션으로 객체를 생성할 때 생성자가 private면 객체를 생성할 수 없다.

 

@AllArgsConstructor

 

모든 필드를 매개변수로 받는 생성자를 생성

@AllArgsConstructor
public class User {
    private String name;
    private String email;
}

// 생성되는 코드
public User(String name, String email) {
    this.name = name;
    this.email = email;
}

 

@RequiredArgsConstructor

 

final 이나 @NonNull 필드만을 매개변수로 받는 생성자를 생성

@RequiredArgsConstructor
public class User {
    private final String name;     // final 필드
    @NonNull
    private String email;          // @NonNull 필드
    private String role;           // 일반 필드 (생성자에 포함되지 않음)
}

// 생성되는 코드
public User(String name, @NonNull String email) {
    this.name = name;
    this.email = Objects.requireNonNull(email, "email is marked non-null but is null");
}

 

 


 

2. @Builder 어노테이션

클래스 레벨에서 사용

@Builder
public class User {
    private String name;
    private String email;
}

// 사용법
User user = User.builder()
    .name("John")
    .email("john@example.com")
    .build();

 

특징:

  • 모든 필드를 대상으로 빌더 생성
  • 기본 생성자는 private으로 생성
  • 모든 필드를 포함하는 생성자도 private로 생성

 

생성자 레벨에서 사용

 

특정 필드만 선택적으로 빌더에 포함하고 싶을 때 사용

public class User {
    private String name;
    private String email;
    private String role;

    @Builder
    public User(String name, String email) {  // role은 제외
        this.name = name;
        this.email = email;
        this.role = "USER";  // 기본값 설정
    }
}

 

 


 

3. 어노테이션 조합 패턴

@Builder만 사용하는 경우

@Getter
@Builder
public class AccountInformationResponse {
    private String field;
}

 

장점:

  • 불변성을 보장
  • 외부에서 직접 생성자 호출을 막을 수 있음

단점:

  • JPA 엔티티나 JSON 직렬화 시 문제 발생 가능

 

@Builder + @NoArgsConstructor + @AllArgsConstructor

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String field;
}

 

필요한 경우:

  • JPA 엔티티 클래스
  • JSON 직렬화/역직렬화 (Jackson 라이브러리)
  • Spring Framework의 일부 기능들
  • 프레임워크나 라이브러리가 리플렉션을 사용하여 객체를 생성할 때

 

권장 패턴 (접근 제어자 활용)

@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class AccountEntity {
    private String field;
}

 

이점:

  • Protected 생성자로 JPA 요구사항 충족
  • Private 생성자로 직접 생성은 제한
  • Builder 패턴만 허용

 


 

4. 상속과 @SuperBuilder

 

상속 관계에서 빌더 패턴을 사용할 때는 @SuperBuilder를 사용합니다.

@SuperBuilder
public class Parent {
    private String parentField;
}

@SuperBuilder
public class Child extends Parent {
    private String childField;
}

// 사용법
Child child = Child.builder()
    .parentField("parent")  // 부모 클래스 필드
    .childField("child")    // 자식 클래스 필드
    .build();

[!info] @SuperBuilder 를 양쪽에 선언하는 이유
@SuperBuilder는 복잡한 제네릭 구조를 사용하여 상속 체인 전체에서 빌더 패턴을 구현한다.
각 클래스가 자신만의 빌더 클래스를 생성하고, 이들이 서로 연결되어야 하기 때문이다.
내부적으로 생성되는 코드 구조는 다음과 같다.

 // Parent에 @SuperBuilder 적용 시 생성되는 구조
 public abstract class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
     private String parentField;

     public B parentField(String parentField) {
         this.parentField = parentField;
         return self();
     }

     protected abstract B self();
     protected abstract C build();
 }

 // Child에 @SuperBuilder 적용 시 생성되는 구조  
 public class ChildBuilder extends ParentBuilder<Child, ChildBuilder> {
     private String childField;

     public ChildBuilder childField(String childField) {
         this.childField = childField;
         return this;
     }

     @Override
     protected ChildBuilder self() {
         return this;
     }

     @Override
     protected Child build() {
         return new Child(this);
     }
 }

 

 


 

5. @Data와의 관계

 

@Data는 다음 어노테이션들을 포함합니다:

  • @Getter : 모든 필드에 대한 getter 메서드 생성
  • @Setter : 모든 필드에 대한 setter 메서드 생성
  • @ToString : 모든 필드를 포함하는 toString() 메서드 생성
  • @EqualsAndHashCode : equals()와 hashCode() 메서드 생성
  • @RequiredArgsConstructor : 필수 인자를 포함하는 생성자 생성
    • final 필드나 @NonNull로 마크된 필드들을 파라미터로 받는 생성자 생성
    • 만약 해당하는 필드가 없다면 no-args 생성자로 생성
@Data
public class User {
    private final String name;  // final 필드
    private String email;       // 일반 필드
}

// @RequiredArgsConstructor에 의해 final 필드만 포함하는 생성자 생성

 

 


 

6. 주의사항

  1. @Builder만 사용할 경우 리플렉션 기반 라이브러리 사용 시 문제 발생 가능
  2. @NoArgsConstructor 사용 시 필드의 초기화 값 주의 필요
  3. 상속 관계에서는 @Builder 대신 @SuperBuilder 사용 필요
  4. JPA 엔티티에서는 protected 기본 생성자가 필요함