멀티 모듈에서 @ConditionalOnProperty 사용 시 주의사항
개요
멀티 모듈에서 @ConditionalOnProperty 사용 시 주의해야할 사항에 대한 글입니다.
사건의 발단
여러 모듈에서 중복으로 관리되어지는 Configuration 이 있었다.
이것을 하나의 모듈에 모아 관리하려고 했다.
application.properties 옵션을 이용해 @Configuration 내부 빈 들을 주입할지 결정하도록 설계하려했다.
아래와 같이 옵션이 "true" 인 경우만 빈 들을 등록하는 방향으로 시도했다.
kafka.configuration.order=true
kafka.configuration.payment=true
kafka.configuration.stock=true
예시의 Configuration 이다.
@Configuration
@ConditionalOnProperty(value = "kafka.configuration.order", havingValue = "true")
public class OrderConsumerConfiguration {
@Value("${kafka.server.url}")
private String kafkaUrl;
....
}
그러나 각 서비스에서 빈을 찾지 못하는 에러가 발생하였다.
그래서 처음엔 다른 모듈의 빈을 불러오지 못하는 것일까? 라는 생각을 하였다.
해결1: scanBasePackage 경로 변경
예상은 완전 빗나갔다.
Bean Scan이 정상적으로 작동하고 있지 않았다.
Payment-Service로 예시를 살펴보자.
Gradle 에서는 kafka-object(공통 Configuration 관리하는 곳) 라는 모듈을 impot 하여 사용할 수 있도록 지정해놨다.
dependencies {
implementation project(':outbox-pattern:kafka-object')
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'mysql:mysql-connector-java:8.0.32'
implementation 'org.springframework.kafka:spring-kafka'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
그러나 두 서비스 간 Package level 이 다른 것을 볼 수 있다.(com.example vs com.example.paymentservice)
그래서 Payment-Service 는 com.example.paymentservice 를 기준으로 빈 스캔을 하고있었던 것이다.
Kafka-object Package | Payment Package |
![]() |
![]() |
이렇게 첫 번째 문제는 scanBasePackages 옵션을 이용하여 해결하였다.
@SpringBootApplication(scanBasePackages = "com.example")
해결2: scanBasePackage 경로 제약
두 번째 문제가 되는 Order-Service 서비스의 Gradle 이다.
아래 내용을 보면 위 payment-service 와는 다르게 stock-service, payment-service 가 포함되어져 있다.
dependencies {
implementation project(':outbox-pattern:kafka-object')
implementation project(':outbox-pattern:stock-service')
implementation project(':outbox-pattern:payment-service')
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'mysql:mysql-connector-java:8.0.32'
implementation 'org.springframework.kafka:spring-kafka'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
아래는 각 서비스의 scanBasePackage 이다.
kafka-object | order-service | payment-service | stock-service |
com.example | com.example.orderservice | com.example.paymentservice | com.example.stockservice |
처음엔 그럼 이전처럼 "com.example" 불러오면 모든 빈 들이 등록될 것 같은데? 라는 생각이 들었다.
그러나 payment-service, stock-service 에 이름이 같은 빈 들이 존재했고 오류가 발생했다.
또한 com.example 경로로 호출 시 필요없는 빈 까지 호출하므로 관리가 어렵게된다.
결국 scanBasePackages 를 제약할 수 밖에 없었다.
@SpringBootApplication(scanBasePackages = {
"com.example.consumer",
"com.example.producer",
"com.example.orderservice"
})
고찰
1. 멀티모듈을 설계하면서 package 명에 대해서 항상 신경쓰자.
2. 별도의 모듈에서 빈 들을 끌어와 사용하는 경우 어떤 빈 들이 주입되는지 확인하자.