DTO란?
- DTO는 Data Transfer Object로 계층 간 데이터 교환을 하기 위해 사용하는 객체입니다.
- DTO는 DB에서 꺼낸 데이터를 저장하는 Entity를 가지고 만들어지고, Controller 같은 클라이언트 단과 직접 마주하는 계층에 직접 Entity를 전달하지 않고 DTO를 사용해 데이터를 교환합니다.
Entity와 DTO를 분리하는 이유
- Entity가 외부에 노출되는 것을 막기 위함
Entity가 외부에 노출된다면 사용자 민감 정보 등이 노출될 수 있습니다.
- 컨트롤러 메서드 파라미터에 Entity 그래도 사용 시 validation 코드가 Entity 코드에 섞이게 됩니다.
컨트롤러 같은 경우 입력에 대한 검증이 필요하다.
허나 컨트롤러에서 메소드 파라미터로 Entity를 그대로 사용하게 된다면, Entity에 @NotEmpty 같은 코드들이 섞이게 된다.
엔티티는 모델링 코드와 연관관계를 위한 코드를 제외하고 의존성이 없도록 유지해야 한다.
가장 큰 문제는 API 스펙과 Entity 사이에 의존성이 생긴다. Entity 변경에 따라 API 스펙 또한 변경해야 하게 된다.
- 서비스 메서드 파라미터에 Entity 그대로 사용 시영속화 상태를 보장할 수 없습니다.
서비스 객체의 입장에서 인자로 넘어온 엔티티가 영속화 상태라는 것을 보장할 수 없다.
서비스 객체에서 인자로 넘어온 엔티티의 상태를 변경하고 트랜잭션이 commit된다면, JPA가 flush하면서 DB에 update쿼리를 던지지 않습니다. 이유는 해당 엔티티는 비영속 상태이므로 update 쿼리를 날리지 않게 됩니다.
DTO를 어디까지 사용할 것인가?
- Spring 프로젝트를 진행하면서 DTO를 어디까지 사용해야하는지에 대해 고민하였습니다.
- repository에서 가져와 service단에 주고 이 엔티티를 DTO로 만들어 컨트롤러에 주고 컨트롤러는 만들어진 DTO로 응답하는 방법
- repository에서 가져와 service단에 주고 이 엔티티를 컨트롤러에 주고 컨트롤러에서 이 엔티티를 가지고 DTO를 만드는 방법
즉, 저의 고민은 Entity에서 DTO로 변환하는 과정을 Service단에서 할 것인지, Controller단에서 할 것인지 즉, 도메인을 어디까지 노출시킬 것인지에 대한 고민이였습니다.
Tecoble의 블로그의 글을 인용하면
마틴 파울러는 Service 레이어란 어플리케이션의 경계를 정의하고 비즈니스 로직 등 도메인을 캡슐화하는 역할이라고 정의합니다. 즉, 도메인을 보호합니다. 도메인 Model을 표현 계층에서 사용하는 경우 결합도가 증가하여, 도메인의 변경이 Controller의 변경을 촉발하는 유지보수의 문제로 이어질 수 있습니다.
이러한 관점에서 바라볼 때, 레이어간 데이터 전달 목적으로 DTO를 엄격하게 고수한다면 변환 로직이 Service 레이어에서 정의되어야 한다는 의견이 존재했습니다. 요청에 대한 응답 역시 Service 레이어의 일부분이기 때문입니다.
Service단에서 Entity를 반환하는 경우
Service단에서 도메인 모델을 Controller로 반환하고, Controller가 Entity를 DTO로 변환할 경우 예상되는 문제를 무엇이 있을까요?
- View에 반환할 필요가 없는 데이터까지 Domain객체에 포함되어 Controller까지 넘어옵니다.
- Controller가 여러 도메인 객체들의 정보를 조합해 DTO를 생성한다면, 도메인 로직이 Controller에 포함됩니다.
- 여러 도메인 객체들을 조회해야 하기 때문에 하나의 Controller가 의존하는 Service의 개수가 비대해집니다.
하지만, Service단에서 DTO를 반환한다면 이러한 단점을 해결할 수 있습니다.
Service단에서 DTO를 사용하는 경우
Controller가 View로부터 받은 DTO를 Entity로 변환한 뒤, Service 레이어가 Entity를 전달받아 일련의 비즈니스 로직을 수행할 경우를 생각해봅시다.
- 복잡한 어플리케이션의 경우 Controller가 View에서 전달받은 DTO만으로 Entity를 구성하기 어렵습니다. Repository를 통해 여러 부수적인 정보들을 조회하여 도메인 객체를 구성할 수 있는 경우도 존재합니다.- Controller에서 DTO를 완벽하게 만들어서 도메인 객체로 구성한 뒤 Service에 넘겨주려면, 복잡한 경우 Controller가 여러 Service(혹은 Repository)에 의존하게 됩니다.이런 경우 DTO를 Service에게 넘겨주어 Service가 Entity로 변환시키도록 하는 것이 더 나은 방안이라 생각합니다.
그래서 DTO는 어디까지 사용해야하나?
정답은 없는 것 같습니다.
serivce단에 DTO에 안들어오게 되면, 여러 종류의 컨트롤러에서 해당 서비스를 사용할 수 있다는 점에서 DTO를 Service단에 들어오면 안된다는 의견도 존재하며, 현업을 하다보면 여러 종류의 컨트롤러가 한 서비스를 사용하기 보단, 한 종류의 컨트롤러가 한 서비스를 쓰기 때문에 엄격히 제한할 필요는 없다는 의견도 존재합니다.
Entity를 어디까지 노출할 것인지는 팀원들과 원만한 합의하에 규칙을 정하고 그 방식대로 진행하면 괜찮은 것 같습니다.
마지막
저는 프로젝트 당시에 DTO를 service까지 가져와 도메인으로 변환하여 사용하고 Service단에서 DTO를 생성하여 Controller단에 DTO를 반환하였습니다. 이렇게 하였을 때 위와 같은 장점을 가질 수 있었고, 구현의 편리함도 가져갈 수 있었습니다.
참고
https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/
https://gmlwjd9405.github.io/2018/12/25/difference-dao-dto-entity.html
https://cheolhojung.github.io/posts/record/jpa-entity-vs-dto.html
'개발' 카테고리의 다른 글
JPA 즉시 로딩 JPA Repository를 쓰면서 실수 안 할 수 있는가? (0) | 2024.04.06 |
---|---|
TDD를 알고리즘 문제 풀이에 적용시켜보자 (0) | 2022.05.10 |
TDD START! (0) | 2022.05.03 |