[UMC 3기] Vibecap

Before

dev 브랜치에서는 개발을 진행하고 prod 브랜치에 merge하여 빌드한 뒤 배포했다.
test할 때에는 직접 application.properties 파일을 수정하였다.
서버를 업데이트 할 때마다 merge를 해야 하고 그 과정에서 conflict가 발생하면 일일히 해결해야 했다.

빌드 할 때 명령행에서 인자값만으로 이 세 가지 환경을 전환하고 싶었다.

Solution

목적(개발, 배포)에 따라 다른 프로필(설정값 모음)을 만든 뒤 빌드할 때 특정 프로필 적용

이를 가능하게 하기 위해 spring boot(2.4 이후 버전에 해당)에서 config file이 어떻게 처리되는지 알아보기로 했다.

Config file processing in Spring Boot 2.4

스프링 부트 2.3에 volumn mounted configuration에 대한 기능을 추가하고 싶었지만 그러지 못했다. Volume mounted configuration이란 Kubernetes의 기능이다. 스프링 부트 2.4에는 application.propertiesapplication.yml을 최대한 활용하는 Kubernetes에 대한 기능이 추가되었다. 그러기 위해서는 ConfigFileApplicationListener 클래스를 수정해야 했다.

ConfigMap

Kubernetes의 ConfigMap directive는 파일 시스템에 대한 설정(configuration)을 프로그래밍 언어에게 노출시키는 역할을 한다.
yaml 파일을 마운트할 수도 있고 파일 이름이 key이고 파일의 내용이 value인 directory tree format을 사용할 수도 있다.

The problem with ConfigFileApplicationListener

ConfigFileApplicationListener 클래스가 가지고 있던 기존의 문제점 두 가지는 profile을 정의한 문서에 대한 내용이였다.(주로 YAML에서):

  • 하나의 profile specific document에서 추가적인 프로필들을 작성할 수 있다.
  • document들이 어떤 순서로 추가될지 알 수 없다.

YAML structures

YAML은 사람이 쉽게 읽을 수 있는 data serialization language이다.
주로 configuration file을 작성할 때 사용된다.
YAML 파일은 인코딩 방식을 지정할 필요가 없다.

  • hash(“#”) : 주석
  • three dashes(“—”) : 하나의 document가 시작됨을 의미
  • three dots(“…”) : 하나의 document가 완전히 끝났음을 의미

아래의 코드는 세 개의 논리적 document로 이루어진 하나의 multi-document YAML 파일이다.

security.user.password: usera
---
spring.profiles: local
security.user.password: userb
runlocal: true
---
spring.profiles: !dev
spring.profiles.include: local
security.user.password: userc

Spring Boot에서는 두 가지 문제를 해결하기 위해 properties 파일과 yaml 파일이 load되는 방식을 변화시켰다.

  1. Document들은 그들이 정의된 순서대로 load된다.
  2. 프로필들은 profile specific document에서 활성화(acitvated)될 수 없다.

Document Order

Stpring Boot 2.4부터 properties 파일과 yaml파일이 로드될 때 파일의 아래쪽에 있는 내용들의 위쪽의 내용을 덮어쓴다. 각 행의 내용을 Map의 entry처럼 취급한다. 동일한 key 값을 가진 새로운 value가 추가되면 기존의 value는 사라진다.

test: "value"
---
test: "overriden-value"

Multi-Document Properties Files

Spring Boot 2.4부터 Java Properties 파일도 YAML같은 multi-document 기능을 사용할 수 있게 되었다. #---를 사용하여 논리적 document를 분리한다.

test=value
#---
test=overriden-value

Profile Specific Documents

Spring Boot 2.4부터는 spring.config.activate.on-profile을 사용하여 프로필의 이름을 지정한다.

test=value
#---
spring.config.acitvate.on-profile=dev
test=overriden-value

testdev 프로필이 활성화되었을 때에만 덮어써진다.

Profile Activation

spring.profiles.activate 속성을 통해 application.properties 또는 application.yaml 파일에 정의된 프로필을 활성화 한다.

test=value
spring.profiles.active=local
#---
spring.config.activate.on-profile=dev
test=overriden-value

test 속성은 덮어써지지 않는다.

단, 여러 가지 프로필을 조합할 수는 없다. 아래의 코드는 exception을 발생시킨다.

test=value
spring.config.activate.on-profile=dev
spring.profiles.active=local # will fail
test=overriden-value

Profile Groups

Spring Boot 2.4에 추가된 새로운 기능이다. 하나의 프로필을 여러 개의 sub-profile들로 확장할 수 있는 기능이다. 예를 들어 조건에 따라 @Profile 어노테이션을 가지는 여러 개의 복잡한 @Configuration 클래스들을 가지고 있다고 생각해보자. Database에 대한 설정 @Profile("proddb"), 메세지에 관한 설정 @Profile("prodmq") 등을 가질 것이다.
여러 개의 분리된 프로필들을 사용하는 것은 코드를 이해하기에 편리하지만 배치(deployment)에 대해서는 이상적이지 않다.배포 환경에서 프로그래머가 proddb, prodmq, prodmetrics등의 클래스를 한 번에 등록해야 한다는 사실을 기억하는 것은 효율적이지 못하다. 대신, profile group을 만들면 prod 프로필 하나를 활성화 하는 것으로 하위의 모든 설정을 활성화 할 수 있다.

spring.profiles.group 속성을 사용하여 해당 프로필이 포함할 하위 프로필을 지정할 수 있다.

spring.profiles.group.prod=proddb, prodmq, prodmetrics

Importing Additional Configuration

Spring Boot 2.4의 핵심 기능이다. 이전 버전에서는 application.properties, application.yaml 파일 이외의 properties 파일이나 yaml 파일의 내용을 import하기 힘들었다. spring.config.import 속성을 사용하면 쉽게 import할 수 있다.
spring.config.activate.on-profile 속성과도 함께 사용할 수 있다.

spring.config.activate.on-profile=prod
spring.config.import=prod.properties

C언어의 #inlucde와 비슷하다.

After

application-<profile name>.properties 파일 작성

  • application-local : 개발환경
  • application-prod : 배포 환경
  • application-test : 테스트 환경
spring.config.activate.on-profile=dev

# server.port=8080

# server name(id) : jwt의 aud 클레임에 들어갈 값
springboot.jwt.audience=vibecapJavaServer

# swagger
spring.mvc.pathmatch.matching-strategy=ant_path_matcher

# mysql
spring.datasource.url=jdbc:mysql://localhost:3306/vibecap_prototype?serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=<password>
spring.jpa.open-in-view=false

# jpa
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

# Encoding
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true

# multipart
spring.servlet.multipart.maxFileSize=7MB
spring.servlet.multipart.maxRequestSize=7MB

# logging
logging.config=classpath:logback-dev.xml

이렇게 설정해준 뒤

  1. application.properties 파일에 다음과 같은 코드를 작성하고 빌드하거나
     spring.profiles.active=dev
    
  2. 터미널에서 인자를 전달하여 빌드할 수 있다.
     $ gradle build --args='--spring.profiles.active=dev' -x test
    

references

Comments