[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는 편리해 보이지만 독이 될 수 있습니다.
- 불변성 파괴: 원치 않는 Setter가 모두 생성되어 객체 값이 어디서든 변할 수 있게 된다
- 예측 불가능: EqualsAndHashCode가 모든 필드를 다 뒤지므로, 연관관계가 복잡한 엔티티에서는 성능 저하나 쿼리 폭탄의 원인이 된다
This post is licensed under CC BY 4.0 by the author.