개발지식

DTO(Data Transfer Object)의 사용범위와 특징

여행하는 개발자(SOO) 2023. 12. 31. 09:00
728x90

DTO란?

DTO란 Data Transfer Object의 약자로, 계층 간 데이터 전송을 위해 도메인 모델 대신 사용되는 객체이다. 여기서 계층이란 Presentation(View, Controller), Business(Service), Persistence(DAO, Repository)를 의미한다.

DTO의 특징

  • 데이터를 저장하는 용도로만 사용되어야 한다. 데이터에 대한 getter, setter 만을 가져야 한다.
  • 저장, 조회를 제외한 어떠한 비즈니스 로직도 있어서는 안 된다.
  • 하지만, 데이터 전송을 위해 직렬화, 역직렬화 메커니즘은 포함할 수 있다.

직렬화란 객체를 JSON, XML, 바이트스트림 등으로 변환하는 것을 의미한다. 역직렬화는 직렬화의 반대이다.

도메인 대신 DTO를 사용하는 이유

도메인 모델을 계층 간 전달에서 사용하면, 혹시나 도메인 모델의 메서드를 호출하여 상태를 변경시킬 위험이 있다. 또한, 글 쓰기와 글 수정같이 화면이 다른 곳에서 같은 도메인을 사용한다면 불필요한 속성을 가지고 있어야 하는 경우가 생긴다. 도메인 대신 DTO를 사용한다면 UI에 맞는 속성만 가질 수 있고 도메인 모델이 외부에 노출되어 발생할 수 있는 보안 문제를 방지할 수 있다. 즉, 도메인 모델을 캡슐화하여 보호할 수 있다.

또한, 도메인 모델을 계층 간 전송에 사용하면, 모델과 뷰가 강하게 결합될 수 있으며 뷰의 요구사항 변화로 도메인의 코드를 변경해야 할 일이 생길 수 있다. 이는 좋지 않은 일이며 이때 DTO를 사용하여 결합을 느슨하게 할 수 있다.

그렇다면? DTO의 사용 범위는?

결론부터 말하자면 영속 계층(Persistence Layer)까지 DTO가 전달되는 것은 지양하는 것 같다.

그럼, 컨트롤러와 서비스 레이어 중 어느 곳에서 DTO를 변환해야 할까?

A Service Layer defines an application’s boundary [Cockburn PloP] and its set of available operations from the perspective of interfacing client layers. It encapsulates the application’s business logic, controlling transactions and coor-dinating responses in the implementation of its operations. - 마틴 파울러 -

마틴파울러는 Service 레이어란 애플리케이션의 경계를 정의하고 비즈니스 로직 등 도메인을 캡슐화하는 역할이라고 정의한다.

Controller에서 처리하는 경우 View로부터 받아온 DTO를 Controller에서 Domain(Entity)으로 변환하고 Service 레이어에게 이를 전달하여 작업을 수행한다.

하지만, 이렇게 하면 여러 문제점이 발생한다.

  1. View에 반환할 필요가 없는 데이터까지 Domain 객체에 포함되어 Controller까지 넘어온다.
  2. Controller가 여러 Domain 객체들의 정보를 조합해서 DTO를 생성해야 하는 경우, Service(응용 계층) 로직이 Controller에 포함되게 된다.
  3. 예를 들어, 쇼핑몰 웹 사이트에서 특정 상품의 상세 정보를 보여주는 기능이 있을 때 해당 상품의 정보(Product Domain), 판매자의 정보(Seller Domain), 상품에 대한 리뷰 정보(Review Domain) 등 여러 가지 정보를 한 번에 보여주어야 한다.
  4. 이런 경우, 각각의 Domain 객체들을 따로 DTO로 변환해서 전달하면 클라이언트에서 이들을 다시 조합해야 하므로 비효율적일 수 있다. 따라서 서버에서 미리 여러 Domain을 조합해서 DTO를 만들고 이를 클라이언트에 전달하는 것이 더 나은 방법이다.
  5. 하지만, 이렇게 서버에서 여러 Domain을 조합하는 과정에서 비즈니스 로직을 포함할 수 있다. 판매자의 정보 중 어떤 정보를 포함할 것인지, 리뷰 정보 중 최근 리뷰만을 선택할 것인지 등의 결정은 서비스 계층에서 이루어져야 하는데 이러한 결정이 Controller로 이동한 것이므로, 다른 방법을 선택하는 것이 좋다.
  6. 여러 Domain 객체들을 조회해야 하기 때문에 하나의 Controller가 의존하는 Service의 개수가 많아진다.
  7. Service가 DTO를 반환하면 이런 문제들이 상쇄된다.

하지만, Service 레이어에 DTO가 들어오지 않아야 여러 종류의 컨트롤러에서 해당 Service를 사용할 수 있다. 그러나 현업에서는 여러 종류의 컨트롤러가 한 서비스를 사용하는 경우보다는, 한 종류의 컨트롤러가 서비스를 사용하기 때문에 엄격히 제안할 필요가 없다. 고 한다.

결론

Controller에서 DTO 변환은 몇몇 문제가 있기 때문에 Service에서 하는 것이 좋은 선택 같다.

주의할 점

  • 잘못하면 DTO가 Repository까지 흘러가 아래의 문제를 발생시킬 수 있다.
    1. 비즈니스 로직의 캡슐화 위반
    2. 재사용성의 감소
    3. 테스트의 어려움
  • DTO 변환 시 서비스 메서드 상위에서 DTO체크 및 도메인 변환을 해야 한다. 그리고 변환된 도메인을 사용해서 비즈니스 로직을 수행해야 한다.
public BoardDto createBoard(BoardDto BoardRequestDto) {
	Board board = BoardRequestDto.toEntity();
	//비즈니스 로직
	return BoardDto.from(boardRepository.save(board));
}

참고

https://hudi.blog/data-transfer-object/

https://inpa.tistory.com/entry/JAVA-☕-직렬화Serializable-완벽-마스터하기

https://martinfowler.com/eaaCatalog/dataTransferObject.html

https://www.inflearn.com/questions/1091482/controller의-dto를-repository에서-사용할-수-없는-이유

728x90