| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
- JdbcTemplate
- Dead Letter Queue
- @Transactional
- mybatis
- 페이징
- Spring
- Routing Key
- securitycontextholderfilter
- Spring Container
- dockerhub
- JWT
- JPQL
- Web
- Spring Data JPA
- JPA
- docker compose
- 쿠버네티스
- 스프링 부트
- @ComponentScan
- MSA
- CORS
- 서블릿 컨테이너
- DLQ
- redis
- DI
- 지연 로딩
- AWS
- kafka
- 컨테이너
- docker
- Today
- Total
look-forest
외부 설정 사용 본문
스프링이 제공하는 외부 설정을 읽는 방법들을 알아보자.
스프링이 지원하는 다양한 외부 설정 조회 방법
- Environment
- @Value - 값 주입
- @ConfigurationProperties - 타입 안전한 설정 속성
외부 설정 사용 - Environment
외부 설정들은 스프링이 제공하는 Environment 를 통해서 일관된 방식으로 조회할 수 있다.
@Configuration
public class MyDataSourceEnvConfig {
private final Environment env;
@Bean
public MyDataSource myDataSource() {
String url = env.getProperty("my.datasource.url");
String username = env.getProperty("my.datasource.username");
String password = env.getProperty("my.datasource.password");
int maxConnection = env.getProperty("my.datasource.etc.max-connection", Integer.class);
Duration timeout = env.getProperty("my.datasource.etc.timeout", Duration.class);
List<String> options = env.getProperty("my.datasource.etc.options", List.class);
return new MyDataSource(url, username, password, maxConnection, timeout, options);
}
}
위 방식의 단점은 Environment 를 직접 주입받고, env.getProperty(key) 를 통해서 값을 꺼내는 과정을 반복해야 한다는 점이다. 스프링은 @Value 를 통해서 외부 설정값을 주입 받는 더욱 편리한 기능을 제공한다.
외부설정 사용 - @Value
@Value 를 사용하면 외부 설정값을 편리하게 주입받을 수 있다.
참고로 @Value 도 내부에서는 Environment 를 사용한다.
@Value 는 필드에 사용할 수도 있고, 파라미터에 사용할 수도 있다. 타입 컨버팅도 적용된다.
@Configuration
public class MyDataSourceValueConfig {
@Value("${my.datasource.url}")
private String url;
@Value("${my.datasource.username}")
private String username;
@Value("${my.datasource.password}")
private String password;
@Value("${my.datasource.etc.max-connection}")
private int maxConnection;
@Value("${my.datasource.etc.timeout}")
private Duration timeout;
@Value("${my.datasource.etc.options}")
private List<String> options;
@Bean
public MyDataSource myDataSource1() {
return new MyDataSource(url, username, password, maxConnection, timeout, options);
}
@Bean
public MyDataSource myDataSource2(
@Value("${my.datasource.url}") String url,
@Value("${my.datasource.username}") String username,
@Value("${my.datasource.password}") String password,
//만약 키를 찾지 못할 경우 코드에서 기본값을 사용하려면 다음과 같이 : 뒤에 기본값을 적어주면 된다
@Value("${my.datasource.etc.max-connection:1}") int maxConnection,
@Value("${my.datasource.etc.timeout}") Duration timeout,
@Value("${my.datasource.etc.options}") List<String> options) {
return new MyDataSource(url, username, password, maxConnection, timeout, options);
}
}
단점
@Value 로 하나하나 외부 설정 정보의 키 값을 입력받고, 주입 받아와야 하는 부분이 번거롭다.
그리고 설정 데이터를 보면 하나하나 분리되어 있는 것이 아니라 my.datasource 의 묶음으로 되어 있다.
이런 부분을 객체로 변환해서 편리하게 사용할 수 있는 방법을 알아보자.
외부설정 사용 - @ConfigurationProperties
Type-safe Configuration Properties
스프링은 외부 설정의 묶음 정보를 객체로 변환하는 기능을 제공한다. 이것을 타입 안전한 설정 속성이라 한다. 객체를 사용하면 타입을 사용할 수 있다. 따라서 실수로 잘못된 타입이 들어오는 문제도 방지할 수 있고, 객체를 통해서 활용할 수 있는 부분들이 많아진다. 쉽게 이야기해서 외부 설정을 자바 코드로 관리할 수 있는 것이다. 그리고 설정 정보 그 자체도 타입을 가지게 된다.
ConfigurationProperties 장점
- 외부 설정을 객체로 편리하게 변환해서 사용할 수 있다.
- 외부 설정의 계층을 객체로 편리하게 표현할 수 있다.
- 외부 설정을 타입 안전하게 사용할 수 있다.
- 검증기를 적용할 수 있다.
ConfigurationProperties 사용법
1. 외부 설정을 주입 받을 객체를 생성
@ConfigurationProperties 이 있으면 외부 설정을 주입 받는 객체라는 뜻이다. <지정>
기본 주입 방식은 자바빈 프로퍼티 방식이다. Getter , Setter 가 필요하다.
설정 속성은 빈으로 등록된다.
@Data //getter, setter
@ConfigurationProperties("my.datasource") //외부 설정을 주입받는 객체라는 뜻
public class MyDataSourcePropertiesV1 {
private String url;
private String username;
private String password;
private Etc etc;
@Data
public static class Etc {
private int maxConnection;
private Duration timeout;
private List<String> options = new ArrayList<>();
}
}
2. 설정 속성 사용
Spring Boot 2.2+에서는 @Component, @Configuration, @Service 등의 어노테이션이 붙은 클래스에서 @ConfigurationProperties를 사용할 때 자동 감지된다!
✅ Spring Boot 3.0 이전: @ConfigurationProperties에 @Component를 붙이면 자동 빈 등록된다.
⚠️ Spring Boot 3.0 이상 (3.5 포함): @Component 방식은 권장되지 않고, 일부 경우 제대로 작동하지 않을 수 있다.
Spring Boot 3에서는 @ConfigurationProperties를 명확히 등록 방식에 따라 구분해서 사용하라고 가이드.
# 2.2-
@EnableConfigurationProperties 를 붙여야 설정 값을 주입 받는다. <주입>
해당 클래스는 빈으로 등록된다.
@EnableConfigurationProperties(MyDataSourcePropertiesV1.class)
public class MyDataSourceConfigV1 {
private final MyDataSourcePropertiesV1 properties;
@Bean
public MyDataSource myDataSource() {
return new MyDataSource(
properties.getUrl(),
properties.getUsername(),
properties.getPassword(),
properties.getEtc().getMaxConnection(),
properties.getEtc().getTimeout(),
properties.getEtc().getOptions());
}
}
@ConfigurationPropertiesScan
@ConfigurationProperties 를 하나하나 직접 등록할 때는 @EnableConfigurationProperties 를 사용한다. @ConfigurationProperties 를 특정 범위로 자동 등록할 때는 @ConfigurationPropertiesScan 을 사용하면 된다.
@SpringBootApplication(scanBasePackages = "hello.datasource")
@ConfigurationPropertiesScan
public class ExternalReadApplication {
public static void main(String[] args) {
SpringApplication.run(ExternalReadApplication.class, args);
}
}
문제
MyDataSourcePropertiesV1 은 스프링 빈으로 등록된다. 그런데 Setter 를 가지고 있기 때문에 누군가 실수로 값을 변경하는 문제가 발생할 수 있다. 여기에 있는 값들은 외부 설정값을 사용해서 초기에만 설정되고, 이후에는 변경하면 안된다. 이럴 때 Setter 를 제거하고 대신에 생성자를 사용하면 중간에 데이터를 변경하는 실수를 근본적으로 방지할 수 있다.
외부설정 사용 - @ConfigurationProperties 생성자
@ConfigurationProperties 는 Setter를 사용하는 자바빈 프로퍼티 방식이 아니라 생성자를 통해서 안전하게 만들수 있다.
생성자 파라미터에 @DefaultValue를 붙이면 해당 값을 찾을 수 없는 경우 기본값을 사용한다.
@Getter
@ConfigurationProperties("my.datasource") //외부 설정을 주입받는 객체라는 뜻
public class MyDataSourcePropertiesV2 {
private String url;
private String username;
private String password;
private Etc etc;
// @ConstructorBinding 생성자가 하나일 때는 생략할 수 있다. 생성자가 둘 이상인 경우에는 생성자 바인딩을 사용할 생성자에 적용
public MyDataSourcePropertiesV2(String url, String username, String password, Etc etc) {
this.url = url;
this.username = username;
this.password = password;
this.etc = etc;
}
@Getter
public static class Etc {
private int maxConnection;
private Duration timeout;
private List<String> options;
public Etc(int maxConnection, Duration timeout, @DefaultValue("DEFAULT") List<String> options) {
this.maxConnection = maxConnection;
this.timeout = timeout;
this.options = options;
}
}
}
외부설정 사용 - @ConfigurationProperties 검증
@ConfigurationProperties 를 통해서 숫자가 들어가야 하는 부분에 문자가 입력되는 문제와 같은 타입이 맞지 않는 데이터를 입력하는 문제는 예방할 수 있다. 그런데 문제는 숫자의 범위라던가, 문자의 길이 같은 부분은 검증이 어렵다. 예를 들어서 최대 커넥션 숫자는 최소 1 최대 999 라는 범위를 가져야 한다면 어떻게 검증할 수 있을까? 이메일을 외부 설정에 입력했는데, 만약 이메일 형식에 맞지 않는다면 어떻게 검증할 수 있을까? 개발자가 직접 하나하나 검증 코드를 작성해도 되지만, 자바에는 자바 빈 검증기(java bean validation)이라는 훌륭한 표준 검증기가 제공된다.
@ConfigurationProperties 은 자바 객체이기 때문에 스프링이 자바 빈 검증기를 사용할 수 있도록 지원한다.
자바 빈 검증기를 사용하려면 spring-boot-starter-validation 이 필요하다.
클래스에 @Validated를 붙여야한다.
@Getter
@ConfigurationProperties("my.datasource") //외부 설정을 주입받는 객체라는 뜻
@Validated
public class MyDataSourcePropertiesV3 {
@NotEmpty
private String url;
@NotEmpty
private String username;
@NotEmpty
private String password;
private Etc etc;
public MyDataSourcePropertiesV3(String url, String username, String password, Etc etc) {
this.url = url;
this.username = username;
this.password = password;
this.etc = etc;
}
@Getter
public static class Etc {
@Min(1)
@Max(999)
private int maxConnection;
@DurationMin(seconds = 1)
@DurationMax(seconds = 60)
private Duration timeout;
private List<String> options;
public Etc(int maxConnection, Duration timeout, @DefaultValue("DEFAULT") List<String> options) {
this.maxConnection = maxConnection;
this.timeout = timeout;
this.options = options;
}
}
}
※ 아래와 같이 프로퍼티 값을 잘못 넣으면 애플리케이션 로딩 시점에 다음과 같은 오류 메시지를 확인 할 수 있다
Property: my.datasource.etc.maxConnection
Value: "0"
Origin: class path resource [application.properties] - 4:34
Reason: 1 이상이어야 합니다
YAML
스프링은 설정 데이터를 사용할 때 application.properties 뿐만 아니라 application.yml 이라는 형식도 지원한다.
YAML(YAML Ain't Markup Language)은 사람이 읽기 좋은 데이터 구조를 목표로 한다. 확장자는 yaml , yml 이다.
YML에도 --- 프로필을 적용할 수 있다.
my:
datasource:
url: local.db.com
username: local_user
password: local_pw
etc:
max-connection: 1
timeout: 60s
options: LOCAL, CACHE
---
spring:
config:
activate:
on-profile: dev
my:
datasource:
url: dev.db.com
username: dev_user
password: dev_pw
etc:
max-connection: 10
timeout: 60s
options: DEV, CACHE
@Profile
프로필과 외부 설정을 사용해서 각 환경마다 설정값을 다르게 적용하는 것은 이해했다. 그런데 설정값이 다른 정도가 아니라 각 환경마다 서로 다른 빈을 등록해야 한다면 어떻게 해야할까?
@Profile 을 사용하면 각 환경 별로 외부 설정 값을 분리하는 것을 넘어서, 등록되는 스프링 빈도 분리할 수 있다
@Profile 애노테이션을 사용하면 해당 프로필이 활성화된 경우에만 빈을 등록한다.
특정 조건에 따라서 해당 빈을 등록할지 말지 선택한다. @Conditional 기능을 활용해서 @Profile 기능을 제공하는 것이다.
코드를 보면 @Conditional(ProfileCondition.class)를 확인할 수 있다.
@Configuration
public class PayConfig {
@Bean
@Profile("default")
public LocalPayClient localPayClient() {
log.info("LocalPayClient 빈 등록");
return new LocalPayClient();
}
@Bean
@Profile("prod")
public ProdPayClient prodPayClient() {
log.info("ProdPayClient 빈 등록");
return new ProdPayClient();
}
}
참고 자료 & 이미지 출처
스프링 부트 - 핵심 원리와 활용 (김영한 님)
'Spring > Spring boot - 핵심 원리와 활용' 카테고리의 다른 글
| 외부 설정과 프로필 (0) | 2025.01.29 |
|---|---|
| 자동 구성(Auto Configuration) (0) | 2025.01.26 |
| 스프링 부트 스타터와 라이브러리 관리 (1) | 2025.01.26 |
| 스프링 부트와 내장 톰캣 (0) | 2025.01.25 |
| 외장 WAS 사용 웹 애플리케이션 셋팅 (without springboot) (0) | 2025.01.25 |