Spring boot 2.4-m2 버전이 저번달(8/14)에 릴리즈 되었다. 그러기엔 이글이 좀 늦은 듯 하다. 한달이나 지나서야..원..
Spring Boot 의 m2 버전에서 여러 추가 기능이 있지만 제일 큰 변화는 아마도 Config file processing 처리하는 방법이 아닐까 싶다.
나중에 정식 릴리즈가 되면 다른 특징들도 알아보고 오늘은 Config file processing 관한 변화의 특징들을 알아보도록 하자.

spring boot 2.3 까지 Kubernetes 지원을 열심히 하고 있다. 하지만 그 중에 할 수 없었던 부분이 volume mounted configuration라는 기능이다. (사실 필자도 잘 모름) Kubernetes 에서 인기있는 기능이라고 하니.. 하지만 이 기능을 지원하기 위해서는 ConfigFileApplicationListener 클래스를 수정해야만 했다고 한다.

개발을 하다보면 때때로 변경하기 어려운 코드들이 존재한다. 사실 이건 어느 누구에게나 닥칠 수 있는 상황이라고 생각한다. 그 중 ConfigFileApplicationListener는 변경하기 어려운 코드중 하나라고 판단 되었다고 한다. 사실 ConfigFileApplicationListener 클래스는 코드를 잘못 작성하거나 혹은 테스트 코드 누락이 된 것이 아니라 기능을 추가 하면서 그 클래스가 할 수 있는 일을 다했다고 판단 되었다.

ConfigFileApplicationListener 문제

spring.profiles: local
foo.username: userb
foo.password: userb
---
spring.profiles: '!dev'
spring.profiles.include: local
foo.username: userc

위와 같이 설정후에 --spring.profiles.active=prod 로 실행하는 경우 어떤 값들이 나와야 할까? 조금 애매한 부분이 많다. prod으로 프로파일을 설정했으니 !dev 프로파일이 동작할텐데 foo.username 과 foo.password 값은 어떤 값이 될까? include가 local 로 되어 있으니 local 프로파일을 오버라이딩 할까? 그럼 foo.password 의 값은 뭐가 될까? 사실 돌려보면 다음과 같다.

userc
null

근데 이게 맞는 걸까? 필자도 잘 모르겠다. 사실 이건 프로파일을처리 할 때 활성화 되지 않았다. (prod 와 정확한 매칭되는게 없어) 그래서 local 이라는 프로파일을 포함시키지 못한다. 이러한 처리는 자주 논란이 되었다고 한다. 문제가 제기 되면 그 문제를 해결 할때마다 다른 문제들이 생기곤 한다. 그래서 Spring boot 쪽에선 이와 같은 문제를 해결하고자 다시 재정의 했다.

  • 문서의 정렬
  • 프로파일에 더이상 추가 프로파일 작성 불가

Spring boot 에선 위와 같이 새롭게 정의 하였다.

문서 정렬

문서 정렬은 아주 간단하다. 하위 문서가 상위 문서를 오버라이딩한다.

spring.profiles: test
foo.username: wonwoo
foo.password: wonwoo123
---
spring.profiles: dev
foo.username: fidel
foo.password: fidel123
---
foo.username: overridden-wonwoo

위와 같이 문서가 작성 되고 --spring.profiles.active=test로 실행을 한다면 어떤 결과가 나올까? spring2.3 이전버전과 spring 2.4 버전에선 다른 결과가 출력 된다.

2.3

wonwoo
wonwoo123

2.4

overridden-wonwoo
wonwoo123

위와 같이 하위 문서가 상위 문서를 재정의 하도록 변경 되었다. 추후에 2.4로 마이그레이션 할 때 주의할 점이다.

멀티 프로퍼티

yaml을 파일과 비슷한 기능이다. yaml 파일은 ---구분자를 통해 한 파일에 여러 프로파일을 작성 할 수 있었다. 이제는 properties 파일도 가능하게 지원한다. (필자는 프로퍼티파일을 더 선호에서 좋은 기능같다.)

spring.profiles=test
foo.username=wonwoo
foo.password=wonwoo123
#---
spring.profiles=dev
foo.username=fidel
foo.password=fidel123

프로퍼티파일은 #--- 구분자를 통해 구분할 수 있다. 이제는 여러 파일로 나누지 않고도 한 파일에 환경별로 분리 할 수 있어 좋은 것 같다. 하지만 아직 IDEA 에선 빨간줄이..

spring.config.activate.on-profile

spring.profiles에서 spring.config.activate.on-profile로 변경 되었다.
아직은 spring.profiles 을 사용할 수는 있지만 deprecated 되어 있으니 참고하면 되겠다.

spring.config.activate.on-profile=test
foo.username=wonwoo
foo.password=wonwoo123
#---
spring.config.activate.on-profile=dev
foo.username=fidel
foo.password=fidel123

위와 같이 작성하면 기존의 spring.profiles 과 동일한 효과를 얻을 수 있다.

하지만 여기서 조금 주의할 점이 있다.

만약 spring.config.activate.on-profile 설정과 spring.profiles 설정을 함께 사용하면 안된다.

spring.config.activate.on-profile=test
spring.profiles=dev
foo.username=wonwoo
foo.password=wonwoo123

위와 같이 사용할 경우에는 에러가 발생한다. 하지만 아래와 같은 경우는 상관없다.

spring.config.activate.on-profile=test
foo.username=wonwoo
foo.password=wonwoo123
#---
spring.profiles=dev
foo.username=fidel
foo.password=fidel123

각 환경별로는 다르게 사용해도 에러가 나지 않는다. 하지만 일관성있게 사용하는 것이 좋아보인다.

프로파일에 더이상 추가 프로파일 작성 불가

이제는 정의된 프로파일에 더 이상 추가 프로파일을 작성 할 수 없다. 다음 설정 파일을 보자.

spring.config.activate.on-profile=test
spring.profiles.include=dev
foo.username=wonwoo
foo.password=wonwoo123
#---
spring.config.activate.on-profile=dev
foo.username=fidel
foo.password=fidel123

이전에는 위와 같이 특정 프로파일에 추가적으로 프로파일을 포함시킬 수 있었다. 하지만 이제는 더이상 추가적으로 프로파일을 포함 할 수 없다. 위와 같이 작성한다면 에러가 발생한다.

spring.profiles 을 사용해도 마찬가지다.
좀 더 빠르게 이해가 되며 쉽게 관리 할 수 있다는 장점이 있을 것 같다. 허나 여러 종류의 프로파일들을 함께 사용하고 싶을 때는 어떻게 할까?
Spring boot 의 새기능인 Profiles Groups 기능을 사용하면 된다.

Profiles Groups

이제는 Profiles을 group 을 지정할 수 있다. 여러 프로파일들을 한꺼번에 그룹지어 하나의 프로파일로 만들수 있다.

spring.config.activate.on-profile=test
foo.username=wonwoo
foo.password=wonwoo123
#---
spring.config.activate.on-profile=dev
foo.username=fidel
foo.password=fidel123
#---
spring.profiles.group.testdev=test,dev

위와 같이 test, dev 을 그룹지어 하나의 프로파일을 만들 수 있다. include 기능보다 좀 더 쉽게 추론할 수 있을 듯 싶다.
또 한 @Configuration 사용해 @Profile 설정을 할 떄 유용하다.

@Profile("devDb")
@Configuration
class DevDataBase {

}

@Profile("devMessage")
@Configuration
class DevMessage {

}

spring.profiles.group.dev=devDb,devMessage

이와 같이 dev 프로파일에 message 설정과 db 설정을 한꺼번에 넣을 수 있다. 테스트할 때도 좋은 기능같고 다른 설정으로 배포할 경우에도 좋은 기능 같다.

Import Properties

이전 Spring boot는 application.properties, application.yml 이외에 추가적인 설정파일을 가져오는데 spring.config.additional-location 사용하여 가져올 수 있었으나 application.properties 로 작성시에는 가져오지 못하고 환경변수나 인자로 넘거야 했었다. 또 한 파일 유형이 너무 제한적이다.

어쩄든 이 불편함을 수정하기 위해 새로운 설정으로 추가적인 파일들을 추가할 수 있다.

spring.config.import=classpath:/test/test.properties
spring.config.import=file:/test/test.properties
spring.config.import=configtree:/test/test.properties

위와 같이 spring.config.import를 이용해서 추가적인 파일들을 가져올 수 있다. 클래스패스, 파일, 혹은 configtree(Kubernetes 에서 사용하는 듯) 를 prefix를 사용해 다양한 유형들을 설정할 수 있다.

이 뿐만이 아니라 다른 파일 유형들도 적용할 수 있다. 예를 들어 추후에는 archaius, vault, zookeeper 와 같이 외부 설정을 불러 올 수 있도록 확장가능하다.

archaius://
vault://
zookeeper://

대략 위와 같은 모양이 되겠다.

만약 import할 대상의 파일이 없을 경우 에러가 발생한다. 그래서 파일이 없더라도 에러가 발생하지 않고 무시할 수 있는 기능이 있다. 그러기 위해서는 다음과 같이 optional: 을 prefix로 지정하면 된다.

spring.config.import=optional:classpath:/test/test.properties
spring.config.import=optional:file:/test/test.properties
spring.config.import=optional:configtree:/test/test.properties

위와 같이 optional: 사용하면 파일이 없더라도 에러가 나지 않는다. 뿐만아니라 기존에 사용하고 있던 spring.config.additional-location 설정도 동일하게 optional을 사용할 수 있다.

만약 모든 항목에 optional 처럼 기능을 작동하고 싶다면 spring.config.on-location-not-found=ignore, 혹은 SpringApplication.setDefaultProperties(…​) 메서드를 사용해서 속성을 넣을 수 있다.

spring.config.import=classpath:/test/test.properties

ignore 설정 후 위와 같이 optional:를 제거해도 파일이 없다는 에러는 발생하지 않는다.

레거시 사용

기존의 레거시(ConfigFileApplicationListener)를 사용해서 기존과 동일한 프로파일 설정들을 사용할 수 있다. 위의 내용이 아직 불편하다면 굳이 사용할 필요 없이 기존 설정과 동일하게 사용할 수 있다.

spring.config.use-legacy-processing=true

spring.config.use-legacy-processing 를 사용하면 기존 프로파일 설정(ConfigFileApplicationListener) 그대로 사용할 수 있다. 하지만 천천히 조금은 익숙해져야 되지 않을까 싶다.

오늘은 이렇게 spring boot 2.4의 새로운 config file processing 대해서 알아봤다. 만약에 추후에 2.4로 마이그레이션 할 때 조금 눈여겨 봐야할 특징들이다. 아직은 레거시도 지원하기에 급하지 않겠지만 Spring boot 특성상 바로 다음 메이저 업그레이드할 때 삭제 될 것 같다. Spring boot 는 대부분 deprecated 시킨 버전 바로 다음 버전에 대부분 삭제했다. 아마도 2.5 에는 삭제 되지 않을까 생각된다.