// 나쁨: 어떤 값이 무엇인지, 순서를 외우기 어렵다
new Product("MacBook", "M3 Max", 3890000, 20, "노트북", null);
@Getter
@Builder
public class ProductDto {
private final String name;
private final String description;
private final int price;
private final int stockQuantity;
private final String category;
private final String imageUrl;
}
// 사용
ProductDto dto = ProductDto.builder()
.name("MacBook Pro 17")
.description("M3 Max 칩")
.price(3_890_000)
.stockQuantity(20)
.category("노트북")
.imageUrl("https://...")
.build();
@Builder 핵심 옵션@Builder 권장 (불변 DTO 지향)@NoArgsConstructor(protected) + 빌더 생성자에 @Builder@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "products")
public class Product extends BaseTimeEntity {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable=false) private String name;
@Column(nullable=false) private String description;
@Column(nullable=false) private int price;
@Column(nullable=false) private int stockQuantity;
@Column(nullable=false) private String category;
private String imageUrl;
@Builder // ← 생성자에 붙여 의도된 생성 흐름 강제
private Product(String name, String description, int price, int stockQuantity,
String category, String imageUrl) {
this.name = name;
this.description = description;
this.price = price;
this.stockQuantity = stockQuantity;
this.category = category;
this.imageUrl = imageUrl;
}
}
@Builder.Default : 빌더 미지정 시 기본값 유지
@Builder
public class ItemDto {
@Builder.Default
private String status = "ACTIVE"; // builder().build() 시에도 ACTIVE 유지
}
@Singular : 컬렉션 필드를 addXxx() 식으로 누적
@Singular
private List<String> tags; // .tag("a").tag("b").build()
toBuilder=true : 기존 객체를 복제 후 일부만 변경
ItemDto changed = original.toBuilder().status("PAUSED").build();
@Getter 필수 (JSON 직렬화용)@NotNull, @Positive 등 Bean Validation으로 선제from()이란@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PostProductResponse {
private Long id;
private String name;
private String description;
private int price;
private int stockQuantity;
private String category;
private String status;
private String imageUrl;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private String message;
private String warning;
public static PostProductResponse from(Product p, String message, String warning) {
return PostProductResponse.builder()
.id(p.getId())
.name(p.getName())
.description(p.getDescription())
.price(p.getPrice())
.stockQuantity(p.getStockQuantity())
.category(p.getCategory())
.status(p.getStockQuantity() > 0 ? "AVAILABLE" : "OUT_OF_STOCK")
.imageUrl(p.getImageUrl())
.createdAt(p.getCreatedAt())
.updatedAt(p.getUpdatedAt())
.message(message)
.warning(warning)
.build();
}
}
from()만 수정하면 전역 반영@Builder + @Getter@Builder 생성자만 허용(세터 지양)from()로 변환 로직 캡슐화@Valid + Bean Validation으로 최대한 앞단에서 걸러내기@Table(uniqueConstraints=...) 또는 컬럼 unique = truefrom(): 엔티티 -> DTO 변환 규칙의 단일 진실(SSOT) 을