
스프링부트는 @ConfigurationProperties
를 통해 외부 설정을 객체로 바인딩해서 관리하는 기능을 제공한다.
이를 활용하면 설정값을 타입 안전하게 사용할 수 있고, 유지보수성이 크게 향상된다.
이번 글에서는 @ConfigurationProperties
의 개념, 사용법, 검증에 대해 알아보겠다.
1. @ConfigurationProperties란?
1.1 개념과 장점
@ConfigurationProperties
는 외부 설정값을 자바 객체에 자동으로 바인딩하는 기능을 제공한다.
이를 통해 설정 파일의 값을 일괄적으로 객체에 매핑할 수 있으며, 타입 안전성을 보장한다.
장점
- 타입 안전성: 설정값이 객체 필드와 타입에 맞게 바인딩된다.
- 일괄 바인딩: 설정값을 하나의 객체로 묶어서 관리할 수 있다.
- 유지보수성 향상: 설정값이 많아질수록 코드가 간결하고 체계적으로 유지된다.
2. Getter/Setter 기반 바인딩
@ConfigurationProperties
를 사용하려면 Getter/Setter가 있는 자바 클래스를 정의해야 한다.
2.1 코드 예시
application.properties
my.datasource.url=jdbc:mysql://localhost:3306/mydb
my.datasource.username=root
my.datasource.password=1234
my.datasource.etc.max-connection=5
my.datasource.etc.timeout=30s
바인딩 클래스:
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;
@Data
@ConfigurationProperties(prefix = "my.datasource")
public class MyDataSourceProperties {
private String url;
private String username;
private String password;
private Etc etc;
@Data
public static class Etc {
private int maxConnection;
private Duration timeout;
}
}
설정 클래스에서 활성화
import hello.datasource.MyDataSource;
import hello.datasource.MyDataSourceProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@Slf4j
@EnableConfigurationProperties(MyDataSourceProperties.class)
public class MyDataSourceConfig {
private final MyDataSourceProperties properties;
public MyDataSourceConfigV1(MyDataSourceProperties properties) {
this.properties = properties;
}
@Bean
public MyDataSource dataSource() {
return new MyDataSource(
properties.getUrl(),
properties.getUsername(),
properties.getPassword(),
properties.getEtc().getMaxConnection(),
properties.getEtc().getTimeout(),
properties.getEtc().getOptions());
}
}
2.2 단점
setter
가 있기 때문에 외부에서 수정이 가능하기 떄문에 누군가 실수로 값을 변경하는 문제가 발생할 수 있다.
그래서 아래의 생성자 방식으로 바인딩 하는 것이 안전하다.
3. 생성자 기반 바인딩
import lombok.Getter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;
@Getter
@ConfigurationProperties(prefix = "my.datasource")
public class MyDataSourceProperties {
private final String url;
private final String username;
private final String password;
private final Etc etc;
public MyDataSourceProperties(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 final int maxConnection;
private final Duration timeout;
public Etc(int maxConnection, Duration timeout) {
this.maxConnection = maxConnection;
this.timeout = timeout;
}
}
}
타입과 객체를 통해서 숫자에 문자가 들어오는 것 같은 기본적인 타입 문제들은 해결이 되었다.
그런데 타입은 맞는데 숫자의 범위가 기대하는 것과 다르면 어떻게 될까?
이 문제는 아래의 검증 방식으로 해결할 수 있다.
4. 외부 설정 검증
스프링 부트는 @Validated
애너테이션과 함께 Hibernate Validator를 사용해 설정값을 검증할 수 있다.
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;
import lombok.Getter;
import org.hibernate.validator.constraints.time.DurationMax;
import org.hibernate.validator.constraints.time.DurationMin;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import java.time.Duration;
import java.util.List;
@Getter
@ConfigurationProperties("my.datasource")
@Validated
public class MyDataSourceProperties {
@NotEmpty
private String url;
@NotEmpty
private String username;
private Etc etc;
public MyDataSourceProperties(String url, String username, Etc etc) {
this.url = url;
this.username = username;
this.etc = etc;
}
@Getter
public static class Etc {
@Min(1)
@Max(100)
private int maxConnection;
@DurationMin(seconds = 1)
@DurationMax(seconds = 60)
private Duration timeout;
public Etc(int maxConnection, Duration timeout) {
this.maxConnection = maxConnection;
this.timeout = timeout;
}
}
}
application.properties:
my.datasource.url=jdbc:mysql://localhost:3306/mydb
my.datasource.username=
my.datasource.etc.max-connection=150
my.datasource.etc.timeout=30
실행 시 검증 실패 예시:
Caused by: org.springframework.boot.context.properties.bind.BindException:
Validation failed for classes [MyDataSourceProperties]
- username: must not be empty
- maxConnection: must be less than or equal to 100
5. 실제 동작 원리
@ConfigurationProperties
는 ConfigurationPropertiesBindingPostProcessor
를 통해 동작한다.
ConfigurationPropertiesBindingPostProcessor 클래스
- 빈 등록 탐색: 스프링 컨테이너는
@ConfigurationProperties
가 붙은 빈을 탐색한다. - 바인딩 수행:
Binder
클래스를 통해 설정 파일의 값을 객체 필드에 바인딩한다. - 검증 처리:
@Validated
가 적용되었다면 Hibernate Validator를 사용해 값을 검증한다.
6. 마무리: @ConfigurationProperties의 장점
- 타입 안전성: 설정값을 타입에 맞게 안전하게 바인딩한다.
- 유지보수성: 설정값이 많을 경우 객체를 사용해 관리하기 편리하다.
- 검증 기능:
@Validated
를 통해 설정값을 쉽게 검증할 수 있다. - 일괄 바인딩: 설정값을 한 번에 객체에 매핑하여 코드가 간결해진다.
'Backend > Spring' 카테고리의 다른 글
BeanFactory와 ApplicationContext (0) | 2024.12.28 |
---|---|
서블릿 컨테이너 초기화와 스프링의 처리 방식 (0) | 2024.12.27 |
3부: 스프링 레거시와 스프링부트의 외부 설정 (1) | 2024.12.21 |
2부: 스프링 외부 설정 통합 관리 : Environment와 PropertySource의 동작 구조 (1) | 2024.12.19 |
1부: 자바 애플리케이션에서 외부 설정 조회 방법 (0) | 2024.12.18 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!