Hexagonal Architecture 실무 적용하기

hynnch2·2025년 8월 11일

팀 내 신규 프로젝트를 준비하는 과정에서 헥사고날 아키텍처를 적용하고자 했습니다.
꽤 여러번의 팀 이동을 하며, 다양한 프로젝트들을 건드리게 되었었는데요.

그 중에서 submodule + hexagonal architecture로 구성된 프로젝트를 보고 비즈니스에 집중할 수 있는 부분들이 좋았습니다. 또한, 기존 MVC 구조에서 벗어나 Domain 중심 개발에 관심을 가지고 있었는데, 더욱 적절한 패턴이라 생각했습니다.

위와 같은 경험을 통해 궁금한 부분들이 꽤나 생기게 되었고, 어떤 방식들이 추구하는 방향인지 알아보며 신규 서버에 적용해보고자 했습니다.

여기서는 헥사고날 아키텍처가 어떤 기능인지에 대한 것보다는 적용하는 과정들을 서술합니다.
직접 적용해보며 더 많은 데이터가 쌓이게 된다면, 자세한 개념과 경험들을 같이 정리하려고 합니다.

적용 과정

팀 내 공유

우선 새로운 기술을 도입하기 위해서는 팀 내 설득이 필요하다고 생각했습니다.
아무리 좋은 기술이어도 팀 내 상황에 따라 최선의 선택은 아닐 수 있다고 생각했으며, 협업을 하는 상황에서는 같이 일하는 동료들의 의견도 매우 중요하다고 생각하기 때문이었습니다.

팀 내에서는 MVC 모델로만 개발하신 분들이 있었기 때문에, 해당 아키텍처를 도입하고자 하는 배경과 신규 아키텍처가 추구하는 방향들을 먼저 정리했습니다.
그리고, 새로운 서버가 지향하는 방향이 여러 채널들의 어댑터 역할을 하기 떄문에, 적용하면 좋은 장점들을 우선 정리했습니다.

초기 프로젝트 설정

위와 같이 package 구조에 대해 먼저 설계를 잡고 진행을 했습니다.

기존에 작업하던 방향들이 워낙 다른 동료들이 모여서 팀을 이루었기 때문에, 이와 같은 문서화가 절실하게 필요하다고 느끼고 있었습니다.

그러므로 먼저 Package 단위로 필요한 것들을 정리하였고, 그 외 자잘한 DTO, DO 네이밍과 생성사 규칙 등의 자잘한 컨벤션들은 별도 문서로 정리하였습니다.

팀 내 컨벤션 찾기

위 구조를 바탕으로 실제 프로젝트 셋팅하는 작업을 진행했고, 그 과정에서 동료의 제안도 적극 반영 했습니다.

현재 프로젝트 구조에서 Domain -> Adapter 의 존재를 모르는데요.

그러므로 외부와 연결된 부분에는 Interface로 연결하여, 외부에 어떤 모듈이 실제 결합되는지는 Domain 입장에서 모르게 됩니다.

여기서 각 Adapter 역할을 하는 모듈들이 Interface를 구현해서 실제 Runtime에 의존성들을 주입하는데요.
이 때 각 모듈들이 구현한 Implementation 구현체들의 이름이 겹치지 않게 처리했습니다.

원래는 Bootstrap 역할을 하는 Server가 모듈을 선택하기 때문에 adapter를 변경할 때는 변경하고자 하는 구현체를 버리고, 새로 생성한 adapter에서 해당 interface를 만들고자 했는데요.

이는 팀원분이 굳이 지울 필요까지는 없을 것 같다고 피드백을 주어, 각 어댑터의 특징이 드러나게 구현체 이름을 변경하고 적용했었습니다.

주관적인 헥사고날 아키텍처 장단점

제가 생각하는 장단점은 다음과 같습니다.

장점
1. Domain 로직을 작성하는 입장에서 외부에 어떤 구현체가 있는지 관심이 없음.
2. 비즈니스 로직에만 관심을 가지고 작성할 수 있음. (단, 외부 API를 연동할 때 어떻게 동작할 지 알고 있다는 가정.)
3. Domaim 단위 테스트를 가볍게 가져갈 수 있음.

단점
1. MVC 구조로 처리하게 되면 불필요한 Port, Adatper가 생성됨.
2. 비즈니스 로직에만 집중할 수 없는 상황이 있을 수 있음.
3. Domain에서는 Adapter에 있는 구현체를 모르기 때문에, 기능을 적극 활용하지 못할 수 있음. (ex, @Transaction, kafka option)

남은 과제

위와 같이 설계 및 프로젝트 문서 정리를 마무리 해서 실제로 프로젝트를 띄우는 것까지 완료 했습니다.

다만, 실제 비즈니스 로직을 작성하기에 앞서 남은 과제도 있었습니다.

  1. Domain을 어떤 단위로 나누어서 처리할 지 고민
  2. 단위 테스트 / 통합 테스트의 범위 고려

우리가 관리하는 Data의 경우 관심있는 데이터를 모아 Domain을 확장하는 방법으로 작성하면 될 것이라고 생각합니다.

다만, 서비스 특성 상 외부에서 가져오는 데이터를 활용하는 부분이 많을 것으로 보이며, 이를 외부에서 전달하는 Domain을 그대로 사용하는게 편하지 않을까 하는 생각이 들어서, 추후 논의해보려고 하는 주제입니다.

추가로 단위 테스트 / 통합 테스트 도 작성 방법에 대해 고민이 있었습니다.
현재 단위 테스트의 경우 domain / adapter 마다 접근하는 방법이 다른데요.!
Domain의 경우 mock 을 통해 정해진 응답 값을 반환하도록 처리했습니다.

다만, adatper의 경우 실제 기능(jpa, sqs)을 사용하기 때문에, adpater의 단위 테스트는 TestContainer 또는 Inmemory-DB를 사용하는 방식으로 작은 범위의 단위 테스트를 작성하도록 우선 처리해둔 상태였습니다.

이를 통합하는 과정이 필요해보이며, 통합 테스트도 이 과정에서 어떻게 전체 시스템 테스트를 녹여낼 지 고민하고 있습니다.

후기

이미 만들어진 프로젝트 위에서 DDD 를 적용한 hexagonal 아키텍처를 건드려보면서 도메인 로직에 집중할 수 있었기 때문에 좋다고 생각했었는데요.

실제로 프로젝트 설계 초기에 Adapter에 단위 테스트를 적용하는 과정에서 기능 테스트가 필요한가 싶은 생각도 많이 들었었습니다.

추가로, Domain의 단위 테스트를 작성할 때 Mock을 통해 처리했었는데 조금 더 좋은 방향은, Test-Adapter를 통해 given-when 으로 처리되는 것이 아닌, 실제 stub 형식으로 처리하는 것이 더 좋은 방향이지 않을까 생각하고 있습니다.

실제 서비스를 구축하면서 맞이한 문제점들이 있을 것이고, 추후 정리해서 글을 작성해보고자 합니다.

긴 글 읽어주셔서 감사합니다.

profile
more than yesterday

0개의 댓글