DTO 생성과 Builder 패턴

박의진·2025년 8월 22일

DTO 생성에 대한 의문이 있어서 찾아봄

비즈니스 로직에서 가변 DTO와 불변 DTO에 대한 역할 분리의 필요성을 느꼈다.

로직내에서 유동적으로 변해야 하는것은 한번 생성한 DTO에서 필드 값만 변경하는게 좋을지 새 DTO를 생성해서 각자의 역할 분담을 해주는게 좋을지 그게 궁금했었다.

나는 가변 DTO를 쓸 때는 Builder 어노테이션을 사용해서 객체 생성하는 것을 선호하는 편인데 빌더로 설정한 dto 클래스는 마이바티스에서 응답값 전달을 받을때 제대로 실행이 안되고 오류가 난다는 문제가 있다.

왜냐 ?

이유는 마이바티스 실행 로직상 응답값 dto 를 매칭할 때 dto를 기본 생성자와 setter로 객체 생성을 하기 때문이다.

dto클래스를 빌더로 설정해 버리면 기본 생성자로 객체를 생성할 수 없어서 충돌이 나는 것임...

❗ 왜 @Builder가 안 되나?
MyBatis는 다음과 같은 방식으로 DTO를 채웁니다:

UserDTO dto = new UserDTO();
dto.setName(rs.getString("name"));
dto.setAge(rs.getInt("age"));

즉, 기본 생성자를 통해 객체를 만들고, Setter로 값 주입한다.
그런데 @Builder를 쓰면 다음과 같은 생성자가 생기고

public UserDTO(String name, int age, boolean active) {
    this.name = name;
    this.age = age;
    this.active = active;
}

➡️ 기본 생성자가 사라지거나, MyBatis가 무시하게 됨
결과적으로 빌더 생성자는 MyBatis 입장에선 쓸 수 없는 생성자가 된다.

암튼 지피티 햄이 이렇게 정리해쥼

그래서 실무에서는 마이바티스용과 내부 비즈니스로직에서 사용할 dto분리해서 구현하는걸 선호한다고 한다.

  1. MyBatis 매핑 전용 DTO 따로 분리
    ➡️ 실무에서는 이걸 가장 선호
// MyBatis Result 전용 DTO
@Data
public class UserResultDTO {
    private String name;
    private int age;
    private boolean active;
}

// 내부 로직에 사용할 빌더 DTO
@Data
@Builder
public class UserDTO {
    private String name;
    private int age;
    private boolean active;
}

그리고 핵심적으로 이 내용을 정리하게된 이유는 !!!
가변 DTO를 생성하게 될때 두개의 dto를 생성해야하는데 그 두개가 컬럼 하나로만 나뉜다면 ?
이때 하나의 dto를 돌려쓸지 담당 기능을 분리할지가 고민이었는데 분리해서 역할을 명확하게 나누는게 좋다고 한다.

=> 즉 setter로 값만 바꾸는게 아니라 그냥 새 dto 생성하셈ㅇㅇ 어차피 dto는 가볍기때문에 무시가능한 수준임.

❗동일 dto를 가변적으로 사용할 때 단점
불변성이 깨짐 (값 변경을 의도하지 않았더라도 변경 가능)
멀티스레드 상황에서 위험 (공유 DTO라면 더 위험)
의도가 불명확해짐 ("왜 갑자기 status가 바뀌었지?" 혼란 유발 가능)

근데 이제 이거를 빌더패턴으로 또 생성해주기는 번거로우니 toBuilder를 사용해서 새로운 dto를 만들어내는 것이다

UserSearchDTO inactiveDto = dto.toBuilder()
    .status("INACTIVE")
    .build();

장점
불변성 유지
명확한 의도 표현: 두 개의 다른 조회 조건
나중에 디버깅하거나 유지보수할 때 혼란 없음

단점
객체가 한 개 더 생김 (하지만 DTO는 가볍기 때문에 무시 가능한 수준)

그리고 toBuilder를 쓰기 위해서는


@Builder(toBuilder = true)
public class UserSearchDTO {
    // ...
}

요렇코롬 toBuilder = true 를 설정해줘야 사용 가능하다는 점 !

✅ 결론: 실무에서는 Builder로 새 DTO를 생성하는 방식 권장
✔ 의도를 명확하게 표현하고,
✔ 불변성을 유지하면서,
✔ 잠재적인 Side Effect를 방지하기 위해
toBuilder()로 새 DTO를 만드는 방식이 안전하고 유지보수에 좋습니다.

profile
주니어 백엔드 개발자의 개발 log💻

0개의 댓글