# 2022-06-03 service layer에서 dto를 반환하는 것 vs 도메인을 반환하는 것
# 아고라
아고라
고대 그리스의 도시국가(폴리스)에서 자유 시민들이 자유롭게 토론을 벌이던 장소. 아고라라는 단어 자체의 의미는 '집결지'(Gathering Place)이다.
우리는 다양한 미션을 진행하며 많은 의문과 고민에 직면하게 된다. 미션을 진행하며 단순히 정답을 찾는 것이 아닌 자신만의 근거와 사례를 만들어가는 과정이라고 생각한다. 우리 아고라 에서는 구체적인 예시를 기반으로 다양한 상황을 제시한다. 참여자는 그에 대한 실제 적용 사례를 이야기 하거나 자신만의 근거를 이야기 하며 건전한 토론을 진행한다.
# 목적
미션을 진행하며 직면하는 다양한 의문들에 대한 자신만의 근거를 만들기 위한 의식적인 연습
과 자신의 생각을 의도에 맞게 적절히 전달할 수 있는 말하기 연습
이 가장 큰 목적이다.
# service layer에서 dto를 반환하는 것 vs 도메인을 반환하는 것
주최자
: 우리는 도메인 혹은 엔티티 객체의 내부를 보호하며 필요한 데이터만을 전달하기 위해 응답 dto를 활용하곤 한다. 즉 우리는 도메인 객체를 그대로 반환하는 것에 대한 위험성을 충분히 인지하고 있으며 필요성을 느끼고 있다. 그렇다면 이러한 도메인 -> dto로 변환하는 행위를 비즈니스 로직을 담당하는 service layer에서 변환
할지 presentation layer를 담당하는 controller에서 변환
할지 service layer 입장
에서 이야기를 나눠보려 한다.
WARNING
보다 더 나은 표기를 위해 service layer에서 dto를 변환하는 진영을 dto파
, 도메인을 반환하는 진영을 도메인파
로 작성하였습니다.
해당 내용은 토론을 진행할 때 주최자가 기록한 것을 기반으로 각색한 것입니다! 모든 내용을 기록하지 못했기 때문에 잘못된 부분이나 틀린 부분이 존재할 수 있습니다. 만약 잘못된 부분이나 추가하고 싶은 내용이 있다면 아래 코멘트에 남겨주세요!
dto파
: 컨트롤러가 도메인 객체에 대한 의존성을 가지는 것은 위험하다. 도메인 안에는 애플리케이션의 핵심인 비즈니스 로직을 담고 있으며 해당 객체가 controller 까지 넘어가면 어떻게 사용될지 전혀 모르기 때문이다.
도메인파
: 체스 미션을 진행할 때 우리는 체스 기물에 민감한 정보를 가지고 있는가? 전혀 아니라고 생각한다. 결국 dto 객체를 만드는 것 자체로 관리 포인트가 늘어날 뿐이다. 숨길 정보가 없거나 작은 규모의 프로젝트인 경우 dto까지 만들어 반환할 필요는 없다고 생각한다.
또한 컨트롤러에서 도메인을 사용할 때 단순히 getter를 사용하도록 제한함으로써 위험성을 줄일 수 있다. 결국 이것은 코드의 문제보다 휴먼 에러에 가깝다고 생각한다.
dto파
: 현재 우리는 복잡한 요구사항을 기반으로 미션을 진행하지 않기 때문에 도메인을 고려할 여지가 생긴다. 만약 아키텍처적으로 쉽게 결정할 수 없는 복잡한 상황이 오게 되면 결합성을 낮추는 측면에서도 dto를 반환하는 것이 좋다고 생각한다. 만약 도메인이 controller에서도 사용되면 결국 모든 계층에서 사용되는 것을 의미하는데 이것은 도메인 객체가 수정되면 애플리케이션 전반적으로 영향을 끼칠 우려가 있다.
dto파
: 우리는 레이어드 아키텍처를 나눈 이유를 생각해봐야 한다. 큰 규모의 애플리케이션을 개발하게 된다면 결합성을 낮추는 측면에서도 dto는 더더욱 필요할 것이다. 또한 규모가 커질 수록 도메인은 복잡한 요구사항을 해결하기 위해 다양한 도메인 객체를 참조할 것이다. 이렇게 깊은 참조를 가진 도메인 객체를 반환하는 것은 과연 합당한지 고민해봐야 한다.
도메인파
: 우리는 간혹 service에서 service를 의존하여 사용 하곤 한다. 만약 service layer에서 응답을 위한 dto를 반환할 경우 service 내부에서 사용하기 위해 응답 dto를 도메인으로 변환해야 하는 비용이 추가적으로 발생하게 된다.
또한 service layer에서 응답을 dto로 강제한다고 가정한다. 결국 도메인을 그대로 조회하기 위해서는 dao 혹은 repository를 의존해야 하며 이것은 결국 해당 dao를 사용하는 다양한 service에 온전한 도메인 객체를 얻기 위한 중복된 코드를 야기할 수 있다.
dto파
: 단순히 도메인에 집중하지 않고 기능이라는 단위에 집중해서 service를 나눠도 되지 않을까? 그것이 중복된 코드를 야기할 수는 있지만 해당 비즈니스를 적절히 처리하기 위해서라면 감수할 수 있는 영역이라고 생각한다.
또한 이러한 현상이 현업에서 자주 벌어지는 일인가? 그것은 실제 사용되는 애플리케이션에서 중복될 문제가 있는지 고민해볼 필요가 있다. 이러한 중복들을 도메인 내부로 옮길 수 있지 않은가?
도메인파
: 하지만 결국 dao나 repository의 persistence layer에서 조회한 온전한 데이터는 중복 코드를 야기할 수 밖에 없다. 이런 경우는 어떻게 해결하는게 좋은가?
dto파
: 특정 비즈니스를 해결하기 위해서 필요한 것이라면 중복이라고 생각하지 않는다. 또한 기능 단위로 나눈 service는 특정 기능에 종속적인 행위들을 필요로 하기 때문에 약간의 차이를 가질 수도 있다.
dto파
: 앞서 언급한 것 처럼 우리는 service에서 service를 의존해서 사용할 수 있다
고 말했다. 그렇다면 어떠한 상황에서 service에서 service를 의존할 수 있는가?
도메인파
: 현재 shopping cart 미션을 살펴보면 크게 두 개의 패키지로 분리된 형태를 확인할 수 있다. 인증과 관련된 auth와 장바구니 비즈니스와 관련된 shopping cart로 크게 분리되어 있다. 각각의 패키지는 역할에 따라 분리되어 있다. 이것은 마치 수직으로 잘린 기둥 처럼 보인다. 우리는 인증을 진행할 때 Customer와 관련된 도메인에서 아이디와 비밀번호를 확인해야 한다. 결국 이러한 행위는 AuthService에서 CustomerService를 참조하여 해결할 수 있게 된다.
도메인파
: 결국 service에서 응답에 대한 dto를 만들 경우 controller에 의존적인 코드를 작성할 수 있다. service도 하나의 객체이기 때문에 객체간의 의존성을 최소화해야 하는데 응답에 dto를 반환할 경우 presentation layer에 매우 종속적인 구조이지 않을까?
dto파
: service의 역할에 대해서 다시 한 번 생각해보자. 마틴 파울러는 service layer가 애플리케이션의 경계를 정의하며 비즈니스 로직 등 도메인을 캡슐화
하는 역할이라고 정의했다. 즉 도메인을 보호해야 하는 것이 해당 계층의 역할이 된다. 결국 이러한 요청에 대한 응답을 dto로 변환하는 것도 service layer의 역할의 일부분이다. 그렇다면 도메인파
가 생각하는 service의 역할은 무엇인가?
도메인파
: 여러 도메인들의 조각난 로직들을 하나의 단위로 묶어준다. 이러한 로직들이 모여 하나의 큰 기능을 이루기 위한 단위가 될 수 있다. 하지만 요청과 응답 dto의 변환에 대한 책임 까지 가지기엔 service layer가 너무 많은 책임을 가지는 것은 아닌가?
dto파
: 그렇다면 controller의 역할에 대해서도 고민해보자. controller는 단순히 요청과 응답에 대한 책임을 기반으로 애플리케이션 맨 앞단에서 통신 위해 위치하고 있다. service에서 응답에 대한 처리를 하게 되면 controller의 역할은 모호해질 수 있다고 생각할 수 있다. 하지만 대부분 많은 기능들을 spring에서 처리해주고 있기 때문에 그렇게 보일 뿐이다. 실상은 많은 일을 하고 있다. 이번에 학습하게 된 interceptor나 argumentResolver등 도 결국 이러한 controller에서 중복된 로직들을 미리 처리하기 위해 사용할 뿐이다.
dto파
: 만약 service 도메인을 반환하도록 하면 여러 도메인을 묶어서 처리해야 할 경우 어떻게 해결했는가? 이번 지하철 미션에서 최단 경로와 최단 경로를 거치는 지하철역과 최단 거리를 반환하는 것 등 다양한 도메인 정보들이 묶일 수 있다. 이것은 결국 controller에서 여러 service를 의존하여 응답 dto를 생성하여 처리했는가?
도메인파
: Path
와 같은 dto 같은 도메인 객체를 만들 수 있다. 해당 객체는 실제 유의미한 비즈니스 로직은 포함하지 않지만 도메인에 대한 정보를 담고 있기 때문에 도메인 내의 정보 전달을 위해 충분히 사용될 여지가 있었다.
# 최후의 발언
익명의 크루1
: service에서 도메인을 반환하게 되면 controller에서 악용될 우려가 있다고 하지만 이러한 위험성에 대해서는 공감할 수 없다. controller가 도메인이 오는 것은 결국 재사용성을 위한 타협이다.
익명의 크루2
: service도 하나의 객체로 바라보자. 온전한 도메인을 반환하는 것은 service간 통신 시 변환 로직들을 최소화할 수 있다.
익명의 크루3
: 아직은 controller에서 도메인을 알게되면 의존이 안정적으로 향하는 것이 아닌 것 처럼 느껴진다. 결국 모든 계층이 도메인에 의존하고 있는 형태가 되며 도메인에 대한 수정으로 애플리케이션 전반적으로 영향을 끼칠 우려가 있다.
익명의 크루4
: Don't Talk to Strangers
, 디미터 법칙에 대해 들어본 적이 있을 것이다. 이것은 객체와 객체 사이에서만 해당하는 법칙이 아니라 layer와 layer 사이에서도 적용될 수 있다. service와 service도 결국 같은 layer에서 통신을 진행한다. 이때는 도메인을 반환하도록 하고 service와 controller가 통신할 때는 dto를 반환하여 활용할 수 있다. 핵심은 layer를 넘나들며 통신하는 것을 지양해야 한다는 것이다.
익명의 크루5
: 우리는 이번 토론을 통해 각 진영에 대해 장점과 단점에 대해 의견을 나눴으며 결국 완벽에 가까운 정답은 찾을 수 없었다. 결국 상황 혹은 규모에 따라 적절히 선택할 수 있으며 사용할 때 어떠한 이점과 단점이 있는지 의식적으로 확인하며 사용하면 큰 문제가 없다고 생각한다.
# 회고
우선 이번 아고라에 참석해준 각 진영의 참여자들과 참관한 크루들에게 감사를 표한다. 생각보다 바쁜 시기와 팀 프로젝트로 정신이 없는 와중에 대략 15명의 크루들이 참여하였다. 토론 형식으로 진행하니 다양한 시선에서 많은 생각들을 들을 수 있었으며 각 진영은 실제 적용한 미션을 기반으로 자신의 근거를 적절히 어필할 수 있었기 때문에 나름 성공적으로 마무리 했다고 생각한다.
내가 정한 아고라를 가장 큰 목표는 부담 없이 언제든 참여할 수 있는 토론의 장을 마련하는 것
이다. 부담 없는 참여를 위해서는 기록에 대한 부담
을 줄여야 한다. 하지만 이번 아고라를 진행하며 혼자 토론의 모든 내용을 기록하는데는 한계가 있다고 느꼈다. 또한 나도 의식적인 연습
을 위한 참여자로써 모든 대화를 기록하고 내 것으로 만드는 것은 쉽지 않게 느껴졌다. 그렇기 때문에 위에 작성한 내용들은 상당부분 각색되어 있으며 때론 주관적인 의견이 섞일 수 있게 되었다. 이것을 해결하기 위한 방법을 고민해봐야 한다.
또한 주제 선정에 대한 부담도 가지고 있다. 모든 크루들과 친한 것이 아니기 때문에 다들 어떠한 고민을 진행하며 미션에 임하고 있는지 확인하는 것은 쉽지 않다. 결국 평소 내가 궁금했던 것이나 나누고 싶은 주제를 선택할 수 밖에 없는데 이것이 많은 수요를 야기하지 않으면 많은 참여를 이끌 수 없을 것이다. 최대한 많은 홍보와 질문을 통해 다른 크루들이 고민하고 있는 것들을 잘 저장해두어야 한다.
몇몇 크루들의 이야기를 통해 좋은 반응들을 듣고 난 뒤 괜시리 뿌듯한 마음이 들었다. 아직은 작은 영향력을 행사하고 있지만 이러한 경험들을 차곡차곡 쌓아 함께 자라기
위한 발판을 마련하고 싶다.
# References
DTO를 사용한다면 어느 Layer까지 사용하는 것이 좋을까요? (opens new window)
DTO는 어느 레이어까지 사용하는 것이 맞을까? (opens new window)
DTO의 사용 범위에 대하여 (opens new window)