문제 상황
응답 DTO를 작성하던 중,
builder와 생성자를 혼용해서 사용하고 있다는 점을 인지하게 되었다.
@Getter
@Builder
public class MenuResponse {
private Long menuId;
private String name;
private BigDecimal price;
public static MenuResponse from(Menu menu) {
return MenuResponse.builder()
.menuId(menu.getId())
.name(menu.getName())
.price(menu.getPrice())
.build();
}
}
@Getter
public class PopularMenuResponse {
private Long menuId;
private String name;
private Long orderCount;
public PopularMenuResponse(Long menuId, String name, Long orderCount) {
this.menuId = menuId;
this.name = name;
this.orderCount = orderCount;
}
}
두 DTO 모두 단순히 데이터를 담는 객체임에도 불구하고
생성 방식이 서로 달라,
어떤 기준으로 builder와 생성자를 선택해야 하는지 고민이 생겼다.
특히,
- builder로 통일하는 것이 맞는지
- 아니면 생성자를 사용하는 것이 더 적절한지
명확한 기준이 없다는 점이 문제였다.
고민 과정
처음에는 단순히 코드 스타일의 문제라고 생각했다.
"어차피 DTO인데 하나로 통일하면 더 깔끔하지 않을까?"
라는 생각으로 builder로 통일하는 것도 고려했다.
하지만 조회 로직을 구현하면서
집계 결과를 DTO로 바로 매핑하는 과정에서 의문이 생겼다.
"이 경우에도 builder를 쓰는 게 맞을까?"
이 질문을 기준으로 DTO의 역할을 다시 생각해보게 되었다.
원인 분석
두 DTO는 겉보기에는 비슷하지만
생성되는 위치와 방식이 완전히 다르다는 것을 확인할 수 있었다.
1. MenuResponse (일반 응답 DTO)
- 엔티티 → DTO 변환 과정에서 생성
- 서비스 레이어에서 직접 생성
이 경우 bulider를 사용하면
- 필드명이 명확하게 드러나고
- 순서에 의존하지 않아 안전하며
- 확장에도 유연하게 대응할 수 있다.
따라서 builder가 적합하다.
2. PopularMenuResponse (조회용 DTO)
- DB 조회 결과를 바로 DTO로 매핑
- JPQL / QueryDSL에서 생성
예를 들어 JPQL에서는 아래와 같이 사용된다.
select new com.example.PopularMenuResponse(m.id, m.name, count(o.id))
이 방식은 생성자를 통해서만 동작하며,
builder 패턴은 사용할 수 없다.
따라서 조회용 DTO는 생성자를 사용하는 것이 자연스럽다.
해결 방법
DTO를 하나의 방식으로 통일하는 것이 아니라,
역할에 따라 생성 방식을 구분하기로 했다.
기준 정리
- 일반 응답 DTO → builder 사용
- 조회 결과 DTO → 생성자 사용
이 기준을 적용하니
- 코드의 의도가 명확해지고
- DTO의 역할이 더 분명해졌으며
- 유지보수도 쉬워졌다
개선 결과
기존에는 DTO 생성 방식에 대한 기준이 없어
코드 작성 시마다 고민이 발생했지만,
명확한 기준을 정리한 이후에는
- DTO를 만들 때 고민이 줄어들고
- 상황에 맞는 생성 방식을 바로 선택할 수 있게 되었다.
또한 DTO를 단순한 데이터 객체가 아니라
설계 관점에서 바라보게 되는 계기가 되었다.
느낀 점
처음에는 사소한 차이라고 생각했던 부분이었지만,
정리해보니 DTO 역시 역할에 따라 설계가 필요한 요소라는 것을 느꼈다.
특히
"DTO는 그냥 데이터 담는 객체가 아니다"
라는 관점을 갖게 된 것이 가장 큰 수확이었다.
앞으로는 DTO를 작성할 때
- 무조건 builder를 쓰거나
- 무조건 생성자를 쓰기보다
해당 DTO가 어디에서, 어떻게 생성되는지를 먼저 고려하고
적절한 방식을 선택해야겠다고 생각했다.
'트러블슈팅' 카테고리의 다른 글
| DTO 생성 방식 혼용 문제를 기준 정립으로 해결하기 (1) | 2026.03.30 |
|---|---|
| 기능 중심 API 설계의 한계와 도메인 중심으로의 개선 과정 (0) | 2026.03.30 |
| Redis write-back 구조에서 벌크 업데이트 적용하기 (0) | 2026.03.20 |
| Redis write-back 구조에서 스케줄러와 벌크 처리 개념 혼동 (0) | 2026.03.20 |
| Redis write-back 구조에서 데이터 유실 문제 (0) | 2026.03.19 |