Skip to content

Commit

Permalink
Update DTO-DomainObject-Converter.md
Browse files Browse the repository at this point in the history
  • Loading branch information
HomoEfficio authored Aug 14, 2021
1 parent 73be0bd commit f98f886
Showing 1 changed file with 17 additions and 6 deletions.
23 changes: 17 additions & 6 deletions DTO-DomainObject-Converter.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@ DTO가 있어야 하는 상황이라면, 프론트엔드의 View에는 결국 DT

## 컨트롤러 vs 서비스

처음에는 컨트롤러냐 서비스냐가 고민이었다. 컨트롤러 메서드의 인자로 DTO가 사용되는 점을 감안하면 컨트롤러에 변환 로직을 두는 게 나을 것 같다. 하지만, DTO를 Domain 객체로 만들 때는 Repository를 통한 조회가 필요할 때가 종종 있고, 이 때는 컨트롤러에서 처리할 수가 없다. 그래서 서비스에서 DTO - Domain 객체 변환을 담당하게 했다.
컨트롤러냐 서비스냐를 얘기하기 전에 살짝 용어부터 정하고 가자.
웹 애플리케이션에서 컨트롤러가 지칭하는 것에는 별다른 혼동이 없다. 그냥 시스템 외부로 노출되는 api 라고 생각하면 된다.
반면에 서비스는 굉장히 여러곳에서 사용되는 용어라서 미묘한 혼동이 있을 수 있다.
여기에서 말하는 서비스는 컨트롤러 바로 뒤에서 트랜잭션 관리나 이벤트 처리를 포함해서 타 시스템 연계를 담당하는 역할을 맡는 응용 서비스(Application Service)다. DDD 에서 말하는 도메인 서비스와는 다르다.

다시 본론으로 돌아와서 DTO와 Domain 객체가 서로 변환되는 지점은 어디일까?

처음에는 컨트롤러냐 서비스냐가 고민이었다. 컨트롤러 메서드의 인자로 DTO가 사용되는 점을 감안하면 컨트롤러에 변환 로직을 두는 게 나을 것 같다. 하지만, DTO를 Domain 객체로 만들 때는 Repository를 통한 조회가 필요할 때가 종종 있다. 컨트롤러가 응용 서비스를 거치지 않고 Repository에 접근하는 것은 계층 구조를 위반하므로 일반적으로 권장되지는 않는다. 따라서 이런 경우에는 컨트롤러에서 변환할 수가 없다. 그래서 서비스에서 DTO - Domain 객체 변환을 담당하게 했다.

좀 지나서는 별도로 Converter로 빼는 게 좋을 것 같아서 별도의 Converter 계층을 두고 변환 로직을 모두 Converter에 담고, 각 Converter를 서비스에 주입해서 서비스가 DTO - Domain 객체간 변환을 Converter에게 위임해서 처리하게 했다.

Expand All @@ -45,10 +52,11 @@ Domain 객체 -> DTO -> View 방향의 흐름에서는 필요한 모든 Domain
이 방향의 DTO 변환을 컨트롤러 계층에서 한다면 다음과 같은 단점이 있다.

- 클라이언트에 반환할 필요가 없는 데이터까지 Domain 객체에 포함되어 컨트롤러 계층에 까지 넘어온다.
- Domain 객체가 컨트롤러에 공개되므로, 컨트롤러가 서비스 계층을 건너뛰고 직접 Domain 객체 메서드르 호출할 수 있으므로 응용 로직이 컨트롤러에 스며들 수 있다.
- 여러 Domain 객체로부터 조합되는 DTO의 경우 컨트롤러 계층에서 조합해야 하며 결국 응용 로직이 컨트롤러에 스며든다.
- 여러 Domain 객체를 조회하는 서비스를 각각 호출해야 하므로 의존하는 서비스의 갯수가 늘어날 수 있다.
- JPA를 사용할 때,
- OSIV를 비활성화하면 session 이 서비스 계층에서 종료되므로, 컨트롤러 계층에서 변환하다가 lazy initialization exception 이 발생할 수 있다.
- OSIV를 비활성화하면 session 이 서비스 계층에서 종료되므로, 컨트롤러 계층에서 DTO로 변환하다가 lazy initialization exception 이 발생할 수 있다.
- 양방향 참조가 포함된 객체를 JSON 직렬화 하다가 StackOverflow 가 발생할 수 있다.

반면에 이 방향의 DTO 변환을 서비스 계층에서 할 때 어떤 단점이 있는지는 잘 떠오르는 것이 없다.
Expand All @@ -69,10 +77,12 @@ Domain 객체 -> DTO -> View 방향의 흐름에서는 필요한 모든 Domain

>**Domain 객체 <-> DTO 변환은 컨트롤러 계층이 아니라 서비스 계층에서 처리하는 것이 타당하다.**
>
>Domain 객체 -> DTO 의 변환은 Converter에서 담당하고, Converter를 서비스에 주입해서 서비스 계층에서 Converter를 호출해서 처리
>또는 아예 DTO 내에 변환 로직을 두고 DTO가 Domain 객체를 생성자로 주입 받아서 DTO 내에서 변환, 이 경우에도 DTO가 생성되는 지점을 기준으로 서비스 계층에서 처리한다고 분류
>- Domain 객체 -> DTO 의 변환은 Converter에서 담당하고, Converter를 서비스에 주입해서 서비스 계층에서 Converter를 호출해서 처리
>- 또는 아예 DTO 내에 변환 로직을 두고 DTO가 Domain 객체를 생성자로 주입 받아서 DTO 내에서 변환 - 이 경우에도 DTO가 생성되는 지점을 기준으로 서비스 계층에서 처리한다고 분류.
>- ex) 정적 메서드에 변환 로직을 담고 `XXXOut.fromEntity(entity)` 와 같이 호출해서 DTO로 변환
>
>DTO -> Domain 객체의 변환은 서비스의 private 메서드에서 처리
>DTO -> Domain 객체의 변환은 서비스의 private 메서드에서 처리
>또는 아예 DTO 내에 변환 로직을 두고 `yyyIn.toEntity()` 와 같이 호출해서 Domain 객체로 변환

## 여기서 잠깐! DTO 라는 용어는 적절한가?
Expand All @@ -97,4 +107,5 @@ DTO가 셔틀 역할을 하는 것은 맞지만, [마틴 파울러의 글](https

이규원님이 'View에 사용될 목적의 응답 계약은 ViewModel 이란 패턴이 더 적절할 것 같다'는 의견을 주셨는데 ViewModel을 잘 몰라서 쉽게 설명을 못 하겠다. ViewModel을 알려면 MVC, MVP, PM, MVVM 등을 알아봐야 하는데, 이런 건 노력은 많이 들고 결실은 크지 않을 것 같아서 피하고자 한다. 어디에선가 쉽게 써진 자료를 우연히 발견해서 조금이라도 알게 되면 그때나 업데이트를.. ㅋㅋ

그럼 DTO 대신에 뭐라고 불러야 할까? 솔직히 모르겠다. 개인적으로는 그냥 요청 본문, 응답 본문이 정보 함유량은 좀 떨어져보이지만 가장 무난한 것 같다.
그럼 DTO 대신에 뭐라고 불러야 할까? 솔직히 모르겠다.
개인적으로는 위에 잠시 예시로 나온 것처럼 클라이언트로 부터 들어오는 객체는 코드상으로도 In 이라는 접미사를 붙이고 인객체라고 부르고, 서버로부터 나가는 클라이언트쪽으로 나가는 객체는 Out 이라는 접미사를 붙이고 아웃객체라고 부르고 있다.

0 comments on commit f98f886

Please sign in to comment.