Post

DTO란?

DTO란?

DTO 란 무엇인가?

DTO(Data Transfer Object)는 계층 간 데이터 교환을 위해 사용되는 객체를 말합니다. 주로 다음과 같은 상황에서 사용됩니다.

  1. 계층 간 데이터 전송: 프레젠테이션 계층(API 컨트롤러)과 도메인 계층 간에 데이터를 전달할 때 DTO를 사용합니다. 이를 통해 계층 간의 의존성을 낮출 수 있습니다.

  2. 보안: 민감한 데이터나 불필요한 데이터를 노출하지 않고 필요한 정보만 전송할 수 있습니다.

  3. 성능 최적화: 엔티티 객체를 직접 반환하는 것보다 DTO 사용 시 불필요한 연산을 줄일 수 있습니다.

DTO 구현 예시

  1. 프레젠테이션 계층에서 요청 데이터를 받을 때 DTO를 사용합니다. 예를 들어 UserCreateDto를 사용하여 새 사용자 정보를 받습니다.
    1
    2
    3
    4
    5
    6
    
     public class UserCreateDto {
         private String name;
         private String email;
    
         // getter and setter
     }
    
  2. 도메인 계층에서 DTO를 엔티티로 변환합니다. 예를 들어 UserCreateDto를 User 엔티티로 변환합니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
     @Entity
     public class User {
         private Long id;
         private String name;
         private String email;
    
         public User of(UserCreateDto dto) {
             this.name = dto.getName();
             this.email = dto.getEmail();
         }
    
         // getter
     }
    
  3. 도메인 로직 처리 후 엔티티 데이터를 DTO로 변환하여 응답합니다. 예를 들어 User 엔티티를 UserResponseDto로 변환하여 반환합니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
     public class UserResposneDto {
         private Long id;
         private String name;
         private String email;
    
         public UserResponseDto(User user) {
             this.id = user.getId();
             this.name = dto.getName();
             this.email = dto.getEmail();
         }
    
         // getter and setter
     }
    

DTO의 장점은 계층 간 의존성을 낮추고, 불필요한 데이터 노출을 방지하며, 성능을 개선할 수 있다는 점입니다. 그러나 DTO를 과도하게 사용하면 코드가 복잡해질 수 있으므로, 적절한 수준에서 사용하는 것이 중요합니다.

필드가 public 이면 안되는 걸까?

DTO의 필드를 public으로 설정하는 것은 바람직하지 않은 방식입니다. 주된 이유는 다음과 같습니다.

  1. 불변성(Immutability) 위반

    • DTO는 데이터 전송 객체로서 불변 객체로 취급되는 것이 일반적입니다.
    • 필드가 public이면 어디서든 값을 변경할 수 있어 불변성이 깨집니다.
    • 불변 객체는 스레드 안전성과 예측 가능성을 보장합니다.
  2. 캡슐화 원칙 위반

    • 객체지향 설계의 기본 원칙 중 하나는 데이터를 캡슐화하는 것입니다.
    • 필드가 public이면 외부에서 직접 접근할 수 있어 캡슐화가 깨집니다.
    • 데이터 유효성 검사 등의 규칙을 적용하기 어렵습니다.
  3. 대부분의 자바 스타일 가이드에서 권장하지 않음

    • 유명 자바 코딩 스타일 가이드(Google, Oracle 등)에서는 필드를 private으로 선언하고, getter/setter를 통해 접근할 것을 권장합니다.
  4. JSON 직렬화/역직렬화 문제

    • Jackson 등의 라이브러리는 필드가 public일 경우 getter/setter 없이도 직렬화/역직렬화가 가능합니다.
    • 하지만 이는 규칙에 어긋나며, 값 변경 시 문제가 발생할 수 있습니다.

따라서 DTO의 필드는 private으로 선언하고, 필요에 따라 getter를 제공하는 것이 일반적인 방식입니다. 생성자나 정적 팩토리 메서드를 통해 객체를 생성하고, 불변 객체로 취급하는 것이 좋습니다.

불변 객체로 취급되어야 하는 이유는?

DTO를 불변 객체로 취급해야 하는 주된 이유는 다음과 같습니다:

  1. 스레드 안전성(Thread Safety) 보장

    • 불변 객체는 생성 후 그 상태가 변하지 않으므로 여러 스레드에서 동시에 사용해도 안전합니다.
    • 따라서 동기화 처리에 대한 부담이 없어 성능 향상과 코드 간소화가 가능합니다.
  2. side effect 방지

    • DTO 객체의 상태가 변하면 참조하는 다른 객체들에게 영향을 미칠 수 있습니다. - 불변 객체는 이러한 부작용(side effect)을 원천적으로 차단할 수 있습니다.
  3. 추적 및 디버깅 용이성

    • 불변 객체는 상태 변화가 없으므로 데이터의 생성과 전달 과정을 추적하기 쉽습니다.
    • 이는 디버깅과 오류 원인 파악에 큰 도움이 됩니다.
  4. 객체 공유 및 캐싱 가능

    • 불변 객체는 여러 곳에서 안전하게 공유할 수 있습니다.
    • 따라서 메모리 사용량 절감 및 캐싱 기법 활용이 용이합니다.
  5. 부가적인 이점

    • 불변 객체는 String, Integer 등 자바의 기본 데이터 타입과 동일한 특성을 가지므로 직관적입니다.
    • 값에 의한 비교가 가능해 equals(), hashCode() 메서드 활용도가 높아집니다.

DTO는 일회성으로 사용되며, 계층 간 데이터 전송을 위해 생성되는 객체입니다. 따라서 상태 변경 없이 안전하게 사용하기 위해 불변 객체로 취급하는 것이 바람직합니다.

Record

DTO를 불변 객체로 구현하기 위해서는 Java 16부터 지원하는 Record 타입을 사용하는 것이 좋은 방법입니다. Record는 다음과 같은 장점이 있습니다.

  1. 간결한 구문

    • 필드, 생성자, getter, toString 등을 자동으로 생성해주므로 코드가 간결해집니다.
  2. 불변성 보장

    • Record는 기본적으로 final 키워드가 적용되어 불변 객체로 생성됩니다.
  3. Immutable Collection 생성 지원

    • List.of(), Set.of() 등의 메서드를 통해 불변 컬렉션을 쉽게 생성할 수 있습니다.
  4. 주석 지원

    • 필드에 Javadoc 주석을 작성하면 생성자와 getter에도 자동으로 주석이 추가됩니다.

예를 들어, 다음과 같이 간단하게 Record를 정의할 수 있습니다.

1
public record UserCreateDto(String name, String email) {}

그리고 생성자를 통해 불변 객체를 만들 수 있습니다.

1
2
3
4
5
public record UserResponseDto(Long id, String name, String email) {
    public UserResponseDto(User user) {
        this(user.getId(), user.getName(), user.getEmail());
    }
}

Record를 사용하면 기존의 POJO 클래스를 작성하는 것보다 코드가 간결해지며, 불변성도 자연스럽게 보장됩니다. 또한 컴파일러가 equals(), hashCode(), toString() 등의 메서드를 자동으로 생성해주므로 편리합니다. 따라서 Java 16 이상 버전에서는 DTO를 Record로 구현하는 것이 좋은 선택이 될 수 있습니다.

This post is licensed under CC BY 4.0 by the author.