![[JPA] 엔티티 클래스에서 @Builder 위치](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDW2Rs%2FbtsM7gHZ67o%2FDIfjTjnmMtzbGUJL7rUeK0%2Fimg.png)
[JPA] 엔티티 클래스에서 @Builder 위치Back-End/JPA2025. 4. 3. 17:09
Table of Contents
@Builder를 생성자 위에 두는 방식
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Product extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String productNumber;
private String name;
private int price;
@Builder // 생성자 위에 빌더
public Product(String productNumber, String name, int price) {
this.productNumber = productNumber;
this.name = name;
this.price = price;
}
}
생성자 위에 @Builder 애노테이션을 사용하면, 생성자를 private로 막고, 객체의 생성은 정적 팩토리 메서드를 사용하는 경우가 많다.
@Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity public class Product extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String productNumber; private String name; private int price; @Builder // 생성자 위에 빌더 private Product(String productNumber, String name, int price) { this.productNumber = productNumber; this.name = name; this.price = price; } // 정적 팩토리 메서드 public static Product create(String productNumber, String name, int price) { return Product.builder() .productNumber(productNumber) .name(name) .price(price) .build(); } }
특징
- 특정 생성자에만 @Builder가 적용되며, 명확한 생성 목적을 가진 빌더가 된다.
- 주로 필수 필드만 지정하고 싶은 경우, 또는 JPA와 함께 사용할 때 자주 사용된다.
- 보통은 생성자를 private으로 막고, 정적 팩토리 메서드에서만 빌더를 사용하게 한다.
장점
- JPA에 매우 적합하다 (id 제외 가능).
- 어떤 값으로 객체를 생성할 수 있는지 명확하다.
- 엔티티의 생성 책임이 분리되어 깔끔하다.
- 정적 팩토리 메서드와 조합하면 유효성 검사 및 생명주기 통제도 가능하다.
단점
- 나중에 수정 목적 등으로 빌더를 재사용하려면 번거로움
- 값 수정용 toBuilder() 기능은 사용할 수 없음
@Builder를 클래스 레벨에 적용하는 방법
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Builder // 클래스 레벨에 빌더
public class Product extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String productNumber;
private String name;
private int price;
}
특징
- 전체 클래스 필드를 대상으로 한 빌더가 생성됨
- 모든 필드(id 포함)에 접근 가능
- toBuilder = true 옵션으로 이미 생성된 객체로부터 수정 가능한 빌더 생성 가능
장점
- 기존 객체를 일부만 바꿔서 새 객체를 만들기 쉬움
- 유연성 매우 높음 (특히 DTO 변환, 수정 API 등에 유리)
Product updatedProduct = originalProduct.toBuilder()
.price(4000)
.name("수정된 이름")
.build();
단점
- 실수로 id, createdDateTime 등의 JPA 관리 필드까지 수정 가능 → 실무에서 엔티티 무결성에 문제 생길 수 있음
- 어떤 필드를 생성/수정에 써야 할지 애매해져서 설계가 흐려짐
결론
엔티티의 생성 책임과 수정 책임은 분리되어야 한다.
@Builder를 생성자 위에 두는 방식을 써야한다.
- @Builder는 생성자 위에 붙이는 방식을 사용하는 것이 가장 안전하고 명확하다.
- 그렇다면 객체 수정이 필요할 때는 어떻게 해야 하나? -> 변경이 필요한 필드에 대해서만 값 변경이 가능한 명시적인 도메인 메서드를 만들어야 한다.
public void changeProductInfo(String name, int price) {
this.name = name;
this.price = price;
}
엔티티에 명시적으로 상태를 바꾸는 메서드를 정의하는 게 DDD(도메인 주도 설계) 관점에서도 가장 이상적이다.
그렇다고 해서 필요한 필드만 setter를 만들라는 것이 아니다.
'Back-End > JPA' 카테고리의 다른 글
[JPA] @Builder.Default (0) | 2025.04.04 |
---|---|
[JPA] 엔티티 공통 필드 상속(@MappedSuperclass) (0) | 2025.04.03 |
[JPA]PostgreSQL 사용시, 엔티티에 Enum 매핑 오류 (0) | 2024.11.21 |
[Spring Data JPA] Projections 과 Native Query (0) | 2024.08.16 |
[Spring Data JPA] 새로운 엔티티인지 구별하는 방법 (0) | 2024.08.15 |