1. 에러 발생
- 의도
- 문제
- 문제 원인 파악
- 문제가 발생한 코드
2. @ConfigurationProperties란?
- Externalized Configuration (외부 구성)
- Externalized Configuration 사용 (바인딩)
- @ConfigurationProperties
3. 해결
- Bean 등록 방법 2가지
- @Configuration + @Bean
- @Component
4. 결론
1. 오류 발생
의도
- Profile에 따라서 다른 Bean이 동작하게 하고 싶었다.
- @Profile 기능을 이용해서 아래 코드처럼 eng Profile이 활성화되면 해당 클래스가 구현되게 하고 싶었다.
(profile에 따라서 서로 다른 클래스 구현)
문제
- @ConfigurationProperties("eng"), @Profile("eng"), @EnableConfigurationProperties(English.class)를 사용했는데 @Profile 에 따라서 Bean이 등록되는게 아니라, 모든 properties를 맵핑하는 클래스들이 다 실행되어서 Bean으로 등록됨.
- 즉, @EnableConfigurationProperties가 @ConfigurationProperties를 활성화 시키는데 @Profile을 고려해서 활성화 후 Bean에 등록시켜야 하는데 이를 무시하고, 모든 @ConfigurationProperties가 구현된 클래스를 활성화시켜서 Bean에 등록함.
문제 원인 파악
- @EnableConfigurationProperties로 @ConfigurationProperties 활성화 중, @Profile 어노테이션이 무시되고 있다.
- profile 에 따라서 Bean이 등록되어야 하는데, 모든 Bean을 다 등록시킨다.
문제가 발생한 코드
1. English.java
@Getter
@Setter
@Profile("eng")
@ConfigurationProperties("eng")
public class English implements OutputFormat{
String city;
String sector;
String unitPrice;
String billTotal;
}
2. resources/application-eng.properties
#csvParsing=true
#file.type=csv
#file.pricePath=price.csv
#file.accountPath=account.csv
jsonParsing=true
file.type=json
file.pricePath=price.json
file.accountPath=account.json
eng.city=city
eng.sector=sector
eng.unitPrice=unit price(won)
eng.billTotal=bill total(won)
3. @EnableConfigurationProperties로 @ConfigurationProperties 활성화
@EnableConfigurationProperties({CsvFileProperties.class, English.class, Korean.class})
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2. @ConfigurationProperties란?
(1) Externalized Configuration (외부 구성)
- Spring-boot는 같은 소스 코드로 여러 환경에서 동작할 수 있도록 외부화 설정을 제공한다
- java properties, YAML, 환경변수, 실행 인자로 설정 가능하다.
- 전체 프로젝트의 설정은 .properties, .yaml 중 하나만 사용하는 것을 권장하고, 동시에 존재하면 .properties가 우선한다.
(2) Externalized Configuration 사용 (바인딩)
- Spring Boot는 설정값을 바인딩 하기 위한 2가지 방법을 제공.
- @Value 바인딩
- @ConfigurationProperties 바인딩
(3) @ConfigurationProperties
- properties를 이 annotation으로 바인딩하여 사용.
- 이 annotation으로 설정된 클래스는 Dependency Injection(의존성 주입)로 참조하여 사용
(DI 3가지 방법: 생성자/필드/Setter 주입)
사용법
- @ConfigurationProperties 클래스 선언
- @ConfigurationProperties 활성화(2가지 방법)
- 활성화가 되면서 Bean 등록됨.
- @EnableConfigurationProperties("GreetingProperties.class")
(활성화가 필요한 클래스를 직접 등록) - @ConfigurationPropertiesScan
(@ComponentScan과 비슷하게 패키지 단위로 스캔 -> 1번 방법이 너무 길어지는 경우 사용)
- @EnableConfigurationProperties("GreetingProperties.class")
/* @ConfigurationProperties 클래스 선언 */
@Getter
@AllArgsConstructor // 외부에서 주입받는 거라서 DI 필요
@ConfigurationProperties("greeting")
public class GreetingProperties {
private String english;
private String korean;
}
/* @ConfigurationProperties 활성화 */
@SpringBootApplication
@EnableConfigurationProperties(GreetingProperties.class)
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3. 해결
정확한 이유는 모르겠지만 @EnableConfigurationProperties, @ConfigurationPropertiesScan ... 등을 사용해서 Bean 등록시 내부 로직에서 @Profile이 제대로 적용되지 않는 것 같다. 그런데 @Component나 @Configuration + @Bean을 활용하면 @Profile이 제대로 고려돼서 Bean이 등록된다.
Bean 등록 방법 2가지
- Bean 등록 방법
- @Configuration + @Bean
- @Component
(1) @Configuration + @Bean
- @ConfiguratonProperties를 선언 후
- Bean 등록 과정에서 @Profile으로 활성화할 Bean이 선택됨.
코드
1. English.java
@Getter
@Setter
@ConfigurationProperties("eng")
public class English implements OutputFormat{
String city;
String sector;
String unitPrice;
String billTotal;
}
2. FormatConfig.java
@Configuration
public class FormatConfig {
@Bean
@Profile("eng")
public OutputFormat englishFormat() {
return new English();
}
@Bean
@Profile("!eng")
public OutputFormat koreanFormat() {
return new Korean();
}
}
(2) @Component
- @Component로 해당 클래스 Bean 등록.
코드
4. 결론
@ConfigurationProperties를 사용하고, 이를 활성화 시키기 위해서 @EnableConfigurationProperties ... 등을 무조건 사용해야 하는 줄 알았는데 아니었다.
내부 로직을 자세히 알 수 없지만 @ConfigurationProperties와 @Profile을 같이 사용 후 활성화를 위해 @EnableConfigurationProperties, @ConfigurationPropertiesScan을 사용하면 @Profile이 제대로 고려되지 않는다. 하지만 스캔을 하지 않고, Bean에 등록시키면 @Profile이 고려된다.
ConfigurationProperties (Spring Boot 3.3.5 API)
Annotation for externalized configuration. Add this to a class definition or a @Bean method in a @Configuration class if you want to bind and validate some external Properties (e.g. from a .properties file). Binding is either performed by calling setters o
docs.spring.io
'Spring' 카테고리의 다른 글
[Spring Boot] 서비스 추상화 (Portable Service Abstraction) (1) | 2024.11.09 |
---|---|
[Spring Boot] 01. Spring Boot Core (3) - AOP(관점 지향 프로그래밍) * (0) | 2024.11.08 |
[Spring Boot] 01. Spring Boot Core (2) - 자동 구성과 외부 구성 (0) | 2024.11.06 |
[Spring Boot] Lombok (0) | 2024.11.06 |
[Spring Boot] 01. Spring Boot Core (1) - Spring Boot 소개, IoC, DI (3) | 2024.11.05 |