About DTO (Data Transfer Object)

bp.chys·2020년 6월 5일
3

Spring Framework

목록 보기
13/15

1. 요청과 응답으로 엔티티보다 DTO를 사용하는 것이 좋다.

@GetMapping("/api/questions/{id}")
public ResponseEntity<Question> read(@Pathvariable("id") Long id) {
    Question question = quesetionService.read(id);
    return ResponseEntity.ok(question);
}
  • 위와 같이 엔티티를 바로 응답 객체로 return하게 되면 UI계층과 도메인 계층에 의존성이 생기게 된다.
    • MVC 패턴을 사용하듯 우리는 UI와 도메인 영역의 독립적인 개발을 지향하기 때문에, 이러한 구조는 바람직하지 않다.
  • 그렇기 때문에 엔티티의 속성만 담아서 전달하는 객체인 DTO(Data Transfer Obejct)를 사용하는 것이 좋다.

요청과 응답으로 DTO를 사용해야하는 이유

  1. 도메인 객체를 캡슐화할 수 있다.
    • UI 계층에서 엔티티의 메서드를 호출하거나, 상태를 변경시킬 위험을 줄일 수 있다.
  2. 화면에 필요한 데이터만 선택적으로 보낼 수 있다.
    • 화면마다 엔티티의 모든 속성이 필요하지 않을 수 있다.
  3. 순환 참조를 예방할 수 있다.
    • 엔티티를 사용한다면 UI 계층에서 toString()과 같은 메서드가 호출될 때 순환참조가 발생할 수 있다.
  4. validation 코드와 모델링 코드를 분리할 수 있다.
    • DTO를 따로 분리하지 않으면, 엔티티가 무분별한 validation 코드로 인해 복잡해진다.

2. Map 자료구조 대신 DTO를 사용하는 것이 좋다.

  • 일부 개발자들은 모든 API마다 요청과 응답에 해당하는 DTO를 만드는 것이 귀찮고 비용이라고 생각하기도 한다. 틀린말은 아니다.
  • 하지만 Map을 쓰는 것은 DTO의 많은 장점을 포기하게 된다.

Map 대신 DTO를 사용하는 것이 좋은 이유

  1. Map안에 어떤 속성이 들어있는지 알 수 없다.
    • Map으로 데이터를 전달 받았지만, 어떤 키로 구성되어있는지 확인하는 작업이 필요하다.
    • 또한 불필요한 데이터가 추가로 들어갈 가능성이 있기 때문에 충분히 비효율이 발생할 수 있다.
  2. Map에서 값을 꺼낼 때 필요한 type casting은 에러 발생 가능성이 있다.
    • Map의 키 타입은 Object를 사용하게 되고, 값을 꺼내서 사용할 때는 type 캐스팅을 해야한다.
    • 역시 정확한 타입을 모르는 경우가 발생할 수 있고, 지원하지 않는 타입일 경우 에러가 발생하기 쉽다.
  3. Validation 코드를 작성하지 못한다.
    • Map안에 키만 존재하고 값이 null인 경우, 이를 사전에 잡지 못하고 나중에 발견하게 된다.
    • 또는 validation 코드를 작성하기 위해 엔티티 코드가 복잡해진다.

3. DTO와 엔티티 사이의 변환은 DTO에서 하는 것이 좋다.

  • DTO는 도메인 객체인 엔티티와 서로 변환이 되도록 잘 매핑되어야 한다.
  • 가장 직관적인 방법은 DTO 혹은 엔티티의 속성을 getter로 모두 추출한다음 상대 객체를 만드는 것일 것이지만, 이는 가독성이 좋지 않으므로 지양하도록 한다.

의존의 방향은 변경이 빈번한 곳 변경이 적은 곳

  • A 객체가 B를 사용하고 있을 때, A는 B에 의존하고 있다고 표현하고 의존 방향은 A → B가 된다.
  • 이는 다른 말로하면 B가 변경될 때마다 A도 함께 변한다는 것을 의미한다.
  • 유지보수 관점에서 당연히 변경이 상대적으로 더 적은 쪽을 의존하는 것이 유리하다.
  • DTO는 화면 요구사항에 맞는 객체이고 엔티티는 영속 객체이다.
  • UI 요구사항의 변경이 더 빈번하므로 우리는 DTO에서 엔티티를 의존하는 것이 바람직하다는 것을 알 수 있다.
@NoArgsConstructor
@Data
public class UserCreateRequest {
    
    @NotBlank
    private String username;
    
    @NotBlank
    private String password;
    
    private String email;
    
    public User toEntity() {
        return User.builder()
            .username(username)
            .password(password)
            .email(email)
            .build();
    }
}

결론

DTO를 처음 접할 때 궁금했던 부분 중 크게 3가지 포인트를 정리해보았다.

  1. 요청과 응답으로 엔티티 대신 DTO를 사용하는 것이 좋다.
  2. Map 자료구조 보다는 DTO를 사용하는 것이 좋다.
  3. DTO와 엔티티 사이의 변환은 변경이 더 빈번한 DTO에서 하는 것이 좋다.

이 외에도 DTO를 사용하는 것이 더 좋은 이유나 다른 생각해볼만한 이슈가 있을 것이다.
아직은 개발 경험이 많이 부족하지만 앞으로 개발을 더 해보면서 DTO의 장단점을 깊이있게 경험해봐야겠다.

profile
하루에 한걸음씩, 꾸준히

0개의 댓글