Property Binding (Normal)
스프링에서 빌드 환경에 따라 변경되는 상수값을 사용하기 위해 보편적으로 application.yml (또는 .property)에 문자열을 작성하고 소스코드에서 @Value 또는 @ConfigurationProperties 애노테이션을 사용해 변수에 바인딩 시킨다.
나는 주로 아웃바운드 API 주소를 개발환경과 운영환경에 달리 설정해주기 위해 사용한다.
YML 설정파일
### application-dev.yml
api:
calendar: http://alpha-api.calendar.example.com:5001/connect/schedules
map:
url: https://map-dev.example.com/api/v1/auth/room
api-key: x-map-api-key
api-value: ba2b5e62-6665-47c2-a9e9-00b04167da84
### application-real.yml
api:
calendar: http://api.calendar.example.com:5001/connect/schedules
map:
url: https://map.example.com/api/v1/auth/room
api-key: x-map-api-key
api-value: ce2e4b88-8086-47c2-0812-10e48391fa11
소스코드 (Kotlin)
@Component
@ConfigurationProperties(prefix="api")
data class ApiUrlProperty (
var calendar: String = "",
var map: Map<String, String> = mapof()
) {
val mapApiUrl by lazy { map["url"] }
val mapApiKey by lazy { map["api-key"] }
val mapApiValue by lazy { map["api-value"] }
}
Mutable Property Binding
하지만 위와 같은 기존의 방식은 치명적인 단점이 있다.
스플링 부트가 실행되면서 변수에 상수값을 바인딩할지라도, var 지시자를 사용해야 하기 때문에 calendar에 바인딩 된 문자열은 언제든지 바뀔 수 있다는, 즉, Mutable 하다는 것이다.
이는 Java로 구현하더라도 마찬가지이다.
왜냐하면 프로퍼티를 바인딩하는 시점이 ApiUrlProperty라는 클래스가 초기화할 때가 아닌, Setter 함수를 기반으로 바인딩하기 때문이다.
* Setter 함수를 사용한다는 것 자체가, 다른 객체에서 ApiUrlProperty 클래스 객체의 setter 호출이 가능하다는 의미
Immutable Property Binding
스프링 부트 2.2 이상에서는 @ConstructorBinding 애노테이션을 사용함으로써 위의 단점을 보완했다.
@ConstructorBinding 애노테이션은 스프링에게 setter 대신, 생성자를 사용해서 프로퍼티를 바인딩하라고 지시한다.
그렇게 되면 바인딩 되는 변수는 더 이상 var 지시자일 필요가 없다.
val 지시자로 명시하면 된다. (자바라면 setter 함수를 생성하지 않아도 된다.)
소스코드 (Kotlin)
// @Component 안 지우면 에러남
@ConfigurationProperties(prefix="api")
@ConstructorBinding
data class ApiUrlProperty (
val calendar: String = "",
val map: Map<String, String> = mapof()
) {
val mapApiUrl by lazy { map["url"] }
val mapApiKey by lazy { map["api-key"] }
val mapApiValue by lazy { map["api-value"] }
}
@ConstructorBinding 을 사용하려면 @EnableConfigurationProperties 또는 @EnableConfigurationPropertiesScan 애노테이션을 반드시 작성해주어야 한다.
그리고 다른 enabler인 @Bean, @Component, @Import 와 함께 사용할 수 없다.
@SpringBootApplication
@ComponentScan("com.example.jolly-sally")
@EnableConfigurationProperties(ApiUrlProperty::class)
class SampleApplication()
fun main(args: Array<String>) {
runApplication<SampleApplication>(*args)
}
출처
springframework.guru/immutable-property-binding/
github.com/spring-projects/spring-boot/issues/18674
cheese10yun.github.io/immutable-properties/
'생계유지형 개발자 > Spring Framework' 카테고리의 다른 글
[Spring/Mybatis] Select 결과를 결과타입 내 다른 객체에 매핑 (0) | 2021.06.21 |
---|---|
[Gradle] war 실행 시 Exploded war 생성하기 (Groovy, Kotlin DSL) (0) | 2021.04.21 |
[Spring5] Thylemeaf Layout Dialect 적용하기 (0) | 2021.03.24 |
[Spring5] Thymeleaf + Vue CDN 혼용하기 (0) | 2021.03.24 |
[Spring5] Spring MVC vs WebFlux (0) | 2021.03.23 |