fix: 하네스 제거 및 keycloak 문서 보강
This commit is contained in:
@@ -1 +0,0 @@
|
||||
../../vault/10-projects/errors/spring-componentcan-multimodule-overlap-collision-2026-06-23.md
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: "Spring Boot multi-module component scan overlap causes BeanDefinitionOverrideException"
|
||||
source_type: error-note
|
||||
status: raw
|
||||
tags: [spring-boot, component-scan, multimodule, bean-definition-override, Clean-Architecture, test-context]
|
||||
created: 2026-06-23
|
||||
---
|
||||
|
||||
# Multi-module component scan overlap causes BeanDefinitionOverrideException
|
||||
|
||||
## Parent
|
||||
|
||||
[[raw/branch-notes/feature-build-release-supply-chain-contract]]
|
||||
|
||||
## 현상
|
||||
|
||||
Spring Boot 애플리케이션 시작 또는 테스트 기동 시 다음 예외가 발생하며 컨텍스트 초기화가 실패한다:
|
||||
|
||||
```
|
||||
org.springframework.beans.factory.support.BeanDefinitionOverrideException:
|
||||
Invalid bean definition with name 'domainContextPropagator'
|
||||
defined in class path resource [dev/caskeleton/sample/portfolio/bootstrap/context/SampleDomainContextConfig.class]:
|
||||
Cannot register bean definition [...] for bean 'domainContextPropagator'
|
||||
since there is already [...] bound.
|
||||
```
|
||||
|
||||
## 원인
|
||||
|
||||
1. **상위 패키지 스캔의 한계**: 프로덕션 모듈의 실행 진입점인 `CaSkeletonApplication`은 `@SpringBootApplication(scanBasePackages = "dev.caskeleton")`을 선언하여 `dev.caskeleton` 하위의 모든 컴포넌트를 스캔하고 있었다.
|
||||
2. **테스트 스코프 모듈의 노출**: `sample-portfolio` 모듈은 테스트 시에만 로드되는 테스트 스코프 의존성이었으나, 테스트 런타임 클래스패스에 올라오면서 `dev.caskeleton.sample.portfolio` 패키지도 최상위 패키지인 `dev.caskeleton`에 포함되게 되었다.
|
||||
3. **빈 정의 충돌**: 이로 인해 `app-bootstrap` 내부의 `DomainContextConfig`와 `sample-portfolio` 내부의 `SampleDomainContextConfig`가 둘 다 스캔 범위 내에 들어가게 되었고, 동일한 이름인 `domainContextPropagator`라는 빈을 이중 등록하려고 시도하면서 `BeanDefinitionOverrideException`이 발생했다.
|
||||
|
||||
### 추가적인 시도와 부작용 (Separate @ComponentScan)
|
||||
|
||||
이를 피하기 위해 `CaSkeletonApplication.java`에 별도의 `@ComponentScan`과 `excludeFilters`를 적용했다:
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@ComponentScan(
|
||||
basePackages = "dev.caskeleton",
|
||||
excludeFilters = {
|
||||
@ComponentScan.Filter(
|
||||
type = FilterType.REGEX,
|
||||
pattern = "dev\\.caskeleton\\.sample\\.portfolio\\..*")
|
||||
})
|
||||
```
|
||||
|
||||
하지만 이 방식을 도입하자, Spring Boot의 기본 컴포넌트 스캔 자동 설정이 완전히 오버라이드(override)되어 무력화되었다. 그 결과 Spring Boot가 테스트 클래스 패키지에 포함된 내부 static `@Configuration`들을 필터링하기 위해 사용하던 기본 필터들(`TypeExcludeFilter`, `AutoConfigurationExcludeFilter`)이 동작하지 않아, 다른 테스트 클래스들의 nested `@Configuration` 빈 정의가 마구잡이로 스캔되어 또 다른 `BeanDefinitionOverrideException` 연쇄 충돌을 일으켰다.
|
||||
|
||||
## 해결
|
||||
|
||||
가장 깔끔하고 부작용이 없는 해결책은 별도의 `@ComponentScan` 선언을 배제하고, `@SpringBootApplication` 및 `@ConfigurationPropertiesScan`의 `scanBasePackages`/`basePackages` 속성에 프로덕션에서 스캔해야 할 패키지 목록을 구체적인 문자열 배열로 직접 명시하는 것이다.
|
||||
|
||||
```java
|
||||
@SpringBootApplication(
|
||||
scanBasePackages = {
|
||||
"dev.caskeleton.bootstrap",
|
||||
"dev.caskeleton.adapter",
|
||||
"dev.caskeleton.application",
|
||||
"dev.caskeleton.domain",
|
||||
"dev.caskeleton.shared"
|
||||
})
|
||||
@ConfigurationPropertiesScan(
|
||||
basePackages = {
|
||||
"dev.caskeleton.bootstrap",
|
||||
"dev.caskeleton.adapter",
|
||||
"dev.caskeleton.application",
|
||||
"dev.caskeleton.domain",
|
||||
"dev.caskeleton.shared"
|
||||
})
|
||||
public class CaSkeletonApplication {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
이 방식을 통해:
|
||||
1. `dev.caskeleton.sample.portfolio` 패키지를 스캔 대상에서 원천적으로 제외하여 빈 충돌을 차단한다.
|
||||
2. Spring Boot가 제공하는 기본 `@ComponentScan` 필터들이 올바르게 보존 및 동작하여, 다른 테스트 내 nested `@Configuration`들이 오버스캔되지 않는다.
|
||||
3. 아키텍처적으로 모듈 경계가 명확하게 보호된다.
|
||||
|
||||
## 정리 (Lessons)
|
||||
|
||||
1. **Clean Architecture 또는 멀티모듈 구조에서 최상위 공통 패키지(`dev.caskeleton`) 기준의 광범위 스캔은 타 모듈(예: 테스트 전용 샘플 모듈) 클래스패스 유입 시 원치 않는 빈 정의 충돌을 야기하기 쉽다.**
|
||||
2. **`@SpringBootApplication`에 별도의 `@ComponentScan` 어노테이션을 덮어씌우면 Spring Boot 내부의 중요한 컴포넌트 스캔 제외 필터들이 무력화되므로 지양해야 한다.**
|
||||
3. **명시적으로 허용할 프로덕션 패키지 목록을 나열하여 스캔 대상을 좁히는 기법이 가장 안전하고 명확하다.**
|
||||
|
||||
## 재현 환경
|
||||
|
||||
- Spring Boot 3.4.x, Java 21, Gradle 9.0
|
||||
- `app-bootstrap` 구동 및 `:app-bootstrap:test` 실행 시 발생
|
||||
- 해결 후: 전체 테스트 통과 (BUILD SUCCESSFUL)
|
||||
|
||||
## Evidence
|
||||
|
||||
- `actually-implemented`: `src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/CaSkeletonApplication.java` 수정 적용.
|
||||
- `locally-verified`: `cd src && ./gradlew test` 성공.
|
||||
Reference in New Issue
Block a user