1. 경계가 흐릿해짐

모듈 간 순환 의존, 공용 util 남용, 도메인 객체를 그대로 넘기며 레이어 침식.

Shared DB 테이블을 서로 직접 건드리며 트랜잭션 경계가 꼬임.


2. 의존성/빌드 지옥



transitive dependency 충돌, BOM 부재로 버전 드리프트.

멀티모듈 빌드 캐시/평행 빌드 미설정으로 빌드 시간 폭증.

리소스/Bean 이름 충돌(동일 이름의 application.yml, message, Bean name 등).


3. Spring 특유의 함정



광범위한 @ComponentScan 으로 모듈 경계 무시, “우연한” 빈 주입.

@Configuration 들이 서로 @Import 하다 모듈 순환.

테스트에서 @SpringBootTest 한 방으로 모두 로딩 → 테스트 느려지고 경계 검증 불가.


4. 릴리스/테스트 어려움



모듈 API 계약이 없어 내부 구현 변경이 연쇄 파손.

통합 테스트만 있고 계약(Contract)·모듈 단위 테스트 부재.


5. 운영/성능 이슈



스타트업 시간 증가(클래스패스 커짐, 자동설정 난립).

모듈별 로깅/모니터링 단위가 없어서 장애 지점 추적 어려움.


6. 조직/프로세스



코드 소유권 불명확, 리뷰 경계가 모호 → 경계 침식 가속.


개선 전략 (실무 체크리스트)

A. 모듈 경계/의존성 규율

Bounded Context 기준으로 모듈 나누기(응용/도메인/인프라 분리: hexagonal/clean).

방향성: api → app → infra (상위는 하위 구현 모름). 순환 금지.

“공유 커널(shared)” 최소화, 도메인 간 통신은 DTO or Domain Event로.

도구로 강제: ArchUnit(순환 금지, 패키지/레이어 룰), Maven Enforcer/Dependency Plugin, Gradle constraints/versions catalog.


B. 공개 API vs 내부 구현 분리

각 도메인 모듈에 :order-api(interface/DTO)와 :order-impl(service/repo) 분리.

다른 모듈은 -api만 의존. 구현 바뀌어도 계약은 안정.

공용 유틸은 “internal” 네이밍/패키지로 노출 억제(문서화 금지, 사용 금지 룰).


C. Spring 구성 가이드

@ComponentScan 최소화: 명시적 구성만 사용(특정 패키지 한정).

auto-config가 필요하면 모듈별 starter 패턴 사용

Spring Boot 3: META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 활용.

불필요 자동설정 제외(spring.autoconfigure.exclude).


@Configuration(proxyBeanMethods = false), Bean 이름 충돌 방지(접두사).

트랜잭션 경계는 app 계층에서만 시작하도록 규칙화.


D. 데이터/트랜잭션

동일 DB라도 모듈별 리포지토리 소유권을 명확히(타 모듈 테이블 직접 접근 금지).

교차 변경은 **도메인 이벤트(동기/비동기 in-process)**로 전달, SAGA가 필요한 흐름은 모듈 경계에서 orchestration.


E. 테스트 전략

모듈 단위 슬라이스 테스트: @DataJpaTest, @JsonTest, @WebMvcTest.

모듈 간 계약은 CDC(Consumer-Driven Contract) 혹은 API 계약 테스트로 고정.

통합 테스트는 “행복 경로”와 핵심 시나리오만. 나머지는 모듈 단위로 커버.

Gradle testFixtures(공유 테스트 유틸)로 중복 축소.


F. 빌드/버전/CI

BOM(java-platform) 또는 Maven BOM으로 버전 고정, drift 방지.

Gradle: 병렬/캐시/Configuration Cache, 빌드 스캔 켜기.

변경 영향도 기반 CI(터치된 모듈만 테스트/빌드).

의존성 잠금(dependency locking)과 취약점 스캔(OWASP/Gradle VULN).


G. 운영/관측성

모듈/도메인별 로거 이름·MDC 키 규약, 요청 식별자 전파.

모듈 경계 이벤트 로깅, 메트릭(Tag=module). 시작시간/빈 수 모듈별 측정.


H. 점진적 전환(안전한 마이그레이션)

Strangler 패턴: 먼저 “경계(anti-corruption layer)”를 세우고, 도메인별로 떼어 -api/-impl로 분리.

가장 독립적인 서브도메인부터 모듈화 → 통신을 이벤트/DTO로 치환 → 순환 제거 → 나머지 확장.


예시 레이아웃(Gradle)

:platform-bom        // 버전 관리(BOM, java-platform)
:common-internal     // 내부 공용(최소화, 외부 의존 금지)

:customer-api
:customer-impl       // depends on :customer-api, :common-internal

:order-api
:order-impl          // depends on :order-api, :customer-api

:payment-api
:payment-impl        // depends on :payment-api

:app                 // 조립 모듈 (Spring Boot main) - 각 *-api만 의존

LIST

+ Recent posts