oguri's garage

Spring DTO 설계 시 기본 생성자만 두는 이유 본문

개발하다/Spring

Spring DTO 설계 시 기본 생성자만 두는 이유

oguri 2025. 10. 22. 20:44



Spring 기반 서버 개발을 하다 보면 Request DTO를 어떻게 설계해야 할지 고민하게 된다.
Lombok의 여러 어노테이션 중 무엇을 사용해야 하는지, record를 써도 되는지, 왜 @AllArgsConstructor는 피해야 하는지에 대한 경험을 정리한다.



불변 전달 객체는 클래스 또는 record로

단순히 값을 담아 전달하는 DTO는 두 가지 방식으로 설계할 수 있다.


방식 1: Lombok 기반 클래스

@Getter
@NoArgsConstructor
public class UserRequest {
    private String name;
    private int age;
}


방식 2: Java 17+ record

public record UserRequest(
    String name,
    int age
) {}


두 방식 모두 Spring의 @RequestBody, @ModelAttribute로 역직렬화할 때 문제없이 동작한다.
값 변경이 필요 없는 단순 전달 객체라면 어떤 방식을 선택해도 안전하다.



@AllArgsConstructor를 만들지 않는 이유

Spring의 JSON 역직렬화(Jackson)는 기본 생성자를 통해 객체를 생성한 뒤 필드에 값을 주입하는 방식으로 동작한다.
@AllArgsConstructor만 있으면 기본 생성자가 생성되지 않아 다음과 같은 오류가 발생한다.

Cannot construct instance of `UserRequest` 
(no Creators, like default constructor, exist)


Request DTO는 setter나 전체 생성자 호출이 거의 필요 없다.
이런 생성 경로는 주로 Service나 Entity 계층에서 Builder 패턴으로 사용된다.
불필요한 객체 생성 경로를 막고 역직렬화를 보장하기 위해 기본 생성자만 두는 것이 안전하다.



값 변경이 필요한 경우

DTO의 값을 수정해야 하는 상황이라면 setter 방식보다는 불변 패턴을 사용한다.

// 안티패턴: setter 사용
request.setName("newName");

// 권장: 새 객체 생성
UserRequest updated = UserRequest.builder()
    .name("newName")
    .age(request.getAge())
    .build();


record는 불변 객체이므로 값 변경 시 항상 새 객체를 생성해야 한다.
이것이 오히려 불변성을 보장하는 장점이 된다.



권장사항

단순 불변 전달 객체

  • 클래스 방식: @Getter, @NoArgsConstructor 조합
  • record 방식: Java 17+ 환경이라면 record 활용
  • 필드 기본값은 선언부에서 직접 할당


복잡한 객체나 값 변경 필요 시

  • @Builder 패턴 추가
  • 불변성 유지를 위해 새 객체 생성 방식 사용



핵심 정리

Request DTO는 불변 전달 객체로 설계하는 것이 안전하다.
클래스에 Lombok을 적용하든 record를 사용하든 Spring/Jackson과 문제없이 호환되며, 역직렬화 이슈도 발생하지 않는다.
@AllArgsConstructor는 역직렬화를 방해하므로 피하고, 기본 생성자만 제공하는 것이 실무에서 검증된 방식이다.

프로젝트 환경과 코드 스타일에 맞춰 자유롭게 선택하되, 불변성과 명확한 생성 경로를 유지하는 것이 중요하다.