DTO의 사용범위는 어디까지? 또, DTO 변환은 어디서?
전에 Controller와 Service 계층 간 데이터 전달을 어떻게 해야 좋을지에 대한 포스팅을 작성했었다.
뭔가 숲보다 나무를 먼저 본? 느낌이긴한데... 하하;
그때 배우던 예제에선 Service가 Entity를 반환했고, 또 Repository 는 DB단에 제일 가깝기 때문에, 단순히 DTO의 사용범위는 Controller에서 Service 계층까지-라고 생각했다. (그리고 tmi지만 파라미터와 반환타입 둘 다 고려해야 한다는 사실을 간과해서 뭔가 찝찝함이 남은 상태다..)
그런데 QueryDSL 학습 중.... @QueryProjection을 사용하면 DTO 가 Repository에서 생성되어서 Service에 반환되는데, Repository 계층까지 DTO가 들어와도 되나? 싶고 당황스러웠다...
생각이 깊어지다 보니 DTO를 Entity로 변환하는 위치가 Controller여야 하는지 Service 여야 하는지에 대해서도 의문이 생겼다. 구글링 하다가 궁금했던 점을 속시원하게 긁어주는(!) 포스팅을 발견하게 되어서 핵심만 정리해봤다. 좋은 포스팅 감사합니다 (_ _) 꾸벅
제가 궁금한 점만 추려서 정리한 글이기 때문에, 자세한 근거와 흐름을 알기 위해서는 인용한 포스팅을 일독하시는 것을 권장드립니다.
출처 : https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/
1. DTO를 어느 계층까지 사용해도 되는가? (= Entity(Domain)을 어느 계층까지 노출해도 되는가?)
Repository는 Entity의 영속성을 관장하는 역할을 한다. 따라서 정공법으로는 Repository에서 DTO 사용을 지양하는 것이 좋다.
그러나 QueryDSL 같은 프레임워크를 사용하는 경우 Repository단까지 DTO가 쓰일 수도 있다.
내가 했던 의문을 똑같이 하신 분이 있길래 반가워서(!) 스크랩해왔다.
음.. 어쨌든 결론을 정리해보자면
기본적으로 Controller-Service 계층까지 DTO를 사용하고, 상황에 따라서 repository 계층에도 DTO를 사용할 수 있다.
2. DTO - Entity 간의 변환 위치는 어디서 수행되어야 하는가?
(Controller 계층이어야 하는가, Service 계층이어야 하는가?)
일단 Repository 계층은 DTO를 사용해도 되는지- 에 대한 결론 자체가 유연하기 때문에 의제에서 빼놓자.
Entity를 표현계층, 즉 Controller에서 사용하는 경우 발생 가능한 문제들은 다음과 같다.
1. 결합도 증가,
2. 도메인의 변경이 Controller의 변경을 촉발하는 유지보수의 문제로 이어질 수 있다.
- View에 반환할 필요가 없는 데이터까지 Entity에 포함되어 Controller까지 넘어온다
- 복잡한 어플리케이션의 경우 Controller가 View에서 전달받은 DTO만으로 Entity를 구성하기란 어렵다. Repository를 통해 여러 부수적인 정보들을 조회하여 Entity를 구성할 경우 결국 Service 로직이 Controller에 포함되게 된다
- 여러 Entity객체들을 조회해야 하기 때문에 하나의 Controller가 의존하는 Service 의 개수가 비대해진다
Service 계층(응용 계층)은 어플리케이션의 경계를 정의하고 비즈니스 로직을 캡슐화하여 트랜잭션을 제어하는 역할을 한다. 즉, 도메인을 보호한다.
따라서 DTO를 Service에게 넘겨주어 Service가 Entity로 변환시키도록 하는 것이 더 나은 방안이지만,
한편으로는 Service 레이어에 DTO가 들어오지 않아야, 여러 종류의 컨트롤러에서 해당 서비스를 사용할 수 있다는 딜레마에 빠진다.
여러 종류의 컨트롤러가 한 서비스를 사용하는 경우와, 한 종류의 컨트롤러만 서비스를 사용하는 경우가 둘 다 발생할 수 있을 것이다. 실무에서는 두번째 경우가 대부분이라, 일반적으로는 서비스로 DTO 진입을 허용하되, 서비스 메소드 상위에서 DTO 체크 및 도메인 변환 후에는 DTO는 사용하지 않고 Entity만 사용하고(초기에 도메인으로 변환하는 규칙), 반환타입의 경우 Entity를 내보내고 컨트롤러에서 DTO로 변환한다. 고 한다.
정리
1. DTO의 사용범위
기본적으로 Controller-Service 계층까지 DTO를 사용하지만, 상황에 따라서 repository 계층에도 DTO를 사용할 수 있다는 점을 잊지 말 것!
2. DTO - Entity 변환 위치
Controller-Service 계층까지 DTO를 사용한다는 가정하에,
일반적인 경우, 컨트롤러-서비스가 1대1 매칭이 될 때,
음 소규모 프로젝트라고 보면 될까...? 다음과 같이 사용하면 된달까(절대적인 전략은 아니다)
하지만 QueryDSL을 사용한다면 ... 🙄 먼산...
view <-(DTO)-> Controller[Entity->DTO 변환] (DTO)-><-(Entity) Service[DTO->Entity 초기변환] <-(Entity)-> Repository <-(DAO)-(Entity)-> DB
Controller와 Service 계층 간의 파라미터 및 반환타입이 꽤... 복잡하다. 한번 더 정리해봤다.
Service는 Controller에서 받아온 DTO를 Entity로 변환해야하고, 결과를 받아와 다시 Controller로 Entity를 반환하면
Controller는 Service가 반환한 Entity를 다시 DTO로 변환해서 사용하는 식이다. (뭐가 이리 복잡해 , , )
< One-Way Mapping 단방향 매핑>
Controller 반환타입: DTO
Controller -- DTO --> Service(초기에 Entity로 변환)
Service 반환타입: Entity
Controller(DTO로 변환) <-- Entity -- Service
Entity 노출, DTO 사용범위 등에 대한 문제는 딱 떨어지는 결론이 없다. 😞 (찝찝해 .. )
프로젝트의 규모, 아키텍쳐의 방향 등을 종합적으로 고려해야 하고, 때문에 유연하게 사고하는 게 중요하다는 거다.
실무에서 설계 시에 사용할 수 있는 전략이 몇가지 추려져 있겠거니 싶은데..
상황에 따라 달라지는 매핑전략을 따로 정리해두신 분을 또 발견했다!! 참고하면 좋을 것 같다.
위에 정리한 전략의 이름도 이 포스팅에서 찾았다. (>.<)V
출처:https://intrepidgeeks.com/tutorial/dto-availability-and-mapping-strategy
이제야 말끔하게 정리가 된 듯해 뿌듯하기 그지없다~ ~ 참고한 글의 글쓴이들에게 무한한 감사를 보냅니당 ^--^
틀린 내용이 있다면, 또 제가 놓친 부분이 있다면 댓글로 알려주시면 감사하겠습니다!