DTO의 사용 이유와 범위

전홍영·2023년 1월 1일
0

Spring

목록 보기
4/25

개발을 하다가 굳이 domain과 DTO를 왜 같이 써야할까?라는 의문점이 생겼다. 내가 배운 DTO는 client와 controller, controller와 service 사이의 객체 전달을 위해 사용하는 것이라 생각했는데 굳이 객체 전달을 위해서라면 그냥 domain을 가져다가 써도 된다고 생각했다.

DTO란?

DTO(Data Transfer Object)란 계층간 데이터 교환을 위해 사용하는 객체이다. DTO는 클라이언트 요청에 포함된 데이터를 담아 서버 측에 전달하고, 서버 측의 응답 데이터를 담아 클라이언트에 전달하는 계층간 전달자 역할을 한다.

도메인 객체를 View에 직접 전달할 수 있지만, 민감한 도메인 비즈니스 기능이 노출될 수 있으며 Model과 View 사이에 의존성이 생기게 된다.

controller가 domain을 그냥 넘겨주면 어떤 문제가 있을까?

public class User {

    public Long id;
    public String name;
    public String email;
    public String password; //외부에 노출되서는 안 될 정보
    public DetailInformation detailInformation; //외부에 노출되서는 안 될 정보

    //비즈니스 로직, getter, setter 등 생략
}

@GetMapping
public ResponseEntity<User> showArticle(@PathVariable long id) {
    User user = userService.findById(id);
    return ResponseEntity.ok().body(user);
}
  • 위의 예시를 보면 Controller가 클라이언트의 요청에 대한 응답으로 도메인 Model인 User를 넘겨주면 도메인 Model의 모든 속성이 외부에 노출된다.
    • 실제로 client에게 보여지는 부분 외의 불필요한 데이터까지 보우하고 있다.
    • 민감한 정보가 외부에 노출될 위험성이 있기 때문에 보안상의 문제가 생긴다.
  • UI계층에서 Model의 메서드를 호출하거나 상태를 변경시킬 위험이 존재한다.
  • Model과 View가 강하게 결합되어, View의 요구사항 변화가 Model에 영향을 끼치기 쉽다.
    • 또한 Entitiy의 속성이 변경되면, view가 전달받을 Json 및 프론트엔드 js코드에도 변경을 유발하기 때문에 상호간 강하게 결합된다.

DTO를 사용한다면 위의 문제를 해결할 수 있다.

public class UserDto {

    public final long id;
    public final String name;
    public final String email;

    //생성자 생략

    public static UserDto from(User user) {
        return new UserDto(user.getId(), user.getName(), user.getEmail());
    }
}

@GetMapping
public ResponseEntity<UserDto> showArticle(@PathVariable long id) {
    User user = userService.findById(id);
    return ResponseEntity.ok().body(UserDto.from(user));
}

DTO를 사용하게 된다면 Model을 캡슐화 하고, 사용하는 데이터만 선택적으로 보낼 수 있게된다. 따라서 DTO는 클라이언트 요청에 포함된 데이터를 담아 서버 측에 전달을 하고, 서버 측은 응답 데이터를 DTO에 담아 클라이언트에게 전달하는 전달자 역할을 한다.

DTO의 사용 범위

Controller와 Clinet 사이에서 DTO를 사용하는 것이 좋은 것인가 Service와 Controller 사이에서 DTO를 사용하는 것이 좋은 사용방법인가? 또한 Repository와 Controller 사이에서는 DTO를 사용하지 않는 것이 옳은 방법인가? 여러 글을 읽고 지금까지의 내가 생각하는 좋은 경우는 Service 단에서 DTO로 변환하여 사용하는 것이 좋다고 생각했다.

Contoller가 Entity를 DTO로 변환하는 경우

Controller에서 DTO를 domain으로 변환하고 이를 Service에 전달한다. Service는 로직을 처리하고 domain을 contoller에 반환하여 controller는 이 domain을 DTO로 다시 변환하여 client에 전달한다. 이 과정에서 생기는 문제점은 무엇일까?

  • View에 반환할 필요가 없는 데이터까지 Domain 객체에 포함되어 Controller까지 넘어온다.
  • Controller가 여러 Domain 객체들의 정보를 조합해서 DTO를 생성하는 경우, 결국 Service 로직이 Controller에 포함된다.
  • 여러 Domain 객체들을 조회해야 하기 때문에 하나의 Controller가 의존하는 Service의 개수가 배대해집니다.

Service가 DTO를 사용하는 경우

Controller가 View로부터 받은 DTO를 Entity로 변환한 뒤, Service 레이어가 Entity를 전달받아 일련의 비즈니스 로직을 수행한다고 가정한다면 Controller가 View에서만 전달받은 DTO만으로 Entity를 구성하기란 어렵다. 따라서 Repository를 통해 여러 부수적인 정보들을 조회하여 Domain 객체를 구성할 수 있는 경우도 존재한다.

Repository에서 DTO를 사용하는 경우

Repository 레이어는 entity의 영속성을 관장하는 역할이라고 한다. 이로 인해, 표현 계층에서 사용할 도메인 계층의 Aggregates를 DTO로 변환하는 작업을 Repository 단에서 책임지게 하는 것을 지양하자는 의견이 다수 존재한다.

따라서

Service 단에서 DTO-Entity 변환을 사용하는 것이 타당하다는 글이 많았다. 그러나 답은 정해진 것이 없고 프로젝트의 크기, 역할 등을 고려하여 사용하는 것이 가장 좋다고 생각된다.

-reference-

profile
Don't watch the clock; do what it does. Keep going.

0개의 댓글