Spring boot 애플리케이션의 설정 값을 외부에서 전달하는 방법을 다룹니다.
기준 버전은 2.2x
입니다.
Spring Boot는 동일한 애플리케이션 코드를 다른 환경에서 작업할 수 있도록 설정을 외부에서 전달할 수 있습니다.
사용 가능한 방법은 다음과 같습니다.
.properties
파일프로퍼티 값은 @Value
어노테이션을 통해 빈에 직접 주입하거나, Spring의 Environment
추상화를 통해 접근하거나 @ConfigurationProperties
를 통해 객체에 바인딩할 수 있습니다.
Spring Boot는 값의 오버라이딩을 구분하도록 설계된 까다로운 순서의 PropertySource
를 사용합니다. 프로퍼티는 다음의 순서로 고려됩니다.
홈 디렉터리(개발 도구가 활성화된 경우 ~/.spring-boot-devtools.properties
)의 개발 도구 전역 설정 프로퍼티
테스트의 @TestPropertySource
어노테이션.
테스트의 properties
애트리뷰트. @SpringBootTest
와 애플리케이션의 특정 부분을 테스트하기 위한 테스트 어노테이션에서 사용 가능.
커맨드 라인 인자.
SPRING_APPLICATION_JSON
의 프로퍼티(환경 변수나 시스템 프로퍼티에 삽입된 인라인 JSON).
ServletConfig
초기 파라미터.
ServletContext
초기 파라미터.
java:comp/env
의 JNDI 애트리뷰트.
Java 시스템 프로퍼티(System.getProperties()
).
OS 환경 변수
random.*
에 프로퍼티를 가진RandomValuePropertySource
.
패키지된 jar 외부의 프로파일 지정 애플리케이션 프로퍼티(application-{profile}.properties
와 YAML 형식).
패키지된 jar 내부의 프로파일 지정 애플리케이션 프로퍼티(application-{profile}.properties
와 YAML 형식).
패키지된 jar 외부의 애플리케이션 프로퍼티(application-{profile}.properties
와 YAML 형식).
패키지된 jar 내부의 애플리케이션 프로퍼티(application-{profile}.properties
와 YAML 형식).
@Configuration
클래스의 @PropertySource
어노테이션
(SpringApplication.setDefaultProperties
에 의해 명시된) 기본 프로퍼티.
구체적인 예를 위해, 다음 예시와 같이 name
프로퍼티를 사용하는 @Component
를 개발한다고 가정하겠습니다.
import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.*;
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}
(jar 내부와 같은)애플리케이션 클래스패스에서 name
의 기본 프로퍼티 값을 제공하는 application.properties
을 가질 수 있습니다. 새로운 환경에서 실행할 때, name
을 오버라이드하는 application.properties
파일을 jar 외부에서 제공할 수 있습니다. 단 한번의 테스트 실행에는 커맨드 라인 스위치(ex. java -jar app.jar --name="Spring"
)를 명시하여 실행할 수 있습니다.
커맨드라인에서
SPRING_APPLICATION_JSON
프로퍼티를 환경 변수로 사용할 수도 있습니다. 예를 들어, UN*X 쉘에서는 다음과 같이 사용할 수 있습니다:$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar
앞의 예시에서 Spring
Environtment
에서acme.name=test
로 사용해야 했습니다. 다음 예시와 같이 시스템 프로퍼티에서 JSON을spring.application.json
으로 제공할 수도 있습니다:$ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar
다음과 같이 커맨드 라인 인자로 전달할 수도 있습니다:
java -jar myapp.jar --spring.application.json='{"name":"test"}'
java:comp/env/spring.application.json
과 같이 JSON을 JNDI 변수로 전달할 수도 있습니다.
RandomValuePropertySource
는 (ex. 비밀번호 혹은 테스트 케이스에)랜덤 값을 주입할 때 유용합니다. 다음 예시처럼 integer, long, uuid 또는 문자열을 생성할 수 있습니다:
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}
random.int*
문법은 OPEN value (,max) CLOSE
입니다. OPEN, CLOSE
는 임의의 문자이며, value, max
는 정수이다. max
가 있으면, value
는 최솟값이 되며 max
가 (배타적)최댓값이 됩니다.
기본적으로, SpringApplication
은 (--server.port=9000
과 같이 --
로 시작하는 인자들)임의의 커맨드 라인 옵션 인자를 property
로 변환한 뒤 이를 Spring Environment
에 추가합니다. 이전에 언급했듯, 커맨드 라인 프로퍼티는 항상 다른 프로퍼티 소스보다 우선합니다.
커맨드 라인 프로퍼티가 Environment
에 추가되는 것을 원치 않으면, SpringApplication.setAddCommandLineProperties(false)
를 사용하여 비활성화 할 수 있습니다.
SpringApplication
은 다음 경로의 application.properties
에서 프로퍼티를 불러와 Spring Environment
에 추가합니다.
/config
서브디렉터리/config
패키지위 리스트는 우선순위 순서입니다(위쪽에 위치한 프로퍼티가 아래쪽에 정의된 것을 오버라이드합니다).
’.properties’ 대신 YAML(‘.yml’)파일을 사용할 수도 있습니다.
설정 파일명으로 application.properties
가 마음에 들지 않는다면, spring.config.name
환경 프로퍼티에 다른 파일명을 명시하여 변경할 수 있습니다. 또한 spring.config.location
환경 프로퍼티(쉼표로 구분된 디렉터리나 파일 경로의 리스트)로 특정 위치를 참조할 수도 있습니다. 아래 예시는 다른 파일명을 명시하는 방법을 보여줍니다:
$ java -jar myproject.jar --spring.config.name=myproject
다음 예시는 두 경로를 명시하는 방법을 보여줍니다:
$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
spring.config.name
과spring.config.location
은 어떤 파일이 로드돼야 하는지 결정하기 위해 아주 이른 시점에 사용됩니다. 때문에 이들은 환경 프로퍼티(주로 OS 환경 변수, 시스템 프로퍼티 혹은 커맨드 라인 인자)로 정의돼야 합니다.
설정 위치는 역순으로 검색됩니다. 기본값으로 설정된 경로들은 classpath:/,classpath:/config/,file:./,file:./config/
입니다. 결과적으로 검색 순서는 다음과 같습니다:
file:./config/
file:./
classpath:/config/
classpath:/
spring.config.location
에 별도로 설정 파일 위치가 명시되면 기본 위치값 위치들을 대체합니다. 예를 들어, spring.config.location
이 classpath:/custom-config/,file:./custom-config/
로 설정되면, 검색 순서는 다음과 같습니다:
file:./custom-config/
classpath:custom-config
대신, spring.config.additional-location
으로 설정 파일 위치가 설정되면, 기본 위치들에 추가로 사용됩니다. 추가 위치들은 기본 위치보다 먼저 검색됩니다. 예를 들어, 추가 위치가 classpath:/custom-config/,file:./custom-config/
이면, 검색 순서는 다음과 같습니다:
file:./custom-config/
classpath:custom-config/
file:./config/
file:./
classpath:/config/
classpath:/
이러한 검색 순서는 하나의 설정 파일에 기본 값을 명시하고 선택적으로 다른 파일들에서 오버라이드할 수 있도록 합니다. 기본 위치들 중의 하나에application.properties
(혹은 spring.config.name
에 명시한 다름 이름)로 애플리케이션에 대한 기본 값을 제공할 수 있습니다. 이러한 기본값들은 별도로 명시한 위치들 중의 하나에 위치한 다른 파일을 통해 런타임에 오버라이드될 수 있습니다.
시스템 프로퍼티보다 환경 변수를 사용하는 경우, 대부분의 운영 체제는 마침표로 구분하는 키 이름을 허용하지 않습니다. 대신 언더스코어를 사용할 수 있습니다(ex.
spring.config.name
대신SPRING_CONFIG_NAME
).애플리케이션을 컨테이너에서 실행하는 경우, (
java:comp/env
의)JNDI 프로퍼티 또는 서블릿 컨텍스트 초기화 파라미터가 환경 변수나 시스템 프로퍼티 대신 사용될 수 있습니다.
application.properties
파일에 추가로, 프로파일을 명시한 프로퍼티를 다음의 명명 규칙에 따라 정의할 수 있습니다: application-{profile}.properties
. Environment
은 활성 프로파일이 설정되지 않았을 때 사용되는 기본 프로파일(기본값은 [default]
) 집합을 가지고 있습니다. 즉, 명시적으로 활성화된 프로파일이 없으면 application-default.properties
이 로드됩니다.
프로파일 명시 프로퍼티는 표준 application.properties
과 같은 위치에서 로드되며, 프로파일 구체적인 파일이 패키지된 jar의 내부에 있든 외부에 있든 프로파일 구체적인 파일이 항상 그렇지 않은(프로파일을 명시하지 않은) 파일을 오버라이드합니다.
여러 개의 프로파일이 명시되면, 최후의 승자 전략을 적용합니다. 예를 들어, spring.profiles.active
프로퍼티에 명시된 프로파일들이 SpringApplication
API를 통한 설정보다 뒤에 추가되므로 우선권을 갖습니다.
spirng.config.location
에 임의의 특정 파일이 있으면, 다른 프로파일 명시 파일은 고려되지 않습니다. 프로파일 명시 프로퍼티도 사용하고자 하는 경우에는spring.config.location
내의 디렉터리를 사용하세요.
application.properties
의 값은 사용될 때 기존의 Environment
를 통해 필터됩니다. 따라서 (ex. 시스템 프로퍼티 등)이전에 정의된 값을 참조할 수 있습니다.
app.name=MyApp
app.description=${app.name} is a Spring Boot application
이 기법을 기존 Spring Boot 프로퍼티의 “짧은” 변경을 만드는 데에도 사용할 수 있습니다. 자세한 내용은 ‘짧은’ 커맨드 라인 인자 사용을 참고하세요.
Spring Boot는 프로퍼티 값 암호화를 위한 내장 기능을 지원하지 않지만, Spring Environment
에 포함된 값을 변경하기 위해 필요한 훅 포인트를 제공합니다. EnvironmentPostProcessor
인터페이스는 애플리케이션을 시작하기 전에 Environment
를 조작할 수 있도록 합니다. 자세한 내용은 시작하기 전에 ApplicationContext 또는 Environment 커스터마이즈하기를 참고하세요.
자격 증명이나 패스워드를 저장할 안전한 방법을 찾고 있다면, HashiCorp Vault에 저장된 외부 설정에 대한 지원을 Spring Cloud Vault 프로젝트가 제공하고 있습니다.
YAML은 흔히 말하는 JSON의 수퍼셋으로, 계층 구조의 설정 데이터를 명시하기 편리한 포맷입니다. SpringApplication
클래스는 라이브러리 클래스패스에 SnakeYAML을 가지고 있으면 자동으로 properties 대신 YAML을 지원합니다.
“Starters”를 사용한다면, SnakeYAML은
spring-boot-starter
에 자동으로 제공됩니다.
Spring 프레임워크는 YAML 문서를 로드하는 데 사용될 수 있는 두 개의 편리한 클래스를 제공합니다. YamlPropertiesFactoryBean
은 YAML을 Properties
로 로드하고 YamlMapFactoryBean
은 YAML을 Map
으로 로드합니다.
예를 들어, 다음과 같은 YAML를 가정하겠습니다:
environments:
dev:
url: https://dev.example.com
name: Developer Setup
prod:
url: https://another.example.com
name: My Cool App
앞의 예시는 다음과 같은 프로퍼티로 변환됩니다:
environments.dev.url=https://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=https://another.example.com
environments.prod.name=My Cool App
YAML 리스트는 [index]
역참조한 프로퍼티 키로 표현됩니다. 예를 들어, 다음과 같은 YAML 문서를 가정하겠습니다:
my:
servers:
- dev.example.com
- another.example.com
앞의 예시는 다음과 같은 프로퍼티로 변환됩니다:
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
(ConfigurationProperties
가 하는 것 처럼)Spring Boot의 Binder
유틸리티를 사용하여 이와 같이 프로퍼티를 바인드하려면, 목표 빈에 java.util.List
(혹은 Set
) 타입의 프로퍼티가 있어야 하며 setter를 제공하거나 가변(mutable) 값으로 초기화해야 합니다. 예를 들어, 다음 예시는 이전에 본 properties와 바인드합니다:
@ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}
YamlPropertySourceLoader
클래스는 Spring Environment
에서 YAML을 PorpertySource
로 노출하는 데 사용할 수 있습니다. 이렇게 함으로써 플레이스홀더 구문으로 @Value
어노테이션을 YAML 프로퍼티에 접근하는 데 사용할 수 있습니다.
다음 예시와 같이 문서를 적용할 시기를 나타내는 spring.profiles
키를 사용하여 여러 개의 프로파일을 명시한 YAML 문서를 하나의 파일에 작성할 수 있습니다:
server:
address: 192.168.1.100
---
spring:
profiles: development
server:
address: 127.0.0.1
---
spring:
profiles: production & eu-central
server:
address: 192.168.1.120
앞의 예시에서, development
프로파일이 활성화되면, server.address
프로파일은 127.0.0.1
입니다. 비슷하게, production
과 eu-central
프로파일이 활성화되면, server.address
프로퍼티는 192.168.1.120
입니다. development
, production
과 eu-central
프로파일이 활성화되지 않으면, 프로퍼티 값은 192.168.1.100
입니다.
그렇기 때문에
spring.profiles
는 단순한 프로파일 이름(ex.production
) 또는 프로파일 표현식을 포함할 수 있습니다. 프로파일 표현식은production & (eu-central | eu-west)
과 같이 더 복잡한 프로파일 로직을 표현할 수 있도록 합니다. 더 자세한 내용은 레퍼런스 가이드를 참조하세요.
애플리케이션 컨텍스트를 시작할 때 명시적으로 활성화된 프로파일이 없으면 기본 프로파일이 활성화됩니다. 따라서, 다음의 YAML에서는 오직 “default” 프로파일에서만 사용 가능한 spring.security.user.password
값을 설정합니다:
server:
port: 8000
---
spring:
profiles: default
security:
user:
password: weak
반면에, 다음의 예시에서 패스워드는 어떤 프로파일에도 속해있지 않기 때문에 항상 설정되며 필요에 따라 다른 프로파일에서 명시적으로 재설정됩니다:
server:
port: 8000
spring:
security:
user:
password: weak
spring.profiles
엘리먼트를 사용하여 지정된 Spring 프로파일은 !
문자를 사용하여 선택적으로 부정될 수 있습니다. 만약 한 문서에 부정된 프로파일과 부정되지 않은 프로파일이 모두 명시되면, 적어도 하나의 부정되지 않은 프로파일이 일치해야 하며, 부정된 프로파일은 일치하지 않을 수 있습니다.
YAML 파일은 @PropertySource
어노테이션을 사용해서 로드될 수 없습니다. 때문에 이 방법으로 값을 로드해야 하는 경우 프로퍼티 파일을 사용해야 합니다.
프로파일을 명시한 YAML 파일에서 여러 개의 YAML 문서 구문을 사용하는 것은 예기치 못한 동작으로 이어질 수 있습니다. 예를 들어, application-dev.yml
이라는 파일에서 dev
프로파일이 활성화된 상태로 다음 설정을 가정하겠습니다:
server:
port: 8000
---
spring:
profiles: !test
security:
user:
password: weak
위의 예시에서, 프로파일 부정과 프로파일 표현식은 예상대로 동작하지 않을 것입니다. 프로파일을 명시한 YAML 파일과 복수의 YAML 문서를 함께 사용하지 않고 둘 중 하나를 계속 사용할 것을 권장합니다.
설정 프로퍼티 주입에 @Value("${property}")
어노테이션을 사용하는 것은 프로퍼티가 여럿이거나 데이터가 사실상 계층 구조인 상황에서 특히 복잡할 수 있습니다. Spring Boot는 강 타입 빈(strongly-typed beans)이 애플리케이션의 설정을 통제하고 유효한지 검증하도록 하는 방법을 제공합니다.
@Value
와 타입 안전한 설정 프로퍼티의 차이도 함께 보세요.
다음 예시처럼 표준 자바 빈 프로퍼티로 선언한 빈을 바인드할 수 있습니다:
package com.example;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("acme")
public class AcmeProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
public boolean isEnabled() { ... }
public void setEnabled(boolean enabled) { ... }
public InetAddress getRemoteAddress() { ... }
public void setRemoteAddress(InetAddress remoteAddress) { ... }
public Security getSecurity() { ... }
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
public String getUsername() { ... }
public void setUsername(String username) { ... }
public String getPassword() { ... }
public void setPassword(String password) { ... }
public List<String> getRoles() { ... }
public void setRoles(List<String> roles) { ... }
}
}
앞의 POJO는 다음 프로퍼티를 정의합니다:
false
인 acme.enabled
.String
으로부터 강제될 수 있는 타입의 acme.remote-address
.acme.security.username
. 특히, 반환 타입이 전혀 사용되지 않으며 SecurityProperties
이었을 수도 있습니다.acme.security.password
.USER
인 String
컬렉션 acme.security.roles
.Spring MVC에서와 같이 바인딩이 표준 자바 빈 프로퍼티 설명자를 통해 이뤄지기 때문에, 이러한 방식은 주로 비어있는 기본 생성자와 게터(getter), 세터(setter)가 필수적입니다. 다음의 경우 세터는 생략될 수도 있습니다:
- 맵(Map)은 바인더가 변형할 수 있기 때문에 초기화되기만 하면 게터는 필요하지만 세터는 필수가 아닙니다.
- 컬렉션과 배열은 (주로 YAML에서)인덱스 혹은 단일 쉼표로 구분된 값(프로퍼티)을 통해 접근할 수 있습니다. 후자의 경우 세터가 필수입니다. 이러한 타입에는 항상 세터를 추가할 것을 권장합니다. (앞의 예시에서처럼)컬렉션을 초기화한다면 불변(immutable) 객체가 아닌지 확인하십시오.
- 중첩된 POJO 프로퍼티가 초기화되면 세터가 필요하지 않습니다. 바인더가 기본 생성자를 사용하여 인스턴스를 바로 생성하려면 세터가 필요합니다.
일부 사람들은 게터와 세터를 자동으로 추가하는 데에 Lombok 프로젝트를 사용하기도 합니다. 컨테이너에서 객체를 인스터화하는데 자동으로 사용되기 때문에, Lombok이 이러한 타입에 대해 특정 생성자를 생성하지 않는지 확인하십시오.
마지막으로, 오직 표준 자바 빈만이 고려되며 정적 프로퍼티에 대한 바인딩은 지원되지 않습니다.
이전 섹션의 예시는 다음 예시와 같이 불변 방식으로 다시 작성할 수 있습니다:
package com.example;
import java.net.InetAddress;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DefaultValue;
@ConfigurationProperties("acme")
public class AcmeProperties {
private final boolean enabled;
private final InetAddress remoteAddress;
private final Security security;
public AcmeProperties(boolean enabled, InetAddress remoteAddress, Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
public boolean isEnabled() { ... }
public InetAddress getRemoteAddress() { ... }
public Security getSecurity() { ... }
public static class Security {
private final String username;
private final String password;
private final List<String> roles;
public Security(String username, String password, @DefaultValue("USER") List<String> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
public String getUsername() { ... }
public String getPassword() { ... }
public List<String> getRoles() { ... }
}
}
이 설정에서, 유일한 생성자는 바인드하고자 하는 프로퍼티의 리스트로 정의돼야 하며 그 외의 다른 프로퍼티는 바인드되지 않습니다.
@DefaultValue
를 사용하여 기본 값을 명시할 수 있으며 String
값을 누락된 프로퍼티의 목적 타입으로 강제하는 동일한 변환 서비스가 적용됩니다.
@ConfigurationProperties
어노테이션이 적용된 타입 활성화Spring Boot는 이러한 타입을 바인드하고 자동으로 빈으로 등록하기 위한 기반을 제공합니다. 만약 애플리케이션이 @SpringBootApplication
을 사용한다면, @ConfigurationProperties
가 적용된 클래스들을 자동으로 스캔하고 빈으로 등록합니다. 기본적으로, 스캐닝은 이 어노테이션이 선언된 패키지에서부터 수행됩니다. 만약 스캔할 특정 패키지를 정의하려면 다음 예시처럼 SpringBootApplication
이 적용된 클래스에 @ConfigurationPropertiesScan
지시자를 명시적으로 사용하십시오:
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "org.acme.another" })
public class MyApplication {
}
예를 들어 고유한 자동 설정을 개발하는 경우와 같이, 때로는 클래스에 @ConfigurationProperties
를 적용하는 것이 스캐닝에 적당하지 않을 수 있습니다. 이런 경우에는 다음 예시와 같이 @Configuration
클래스에 처리할 타입의 리스트를 명시할 수 있습니다:
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
@ConfigurationProperties
빈이 스캐닝이나@EnableConfigurationProperties
를 통해 등록될 때, 빈은 관습적인 이름을 갖습니다:<prefix>-<fqn>
,<prefix>
는@ConfigurationProperties
어노테이션에 명시된 환경 키 접두어가 오고<fqn>
은 정규화된 빈 이름이 옵니다. 어노테이션이 접두어를 따로 제공하지 않으면 정규화된 이름만 가진 빈이 사용됩니다.위의 예시에서 빈 이름은
acme-com.example.AcmeProperties
입니다.
@ConfigurationProperties
이 환경만을 다루고, 컨텍스트로부터 다른 빈을 주입하지 않을 것을 권장합니다. 특히 생성자를 사용하여 다른 빈을 주입하는 것은 환경만을 다루는 생성자 바인더를 유발하기 때문에 불가능합니다.
코너 케이스에 대해, 세터 주입이나 (Environment
에 접근하는 경우 EnvironmentAware
와 같이)프레임워크가 제공하는 *Aware
인터페이스가 사용될 수 있습니다.
클래스패스 스캐닝에서 스캔되는 타입인 경우, 타입에
@ConfigurationProperties
를@Component
와 함께 사용하는 것은 같은 타입의 두 개의 빈을 만듭니다.@Component
를 사용하여 빈을 직접 등록하고자 하는 경우,@ConfigurationProperties
의 스캐닝을 비활성화하는 것을 고려하십시오.
@ConfigurationProperties
가 적용된 타입 사용이 스타일의 설정은 다음 예시와 같은 SpringApplication
외부 YAML 설정에 특히 효과적입니다:
# application.yml
acme:
remote-address: 192.168.1.1
security:
username: admin
roles:
- USER
- ADMIN
# additional configuration as required
@ConfigurationProperties
빈과 함께 작동하기 위해 다음 예시와 같이 다른 빈과 같은 방법으로 주입할 수 있습니다:
@Service
public class MyService {
private final AcmeProperties properties;
@Autowired
public MyService(AcmeProperties properties) {
this.properties = properties;
}
// ...
@PostConstruct
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
// ...
}
}
@ConfigurationProperties
를 사용하면 IDE가 키를 자동완성 하는 데 사용할 수 있는 메타데이터를 생성하도록 합니다. 자세한 사항은 부록을 참고하세요.
@ConfigurationProperties
를 클래스에 사용하는 것과 마찬가지로, 이를 public @Bean
메서드에도 사용할 수 있습니다. 이는 통제 밖에 있는 서드 파티 컴포넌트를 프로퍼티에 바인드하고자 할 때 유용합니다.
다음 예시와 같이 Environment
프로퍼티로부터 빈을 설정하기 위해 빈 등록을 위한 @ConfigurationProperties
를 추가합니다:
@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
...
}
another
접두어로 정의된 자바 빈 프로퍼티는 앞의 AcmeProperties
예시와 유사한 방식으로 AnotherComponent
빈에 매핑됩니다.
Spring Boot는 Environment
프로퍼티를 @ConfigurationProperties
빈에 바인딩하기 위해 완화된 규칙을 사용하기 때문에 Environment
프로퍼티 이름과 빈 프로퍼티 이름이 정확히 일치할 필요가 없습니다. 대시(-)로 구분된 환경 프로퍼티(ex. context-path
를 contextPath
에 바인드)와 대문자로 작성(capitalized)된 환경 프로퍼티(ex. PORT
를 port
에 바인드)는 이것이 유용한 흔한 예입니다.
예를 들어, 다음과 같은 @ConfigurationProperties
클래스를 가정합니다:
@ConfigurationProperties(prefix = "acme.my-project.person")
public class OwnerProperties {
private String firstName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
앞의 예시는 다음의 프로퍼티 이름이 모두 사용될 수 있습니다:
프로퍼티 | 참고 |
---|---|
acme.my-project.person.first-name | properties 와 .yml 파일에 사용하도록 권장되는 케밥 케이스. |
acme.myProject.person.firstName | 표준 카멜 케이스 문법. |
acme.my_project.person.first_name | .properties 와 .yml 파일에서 대체 형식으로 사용되는 언더스코어 표기법. |
ACME_MYPROJECT_PERSON_FIRSTNAME | 시스템 환경 변수를 사용할 때 권장되는 대문자 형식. |
어노테이션에 대한
prefix
값은 (acme.my-project.person
과 같이 소문자이며-
로 구분된)케밥 케이스*여야*합니다.
프로퍼티 소스 | 단순 | 리스트 |
---|---|---|
프로퍼티 파일 | 카멜 케이스, 케밥 케이스 또는 언더스코어 표기법 | [ ] 또는 값을 쉼표로 구분한 표준 리스트 구문 |
YAML 파일 | 카멜 케이스, 케밥 케이스 또는 언더스코어 표기법 | 표준 YAML 리스트 구분 또는 쉼표로 구분한 값 |
환경 변수 | 구분자로 언더스코어를 사용한 대문자 형식. _ 는 프로퍼티 이름에서 사용되면 안됨 | MY_ACME_1_OTHER = my.acme[1].other 와 같이 언더스코어에 둘러싸인 숫자 값 |
시스템 프로퍼티 | 카멜 케이스, 케밥 케이스 또는 언더스코어 표기법 | [ ] 또는 값을 쉼표로 구분한 표준 리스트 구문 |
가능한 경우 프로퍼티는
my.property-name=acme
와 같이 소문자 케밥 케이스 사용을 권장합니다.
Map
프로퍼티를 바인딩할 때, key
가 소문자 알파벳과 숫자 또는 -
이외의 다른 문자를 포함하면, 원래의 값이 보존되도록 브라켓 표기법을 사용해야 합니다. 키가 []
에 둘러싸이지 않으면, 알파벳과 숫자 또는 -
이 아닌 문자는 지워집니다. 예를 들어 다음 프로퍼티를 Map
에 바인딩한다고 가정하면:
acme:
map:
"[/key1]": value1
"[/key2]": value2
/key3: value3
위의 프로퍼티는 Map
에 /key1
, /key2
, key3
로 바인드됩니다.
리스트가 한 군데 이상에 구성된 경우, 오버라이딩은 전체 리스트를 대체하는 형태로 동작합니다.
기본값이 null
인 name
와 description
속성을 가진 MyPojo
객체를 에로 들면, 다음 예시는 AcmeProperties
로부터 MyPojo
객체의 리스트를 노출합니다:
@ConfigurationProperties("acme")
public class AcmeProperties {
private final List<MyPojo> list = new ArrayList<>();
public List<MyPojo> getList() {
return this.list;
}
}
다음 설정을 함께 적용합니다:
acme:
list:
- name: my name
description: my description
---
spring:
profiles: dev
acme:
list:
- name: my another name
dev
프로파일이 활성화되지 않은 경우, 앞에서 정의된대로 AcmeProperties.list
는 Mypojo
엔트리 하나를 갖습니다. 하지만 dev
프로파일이 활성화되면, list
는 여전히 (my another name
이라는 이름과 null
인 설명을 갖는) 엔트리 하나만을 갖습니다. 이 설정은 두 번째 MyPojo
인스턴스를 리스트에 추가*하지 않으*며, 아이템을 합치지 않습니다.
List
가 여러 프로파일에서 명시되면, (오직 하나뿐인)가장 높은 우선순위를 가진 하나만이 사용됩니다. 다음 예시를 보겠습니다:
acme:
list:
- name: my name
description: my description
- name: another name
description: another description
---
spring:
profiles: dev
acme:
list:
- name: my another name
앞의 예시에서 dev
프로파일을 활성화하면, AcmeProperties.list
는 (이름이 my another name
이고 설명이 null
인)하나의 MyPojo
엔트리를 갖습니다. YAML에서는 리스트의 내용을 오버라이딩하는데 쉼표로 구분된 리스트와 YAML 리스트를 사용할 수 있습니다.
Map
프로퍼티에는 여러 소스로부터 프로퍼티 값을 뽑아 바인드할 수 있습니다. 하지만, 여러 소스의 같은 프로퍼티에 대해서는 우선순위가 가장 높은 하나가 사용됩니다. 다음의 예시는 AcmeProperties
로부터 Map<String, MyPojo>
를 노출합니다:
@ConfigurationProperties("acme")
public class AcmeProperties {
private final Map<String, MyPojo> map = new HashMap<>();
public Map<String, MyPojo> getMap() {
return this.map;
}
}
다음의 설정을 적용합니다:
acme:
map:
key1:
name: my name 1
description: my description 1
---
spring:
profiles: dev
acme:
map:
key1:
name: dev name 1
key2:
name: dev name 2
description: dev description 2
dev
프로파일을 활성화하지 않으면, AcmeProperties.map
은 (name이 my name 1
이고 description이 my description
인)key1
인 엔트리 하나를 갖습니다. dev
프로파일을 활성화하면 map
은 (name이 dev name 1
이고 description이 my description 1
인)key1
과 (name이 dev name 2
이고 description이 dev description 2
인)key2
를 키로 하는 두 개의 엔트리를 갖습니다.
앞서 다룬 병합 규칙은 YAML 파일 뿐만 아니라 모든 프로퍼티 소스의 프로퍼티에 적용합니다.
외부 애플리케이션 프로퍼티가 @ConfigurationProperties
빈에 바인드되어 있으면 Spring Boot는 강제 변환을 시도합니다. 커스텀 타입 변환이 필요한 경우, (conversionService
라는 이름의)ConversionService
빈 또는 (CustomeEditorConfiguerer
빈을 통한)커스텀 프로퍼티 에디터 또는 커스텀 (@ConfigurationPropertiesBinding
어노테이션이 사용된 빈을 정의하여)Converters
를 제공할 수 있습니다.
이 빈은 애플리케이션 생명주기에서 매우 이른 시점에 요청되기 때문에
ConversionService
가 사용하는 의존성을 반드시 제한하십시오. 보통, 의존성은 생성 시점에 완전히 초기화되어있지 않을 수 있습니다. 설정 키의 변환에 필요하지 않고 오직@ConfigurationPropertiesBinding
으로 지정된 커스텀 컨버터에만 의존하는 경우에는 커스텀ConversionService
의 이름을 바꾸고자 할 수도 있습니다.
Spring Boot는 시간 표현을 지원합니다. java.time.Duration
프로퍼티를 노출하면, 애플리케이션 프로퍼티에서 다음 형식을 사용할 수 있습니다:
@DurationUnit
이 명시되지 않으면 밀리초를 기본 단위로 사용하는) long
표기java.time.Duration
에서 사용하는 ISO-8601 표준 형식10s
는 10초를 의미)다음 예시를 보겠습니다:
@ConfigurationProperties("app.system")
public class AppSystemProperties {
@DurationUnit(ChronoUnit.SECONDS)
private Duration sessionTimeout = Duration.ofSeconds(30);
private Duration readTimeout = Duration.ofMillis(1000);
public Duration getSessionTimeout() {
return this.sessionTimeout;
}
public void setSessionTimeout(Duration sessionTimeout) {
this.sessiontTimeout = sessionTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
public void setReadTimeout(Duration readTimeout) {
this.readTimeout = readTimeout;
}
}
세션 타임아웃 시간을 30 초로 지정할 때, 30
, PT30S
과 30s
는 모두 같습니다. 읽기 타임아웃을 500ms로 지정할 때는 다음과 같은 형식을 사용할 수 있습니다: 500
, PT0.5S
및 500ms
.
지원되는 단위는 다음과 같습니다:
ns
: 나노 초us
: 마이크로 초ms
: 밀리 초s
: 초m
: 분h
: 시d
: 일(day)기본 단위는 밀리 초이며 위의 예시에서 본 것과 같이 @DurationUnit
을 사용하여 재정의할 수 있습니다.
시간 표현에
Long
을 사용하는 이전 버전에서 업그레이드하는 중인 경우, 단위가 밀리 초가 아닌 경우에는 (@DurationUnit
을 사용하여) 단위를 분명히 정의하십시오. 이렇게 함으로써 더 풍부한 형식을 지원하면서 투명한 업그레이드 경로를 제시합니다.
Spring Framework
에는 바이트 크기를 표현할 수 있는 DataSize
값 타입이 있습니다. DataSize
프로퍼티를 노출하면, 애플리케이션 프로퍼티에서 다음 형식을 사용할 수 있습니다:
@DataSizeUnit
이 명시되지 않은 한 바이트를 기본 단위로 사용하는) long
표기10MB
는 10 메가바이트를 의미)다음 예시를 보겠습니다:
@ConfigurationProperties("app.io")
public class AppIoProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize bufferSize = DataSize.ofMegabytes(2);
private DataSize sizeTHreashold = DataSize.ofBytes(512);
public DataSize getBufferSize() {
return this.bufferSize;
}
public void setBufferSize(DataSize bufferSize) {
this.bufferSize = bufferSize;
}
public DataSize getSizeThreshold() {
return this.sizeThreshold;
}
public void setSizeThreshold(DataSize sizeThreshold) {
this.sizeThreshold = sizeThreshold;
}
}
버퍼 크기를 10 메가바이트로 지정할 때, 10
과 10MB
는 같습니다. 256 바이트의 크기 임계치는 256
또는 256B
로 명시할 수 있습니다.
지원되는 단위는 다음과 같습니다:
B
: 바이트KB
: 킬로바이트MB
: 메가바이트GB
: 기가바이트TB
: 테라바이트기본 단위는 바이트이며 위의 예시에서 본 것과 같이 @DataSizeUnit
을 사용하여 재정의할 수 있습니다.
크기 표현에
Long
을 사용하는 이전 버전에서 업그레이드하는 중인 경우, 단위가 바이트가 아닌 경우에는 (@DataSizeUnit
을 사용하여) 단위를 분명히 정의하십시오. 이렇게 함으로써 더 풍부한 형식을 지원하면서 투명한 업그레이드 경로를 제시합니다.
@ConfigurationProperties
클래스에 @Validated
어노테이션이 사용되면 Sprint Boot는 유효성 검사를 시도합니다. JSR-303 javax.validation
제약 조건 어노테이션을 설정 클래스에 바로 사용할 수 있습니다. 이를 위해, 클래스패스에 JSR-303을 준수하는 구현이 있는지 확인하고, 다음 예시와 같이 제약 조건 어노테이션을 필드에 추가하십시오:
@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {
@NotNull
private InetAddress remoteAddress;
// ... getters and setters
}
설정 프로퍼티를 생성하는
@Bean
메서드에@Validated
를 사용해서 유효성 검사를 발동할 수도 있습니다.
중첩 프로퍼티도 바인드될 때 유효성 검사를 하지만, 연관된 필드에도 @Valid
어노테이션을 사용하는 것이 좋은 습관입니다. 이는 중첩 프로퍼티를 찾을 수 없는 경우에도 유효성 검사를 발동하도록 보장합니다. 다음 예시는 앞선 AcmeProperties
예시를 기반으로 합니다:
@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {
@NotNull
private InetAddress remoteAddress;
@Valid
private final SEcurity security = new Security();
// ... getters and setters
public static class Security {
@NotEmpty
public String username;
// ... getters and setters
}
}
configurationPropertiesValidator
로 호출되는 빈 정의를 생성하여 커스텀 Spring Validator
를 추가할 수도 있습니다. @Bean
메서드는 static
으로 선어되어야 합니다. 설정 프로퍼티 유효성 검사자(validator)는 애플리케이션 생명 주기에서 매우 이른 시기에 생성되며, @Bean
메서드를 정적으로 선언하면 @Configuration
클래스를 인스턴스화할 필요 없이 빈을 생성하도록 합니다. 이렇게 함으로써 이른 시기에 인스턴스화하여 발생할 수 있는 문제들을 피합니다.
spring-boot-actuator
모듈은 모든@ConfiruationProperties
빈들을 노출하는 엔드포인트를 갖고 있습니다. 웹 브라우저로/actuator/configprops
에 접속하거나 동일한 JMX 엔드포인트를 사용하세요. 자세한 사항은 “Productrion ready features“를 참조하세요.
@Value
어노테이션는 핵심 컨테이너 기능이며, 타입 안전한 설정 프로퍼티와 동일한 기능을 제공하지 않습니다. 다음 표는 @ConfigurationProperties
와 @Value
가 지원하는 기능들을 요약합니다:
기능 | @ConfigurationProperties | @Value |
---|---|---|
완화된 바인딩 | Yes | No |
메타-데이터 지원 | Yes | No |
SpEL 평가 | No | Yes |
고유 컴포넌트를 위한 설정 키 집합을 정의할 때는 이들을 @ConfigurationProperties
어노테이션을 사용하는 POJO로 그룹화할 것을 권장합니다. @Value
는 완화된 바인딩을 지원하지 않으므로, 환경 변수를 사용하여 값을 제공해야 하는 경우에는 좋은 선택지가 아니라는 점을 유념하십시오.
마지막으로, @Value
에서는 SpEL
표현식을 사용할 수 있는 반면에, 이러한 표현식은 애플리케이션 프로퍼티 파일로부터 처리되지 않습니다.