인프런 커뮤니티 질문&답변
어댑터에서 도메인에 직접 의존하는 경우에 대해
작성
·
13
·
수정됨
0
안녕하세요.
좋은 강의 잘듣고 많이 배우고 있습니다. 좋은 강의 만들어주셔서 감사합니다.
다름이 아니라, 어댑터에서 엔티티에 의존성을 가지는 것이 크게 문제되지 않는다고 말씀주신 내용에 의문이 있어 질문드립니다!
어댑터에서 도메인에 직접 의존하는 것을 막기 위해서 변환 역할을 하는 매퍼 클래스나 DTO에서 엔티티를 자기 자신으로 변환하는 로직을 가지는 것은 어떨까요?
타이트하게 룰을 잡는 케이스를 가정한다면, application layer 내부에 port가 존재할 것이고 이 port에서 return 하는 dto도 application layer일 것이고, 이 dto 안에서 Entity를 받아서 자기 자신으로 변환하는 로직을 가진다면 entity가 application 밖으로 나가지 않을 수 있지 않을까요?
(controller, repository 등 마찬가지입니다)
그리고 dto를 application에서 변환하는 것이 로직의 침투라고 표현하시기도 했는데, 그렇다면 provided port 역시 어댑터의 요구가 침투하는 구조라고 볼 수도 있을 것 같기도 합니다..
(생각해보니 이상적으로는 계층별로 DTO를 생성하는게 맞다고 생각은 들지만, "엔티티를 반환하는 트레이드오프가 굉장히 효율적이고 정당하다"라고 말씀하신 것 같다고도 생각이 듭니다)
강의에서 항상 기술이 등장한 배경과 사상을 이해하고, 이를 바탕으로 정당하게 규칙을 스스로 정하는 것을 강조하시는 것 같습니다. 그래서 강의에서 말씀하신 내용이 비슷한 맥락으로, "절대 노출되면 안된다"라는 것에 대한 반박이라고 받아들여지긴 합니다만.. 그래도 바깥쪽으로 노출하지 않는 것이 의존성 관리 차원에서 더 좋지 않을까하는 생각에 질문드립니다!
답변 2
0
사실 실무에서는 계층간 의존 방향성이나 독립성이 그렇게 엄격하게 지켜지지 않고 개발되는 경우가 많습니다. 애플리케이션 서비스 계층은 항상 DTO를 주고 받도록 만드는 원칙을 가지고 개발하는 유명 기업 개발팀도 많이 봤습니다.
그럼에도 이번 강의가 헥사고날 아키텍처가 제공하는 유익과 가치를 잘 따르려면 어떤 접근이 가능할지에 대해서 이야기하는 것이니만큼, 헥사고날 아키텍처, 또 클린 아키텍처가 추구하는 의존 관계를 잘 지키면 어떻게 될지를 계속 살펴보려고 하고, 그 기준에 따라서 어떤 모습이 나오는지 탐구해보고 싶었습니다.
언제부턴가 많은 분들이 도메인 계층의 요소가 어댑터 계층(인프라, 컨트롤러 등등)에 노출되는 것을 문제라고 이야기하는 것을 잘 알고 있습니다. 엔티티를 컨트롤러에서 직접 리턴하는 초간단 방식이 결국 큰 문제를 일으키기도 하고, 뷰 역할을 하는 코드나 API 응답 생성만 해야 하는 코드에서 도메인 로직을 건드리는 문제 등등도 많이 이야기 되고 있죠.
그래서 만만하게 애플리케이션 계층은 DTO로 변환된 어댑터가 필요로 하는 정보만 노출한다는 것을 해결책으로 삼습니다.
하지만 저는 이 방식이 단순한 경우엔 별 차이가 없겠지만, 시간이 흘러서 동일 포트를 이용하는 어댑터가 여럿이 되고(웹 API, 배치, 다른 메시징 클라이언트 등등), 그때마다 DTO가 자꾸 새로 만들어지거나, 변경이 되는 것을 자주 관찰했습니다. 어댑터 앞 단의 액터의 요구 변경이나 배치 로직 등의 변경이 있을 때마다 이렇게 DTO가 바뀌고, 더 나아가서 리포지토리 로직도 따라서 바뀌는 등의 예는 정말 아주 오래 전부터, DB SQL에 UI 로직까지 반영되던 방식으로 개발하던 시절의 유물이기도 합니다.
강의에서 제가 강조했던 것처럼 그런 DTO의 과도한 사용은 결국 어댑터와 애플리케이션 서비스의 강한 의존를 만들고, 그에 따른 잦은 변경이 여러 계층에서 일어나게 만든다고 봅니다. 이게 DTO라는 아주 심플한 구조여서 그렇지, 오래전 서비스 계층에서 뷰에서 조립할 HTML 조각을 생성해서 보내주던 것과 솔직히 다르지 않다고 봅니다.
어탭터가 도메인을 이용하는 것은 사실 자연스럽습니다. 도메인 중심의 개발에서 모든 코드는 도메인의 개념과 보편언어, 도메인 로직 등에 의존하는 것이 자연스럽습니다. 한번 모델이 잘 설계되면 가장 변하지 않고 유지되는 것이 도메인이고, 그 부분이 변경되는 크리티컬한 설계 변경이 있다면 그에 따라 다른 계층의 변화도 자연스럽게 이어지는 것이 맞다고 봅니다.
물론 경우에 따라 포트 입장에서 단순한 값을 리턴하는 것으로 그 의도를 잘 나타낼 수 있다면 간단한 값이나 작은 DTO를 리턴하게 하는게 맞습니다. 이건 단지 엔티티를 읽기전용으로 만든 오브젝트에 옮겨 담는 것과는 다르고, 어댑터와 그 앞의 요구 변화에 따라 도메인이 변경되지 않았음에도 서비스계층의 DTO가 자꾸 바뀌는 일이 일어나지는 않을 것입니다. 하지만 대부분은 애그리거트나 엔티티를 리턴하고, 이를 어떻게 클라이언트에게 보여줄지는 컨트롤러 코드 내의 뷰 로직을 담당하는 응답 객체 생성 시점에 결정되는 것이 훨씬 낫다고 봅니다.
도메인은 가장 소중하고 중요하고, 그리고 애플리케이션 모든 영역이 이에 의존하는 것이 너무 자연스럽습니다.
물론 요즘은 제가 주장하는 것이 현장에서 보편적으로 통용되는 주류 논리가 아닐 수 있습니다. 언제부턴가 DTO 만능 시대라, DTO는 특정 계층에 속하지 않는 모든 계층에서 사용하는 보편적인 오브젝트로 취급하기도 합니다. 요즘 AI에게 스프링 개발을 시키면 그렇게 만들더라고요. 하지만 계층형 아키테처의 원칙과 헥사고날 아키텍처가 추구하는 것을 잘 지키면서 유지보수성이 좋게 발전하는 코드가 주는 가치가 충분히 있다고 믿기에 이런 구조를 따르려고 합니다.
Provided interface가 어댑터의 요구에 의존한다는 이야기도 하셨는데, 이건 반대로 접근하는 것이 헥사고날 아키텍처의 원칙인 듯 싶습니다. 기본적으로 도메인과 이 로직을 담은 헥사곤은 우선 도메인이 제공하는 기능에 충실하게 만들어지고, 결과적으로 액터, 그리고 이 중계 역할을 하는 어댑터가 이용하는 것이지, 어댑터의 요구를 충족하기 위해서 포트와 인터페이스를 만든다고 생각하지 않으면 좋겠습니다. 물론 액터가 없는 서비스는 없을 것이지만, 도메인 관점에서 설계한 작업의 결과로서 인터페이스가 정의되고 이게 서비스에서 제공되는 것이어야지, 단지 외부의 요구가 있으니까 이걸 만든다는 식으로 접근하지 않는게 도메인 모델 중심의 개발 방법일 듯합니다. 외부의 요구가 존재한다는 것을, 먼저 도메인 모델에 반영하고 안에서부터 설계해서 밖으로 나가는 것이 자연스럽습니다.
더 이야기하고 싶으시면 댓글을 남겨주세요.
0
안녕하세요, 인프런 AI 인턴입니다. 개발덕후님!
강의를 들으며 어댑터에서 엔티티에 직접 의존성을 가질 때의 문제점과, 이를 해결하기 위한 방법에 대한 고민을 남겨주셨네요. 굉장히 중요한 설계 고려사항을 잘 짚어주셨습니다.
어댑터의 의존성 관리
어댑터(예: 컨트롤러, 리포지토리 등)에서 도메인 엔티티에 의존성을 가지는 것은 한층 더 강한 결합을 초래할 수 있고, 이는 시스템의 변경 용이성을 저하시킬 수 있습니다. 따라서, 의존성 역전 원칙(Dependency Inversion Principle)에 따라 어댑터가 도메인 엔티티 대신, 애플리케이션 계층의 인터페이스나 DTO를 사용하도록 설계하는 것이 바람직합니다.
매퍼 클래스 및 DTO 변환 방식
매퍼 클래스 사용: 매퍼 클래스는 어댑터와 도메인 간의 데이터를 변환하는 데 유용합니다. 어댑터에서 직접 엔티티를 다루지 않고, 매퍼를 통해서 데이터를 변환하여 사용하면, 의존성이 간접적으로 관리되어 더 유연한 설계가 가능합니다.
DTO 사용: 어댑터에서 엔티티 대신 DTO를 사용하여 각 계층 간 데이터를 주고받으며, DTO 내부에서 엔티티로의 변환 로직을 가질 수 있습니다. 이는 엔티티가 외부로 노출되지 않도록 막아주며, 시스템의 안정성을 높여줍니다.
의존성 관리의 중요성
토비님의 강의에서도 강조하시듯, 기술의 등장 배경과 그 철학을 이해하는 것이 중요하며, 이를 바탕으로 시스템에 맞는 규칙을 설정하는 것이 핵심입니다. 강의에서 이러한 관점을 기반으로, “절대 노출되면 안 된다”는 것이 아니라, 시스템의 유지보수성과 확장성을 고려했을 때 어떤 접근이 더 적절한지를 고민한 뒤 설계하는 것이 좋다고 말씀하신 것입니다.
관련해서, 토비님이 어플리케이션과 도메인의 경계에 대한 질문에서 헥사고날 아키텍처의 원칙과 의존성 관리에 대한 다양한 방안을 설명하신 적이 있습니다. 해당 답변을 참고하시면 더 많은 인사이트를 얻으실 수 있을 것입니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.




