Post

[Lombok] 보일러플레이트 방지

1. [생성자 주입] @RequiredArgsConstructor + final

스프링에서 의존성 주입(DI)을 할 때 @Autowired 대신 생성자 주입을 쓰는 것이 표준이며 Lombok을 쓰면 생성자를 직접 칠 필요가 없다

  • 핵심: 주입받을 필드는 반드시 private final로 선언
  • 이유: final이 붙지 않은 필드는 Lombok이 생성자에 포함시키지 않아 주입이 누락됨
    1
    2
    3
    4
    5
    6
    
    @Service
    @RequiredArgsConstructor // final이 붙은 필드만 모아서 생성자 자동 생성
    public class OrderService {
      private final OrderRepository orderRepository; // 필수!
      private final DiscountPolicy discountPolicy;   // 필수!
    }
    

2. [JPA 엔티티] @NoArgsConstructor(access = AccessLevel.PROTECTED)

JPA 엔티티를 만들 때 기본 생성자는 필수입니다. 하지만 외부에서 마음대로 객체를 생성하게 두면 객체의 일관성이 깨집니다.

  • 설정: access = AccessLevel.PROTECTED를 반드시 추가하세요.
  • 이유: JPA가 프록시 객체를 만들 수 있는 최소한의 권한(Protected)만 열어두고, 외부에서의 무분별한 new 생성을 막아 안전한 설계를 유지합니다.
    1
    2
    3
    4
    5
    6
    
    @Entity
    @NoArgsConstructor(access = AccessLevel.PROTECTED) // JPA를 위한 최소한의 예의
    public class Member {
      @Id @GeneratedValue
      private Long id;
    }
    

3. [장애 방지] @ToString(exclude = “…”)

양방향 연관관계(OneToMany ↔ ManyToOne)에서 가장 많이 터지는 장애가 순환 참조입니다.

  • 현상: Team이 Member를 부르고, Member가 다시 Team을 부르면서 무한 루프에 빠져 StackOverflowError가 발생합니다.
  • 해결: 연관관계가 있는 필드는 반드시 제외하세요. (보통 ‘다(N)’ 쪽을 제외하거나 양쪽 다 필드 지정을 권장)
    1
    2
    3
    4
    5
    
    @ToString(exclude = "members") // members 필드는 문자열 출력에서 제외 (무한루프 방지)
    public class Team {
      @OneToMany(mappedBy = "team")
      private List<Member> members = new ArrayList<>();
    }
    

4. [객체 생성] @Builder를 쓸 땐 생성자 세트를 갖추자

클래스 레벨에 @Builder만 붙이면 기본 생성자가 사라져서 나중에 JPA나 다른 라이브러리에서 에러가 날 수 있습니다.

  • 권장 세트: @Builder, @NoArgsConstructor, @AllArgsConstructor를 함께 사용하세요.
  • 이유: 빌더 패턴의 편리함과 JPA의 기본 생성자 요구 사항을 모두 충족하는 가장 안전한 조합입니다.
    1
    2
    3
    4
    5
    6
    7
    
    @Builder
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    @AllArgsConstructor // Builder는 모든 필드 생성자를 필요로 함
    public class UserDto {
      private String name;
      private int age;
    }
    

⚠️ @Data 어노테이션 지양

@Data는 편리해 보이지만 독이 될 수 있습니다.

  1. 불변성 파괴: 원치 않는 Setter가 모두 생성되어 객체 값이 어디서든 변할 수 있게 된다
  2. 예측 불가능: EqualsAndHashCode가 모든 필드를 다 뒤지므로, 연관관계가 복잡한 엔티티에서는 성능 저하나 쿼리 폭탄의 원인이 된다

This post is licensed under CC BY 4.0 by the author.