외부 파일(.properties, .yaml)로 설정하기
위 포스팅에서 OS 환경 변수, 자바 시스템 속성, 커맨드 라인 옵션 인수를 사용해 설정을 불러오는 방법을 알아보았습니다. 하지만 사용해야 하는 값이 늘어날수록 사용하기가 불편해지는 단점이 있습니다. 따라서 이번 포스팅은 설정값을 파일에 넣어서 관리하는 방식, 즉,. properties라는 파일을 사용해 설정값을 관리하고 사용하는 방식에 대해 작성하였습니다.
.properties 외부 파일
key=value 형식을 사용해 설정값을 관리합니다. Spring Boot에서는 개발자가 application.properties 파일을 자바가 실행되는 위치에 만들어 두면, Spring이 해당 파일을 읽어서 사용할 수 있는 PropertySource 구현체를 제공합니다. 이때, Spring은 application.properties 파일을 설정 데이터 (Config data)라고 합니다. PropertySource 관련해서는 위 포스팅에서 작성하였으니, 참고하시면 좋을 것 같습니다. PropertySource 구현체를 제공하므로, 당연히 설정 데이터를 Environment를 통해 조회할 수 있습니다.
아래 코드를 통해 확인해보겠습니다.
# application.properties
url=default.db.com
username=default_user
password=default_pw
@Component
public class EnvironmentProperty {
private final Environment env;
public EnvironmentProperty(Environment env) {
this.env = env;
}
@PostConstruct
public void init() {
String url = env.getProperty("url");
String username = env.getProperty("username");
String password = env.getProperty("password");
System.out.printf("env url=%s\n", url);
System.out.printf("env username=%s\n", username);
System.out.printf("env password=%s\n", password);
}
}
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
내부 파일 분리
application.properties 파일을 각 서버마다 만들어서 애플리케이션 로딩 시점에 해당 파일을 읽어 설정값을 사용할 수도 있습니다. 하지만, 설정을 변경해야 할 때마다 각 서버에 들어가서 각각의 설정값들을 수정하고 관리해야 하는 것은 매우 번거롭습니다. 이를 해결하기 위해서는 각 용도에 맞는 설정 파일들을 프로젝트 내부에 포함하고, 각 파일들을 각각의 용도에 사용할 수 있도록 내부에서 분리함으로써, 빌드 시점에 각 환경마다 설정 파일을 모두 포함하여 쉽게 배포할 수 있습니다.
Spring은 이렇게 사용할 수 있도록 Profile(프로필)이라는 것을 제공합니다. spring.profiles.active 외부 설정에 값을 넣으면 해당 프로필을 사용한다고 판단하고 아래 규칙으로 해당 프로필에 맞는 내부 파일(설정 데이터)을 조회합니다.
application-{profile}.properties
ex) spring.profiles.active=dev 일 경우, application-dev.properties를 설정 데이터로 활용
spring.profiles.active=prod 일 경우, application-prod.properties를 설정 데이터로 활용
spring.profiles.active 설정값을 실행하는 방법은 3가지가 있습니다.
- IDE에서 커맨드 라인 옵션 인수 실행 : --spring.profiles.active=dev
- IDE에서 자바 시스템 속성 실행 : -Dspring.profiles.active=dev
- Jar 실행
- ./gradlew clean build
- build/libs로 이동
- java -Dspring.profiles.active=dev -jar {프로젝트명}.jar 혹은 java -jar {프로젝트명}.jar --spring.profiles.active=dev
내부 파일 합체
위와 같이 설정 파일을 분리해서 관리할 경우, 한눈에 전체가 들어오지 않는 단점이 있습니다. Spring에서는 이를 보완하기 위해 하나의 파일 안에서 논리적으로 영역을 구분할 수 있도록 해 줍니다.
- 기존에 분리되었던 파일들을 하나의 application.properties에서 관리할 수 있도록 변경하였습니다.
- 이때, Spring에서는 아래 방법으로 하나의 파일을 논리적인 영역으로 구분합니다.
- application.properties: #--- 또는 !--- (dash 3개)
- application.yml: --- (dash 3개)
- 각 프로필에 따라 논리적으로 구분된 설정 데이터를 활성하는 방법
- 그림과 같이 spring.config.active.on-profile에 프로필 값을 지정합니다.
- 내부 파일 분리에서와 동일한 방법 (커맨드 라인 옵션 인수 실행, 자바 시스템 속성 실행, Jar 실행)으로 프로필을 지정하여 실행하면 됩니다.
application.properties를 아래와 같이 구성 후, 코드를 실행할 때, 각 프로필을 지정하여 실행하면 지정된 프로필에 설정된 값을 조회할 수 있습니다.
# application.properties
url=default.db.com
username=default_user
password=default_pw
#---
spring.config.activate.on-profile=dev
url=dev.db.com
username=dev_user
password=dev_pw
#---
spring.config.activate.on-profile=prod
url=prod.db.com
username=prod_user
password=prod_pw
- 만약, 프로필을 지정하지 않고 실행하면, 가장 위에 있는 'default' 프로필이 실행됩니다.
- 프로필을 한 번에 둘 이상 설정하는 것도 가능합니다.
- --spring.profiles.active=dev, prod
- Spring은 단순하게 순서대로 설정파일을 읽어서 값을 설정합니다. 아래와 같은 설정 파일이 있을 경우 실행 결과가 어떻게 될지 생각해 볼 수 있습니다.
# application.properties
url=default.db.com
username=default_user
password=default_pw
#---
spring.config.activate.on-profile=dev
url=dev.db.com
username=dev_user
password=dev_pw
#---
spring.config.activate.on-profile=prod
url=prod.db.com
username=prod_user
password=prod_pw
#---
url=hello.db.com
- Spring은 처음에 default 프로필의 데이터를 읽어서 설정합니다.
- Spring은 그다음으로 dev 프로필의 데이터를 읽는데, 만약 dev 프로필이 설정되어 있다면 기존 데이터를 dev 데이터로 대체합니다.
- Spring은 그 다음으로 prod 프로필의 데이터를 읽는데, 만약 prod 프로필이 설정되어 있다면 기존 데이터를 prod 데이터로 대체합니다.
- Spring은 마지막으로 url=hello.db.com 데이터를 읽습니다. 별도의 프로필이 지정되어 있지 않으므로 프로필과 무관하게 항상 값이 설정됩니다. (즉, dev나 prod 프로필로 설정되어 있어도 url은 hello.db.com으로 대체됩니다.)
- 위와 같은 특징 때문에, 프로필에서 일부 내용만 교체하고 싶을 경우 (특정 속성만 부분적으로 변경) 유용하게 사용할 수 있습니다. 하지만, 그럴 의도가 아니었다면 본인도 모르게 설정값이 바뀌어 있을 수도 있으니 주의해서 사용해야 합니다.
외부 설정의 우선순위
Spring에서 외부 설정을 사용하는 다양한 방법에 대해서 두 번의 포스팅으로 나누어 살펴보았습니다. 각 방법들의 우선순위는 어떻게 적용되는지 정리해 보겠습니다.
우선순위 결정 방법은 저번 포스팅에서도 소개했지만 다시 정리하면 아래와 같습니다.
- 더 유연한 것이 우선권을 가집니다.
- 변경하기 어려운 파일 < 실행 시 원하는 값을 줄 수 있는 자바 시스템 속성
- 범위가 넓은 것 보다 좁은 것이 우선권을 가집니다.
- OS 환경변수 < 자바 시스템 속성 < 커맨드 라인 옵션 인수
따라서, 우선순위를 정리해 보면 아래와 같습니다. (위에서 아래로 갈수록 우선순위가 낮습니다.)
- @TestPropertySource (테스트에서만 사용)
- 커맨드 라인 옵션 인수
- 자바 시스템 속성
- OS 환경변수
- 설정 데이터 (application.properties)
- jar 내부 application.properties
- jar 내부 프로필 적용 파일 application-{profile}.properties
- jar 외부 application.properties
- jar 외부 프로필 적용 파일 application-{profile}.properties
실무에서는 대부분 application.properties에 외부 설정값들을 관리하여 기본으로 사용하다가, 일부 속성을 변경할 필요가 있다면 더 높은 우선순위를 가진 자바 시스템 속성이나 커맨드 라인 옵션 인수를 사용하여 일부만 변경할 수 있습니다. 이렇게 우선순위에 따라서 설정을 추가하거나 변경하는 방식은 상당히 편리하고 유연한 구조를 만들어 줍니다.
지금까지는 외부 설정을 Environment를 이용하여 읽어 보았습니다. 아래 포스팅에서는 Spring이 지원하는 @Value와 @ConfigurationProperties를 이용하여 더 다양한 방법으로 외부 설정을 조회하는 방법에 대해서 작성하였습니다.
[참고자료]
김영한, " 스프링 부트 - 핵심 원리와 활용", 인프런