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만 의존
'Spring & Backend' 카테고리의 다른 글
| 단순히 ORM을 ‘사용’한 경험이 아니라, ORM ↔ 트랜잭션의 동작 흐름을 이해하고 이를 바탕으로 성능 튜닝과 장애 포인트 식별/대응 (1) | 2025.09.21 |
|---|---|
| RequestBody VS ModelAttribute의 차이점을 말해주세요. (0) | 2025.09.19 |
| 스택을 활용하여 브라우저의 뒤로가기/앞으로가기 기능을 구현하는 방법을 설명해주세요. (0) | 2025.09.18 |
| ControllerAdvice에 대해 설명해주세요 (0) | 2025.09.18 |
| 쿠키와 세션에 대해 설명해주세요. (1) | 2025.09.17 |
