[SPRING] DTO는 final로 설정하는 것이 맞을까?

wannabeing·2025년 4월 2일
1

SPRING

목록 보기
3/12
post-thumbnail

설정하면 아래와 같은 장점이 있죠~

1. DTO는 신뢰해야 될 객체이다.

  • DTO는 기본적으로 데이터를 다루기 위한 객체이다.
  • 의도하지 않은 데이터를 보유하는 것을 방지해야 하고,
    신뢰할 수 있도록 우리가 만들어줘야 한다.

2. DTO의 불변성을 보장한다.

  • 한번 값이 설정되면 바뀌지 않는다.
  • 값이 바뀌지 않으므로, 멀티스레드 환경에서 안전하게 다룰 수 있게 된다.

하지만

@RequestBody로 DTO를 바인딩 시 문제가 될 수 있다.

  • Spring에서 @RequestBody를 쓰면,
    내부적으로 Jackson 라이브러리가 JSON → DTO(객체) 변환을 한다.
  • 이때 Jackson의 기본 전략은
    👉 기본 생성자 호출 → Setter로 필드 값 주입
  • final로 설정할 경우, Setter가 없고 기본생성자로 초기화를 한다.
  • 👉 근데, 기본생성자가 없으므로 컴파일 에러 발생

왜 기본생성자 + Setter 인가요?
기본 생성자로 객체를 만들고, 생성자에서 값을 못받았기 때문에
데이터를 바인딩하려면 반드시 Setter가 필요하다!


❗️어??? 그러면은

  • 기본 생성자가 필요니까 → final을 쓰면 안되겠네???
  • Setter도 써야겠네??

final 쓰고 기본생성자/Setter 안써도 잘돌아가는데? 😇

@Getter
public class ScheduleRequestDto {

    @NotBlank(message = "제목을 입력해주세요.")
    @Size(max = 50, message = "제목은 50자 이내로 입력해주세요.")
    private final String title;

    @NotBlank(message = "내용을 입력해주세요.")
    @Size(max = 200, message = "내용은 200자 이내로 입력해주세요.")
    private final String contents;

    // ✅ 생성자 = @RequiredArgsConstructor
    public ScheduleRequestDto(String title, String contents) {
        this.title = title;
        this.contents = contents;
    }
}

그건 Jackson이 알아서 유일한 생성자를 자동으로 인식해서 바인딩을 도와주는 것임ㅋ

  • jackson-module-parameter-names
    자동 바인딩을 도와준다.
  • Spring Boot 2.x 이상부터 해당 모듈이 포함되어 있다.

❗️인텔리제이에서 빌드툴을 Gradle이 아닌 IntelliJ로 설정할 경우, 기본생성자가 없다고 오류가 날 수 있음!


기본생성자가 필요할 경우도 있다!

DTO에서도 필요한 경우가 있고,
엔티티 클래스의 경우 필수로 필요하다.

// 기본생성자 예시
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public final class RequestDto {

    private String searchText;

    private String searchType;
}

💡 필요하다면 접근을 제한하자! (with Lombok)

기본 생성자의 접근 제한자를 protected로 선언하자
public 또는 protected를 많이 쓰는 듯?

❓ @NoArgConstructor 어노테이션

  • 매개변수가 없는 기본 생성자를 생성하는 어노테이션
  • 기본 생성자로 무분별하게 값을 주입하게 되면, DTO를 신뢰할 수 없으므로
    ❗️기본 생성자가 필요할 경우, access를 반드시 두자.

생성자가 여러개면요?

Jackson 라이브러리에게 역직렬화에 사용할 생성자가 누구인지 알려줘야 한다.
이 때 @JsonCreator를 사용한다.

@Getter
public final class RequestDto {

    private final String searchText;
    private final String searchType;

	// ✅ 역직렬화를 수행하는 생성자 지정
    @JsonCreator
    public RequestDto(String searchText,
                      String searchType) {
        this.searchText = searchText;
        this.searchType = searchType;
    }

    public RequestDto(String searchText) {
        this.searchText = searchText;
        this.searchType = null;
    }
}
❗️JDK8버전 이하라면 각 매개변수에 @JsonProperty를 붙여야 한다..

❓ 직렬화/역직렬화..?

  • 직렬화: Java 객체(DTO) → JSON: (서버가 클라이언트에게 응답 )
  • 역직렬화: JSON → Java 객체(DTO): (클라이언트가 서버에게 요청)

💡 record 사용도 방법이 될 수 있다.

  • 자바 14버전 이후의 프로젝트의 경우, record를 사용할 수도 있지만
    나의 경우, 최대한 낮은 버전을 사용하고 있다고 생각하고 사용하지 않을 것이다!

✅ 따라서

  • QueryString, FormData를 받기위한 DTO는 final로 설정하는 것이 국룰이다.
    → 불변성 보장, 신뢰할 수 있는 객체 설계

  • 기본 생성자의 경우, 최대한 접근제어자를 통해 관리하자.
    → 무분별한 객체 생성 방지

  • 생성자가 두개 이상일 경우
    @JsonCreator로 어떤 생성자를 사용할 것인지 지정해주어야 한다.


출처

profile
wannabe---ing

0개의 댓글