[헥사고날 아키텍처] Day 5: 점진적 전환 - 기존 Spring 서비스를 안전하게 바꾸기
이 글은 AI(Claude)의 도움을 받아 작성하고, 작성자가 검토·편집했습니다.
서론: 전체 재작성은 아키텍처 전략이 아니다
기존 Service와 JPA Entity가 얽힌 시스템을 한 번에 헥사고날 구조로 바꾸면 오랜 기간 기능 개발이 멈추고 회귀 위험이 커진다. 가장 많이 변하고 테스트가 어려운 업무 흐름 하나를 골라 경계를 만들고, 새 기능부터 그 경계를 따르게 하는 방식이 현실적이다.
1. 전환 대상 선택
1
2
3
4
5
좋은 첫 후보:
규칙이 복잡하고 변경이 잦음
외부 API/DB 결합으로 테스트가 어려움
입출력과 성공 기준이 명확
다른 기능과 데이터 경계를 나눌 수 있음
가장 거대한 핵심 모듈보다 작은 성공을 증명할 수 있는 유스케이스가 낫다.
2. 현재 행동을 고정하기
1
2
3
Characterization Test:
현재 입력에 현재 시스템이 내는 결과를 기록
이상해 보여도 우선 회귀 기준으로 확보
기존 버그를 새 구조에서 고칠지, 호환을 위해 유지할지 별도 결정한다. 리팩터링과 정책 변경을 한 배포에 섞지 않는다.
3. Port를 기존 코드 앞에 세우기
1
새 Use Case → Outbound Port ← Legacy Adapter → 기존 Service/Repository
처음부터 JPA 모델을 모두 분리하지 않아도 된다. Legacy Adapter가 기존 구현을 감싸고, 안쪽은 새 계약에만 의존하게 한다. 이후 Adapter 내부를 단계적으로 교체한다.
4. 기능 단위 패키지와 모듈
1
2
3
4
5
6
7
8
9
10
order/
domain
application
adapter/in/web
adapter/out/persistence
payment/
domain
application
adapter/...
기능 모듈 사이 호출은 공개 Use Case/이벤트를 통하게 하고 다른 모듈의 Repository를 직접 참조하지 않는다. 하나의 배포 단위여도 내부 경계가 명확한 모듈러 모놀리스가 된다.
5. DB 경계는 가장 늦게 분리될 수 있다
코드 모듈을 나눴다고 즉시 데이터베이스도 분리할 필요는 없다. 우선 테이블 소유권과 쓰기 책임을 한 모듈로 정한다.
1
2
order 모듈만 orders 테이블 쓰기
다른 모듈은 order Query Port 또는 이벤트 사용
공유 DB 안에서도 소유권을 지킨 뒤 필요할 때 스키마·DB 분리를 검토한다.
6. 성공 지표
1
2
3
4
5
핵심 규칙 테스트 시간이 줄었는가
변경 시 수정 모듈 수가 줄었는가
외부 Adapter 교체가 Use Case에 영향을 덜 주는가
장애 범위와 배포 회귀가 줄었는가
새 개발자가 업무 흐름을 찾기 쉬운가
인터페이스 수와 패키지 수를 성공 지표로 삼지 않는다.
7. 시리즈 종합 체크리스트
- 의존성을 Domain과 Use Case 방향으로 역전했다. (Day 1)
- Use Case는 조율하고 Domain은 불변식을 지키게 했다. (Day 2)
- Web·DB·메시지·외부 API를 Adapter로 번역했다. (Day 3)
- Domain·Port·Adapter별 테스트와 아키텍처 규칙을 갖췄다. (Day 4)
- Legacy Adapter와 기능 모듈로 한 유스케이스씩 점진 전환했다. (Day 5)
시리즈 마무리
헥사고날 아키텍처는 육각형 그림이나 인터페이스의 개수가 아니다. 업무 규칙이 기술 선택보다 오래 살아남도록 의존성 방향을 관리하는 방법이다. 변화와 테스트 비용이 큰 경계부터 적용하고, 효과를 지표로 확인해야 한다. 구조는 목적이 아니라 더 안전하게 변경하기 위한 수단이다.