Back-end

멀티 모듈에서 @ConditionalOnProperty 사용 시 주의사항

HOONY_612 2024. 3. 7. 10:35
반응형

 

개요

멀티 모듈에서 @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. 별도의 모듈에서 빈 들을 끌어와 사용하는 경우 어떤 빈 들이 주입되는지 확인하자.

반응형