https://megamaker.tistory.com/383
[Spring Boot] 외부 설정 알아보기
프로젝트를 진행할 때, 생산성을 위해 개발 환경과 운영 환경을 나누는 경우가 많다. 자바와 스프링 부트에서는 이 환경을 만드는데 도움을 주는 여러 방법이 있다. 외부 설정 방법OS 환경 변수
megamaker.tistory.com
ㄴ 이전 게시글
앞서 알아보았던 OS 환경 변수, JVM 시스템 속성 변수, 커맨드 라인 인수 등 외부 설정을 가져오는 방법은 여러 가지 있었다.
그런데 여기서 불편한게 각각 사용하는 메서드가 다르다는 문제가 발생했다.
그래서 스프링에서는 PropertySource와 Environment를 이용해서 일관된 방법으로 외부 설정을 가져올 수 있도록 지원한다.
Environment
@Slf4j
@Component
public class EnvironmentCheck {
private final Environment env;
public EnvironmentCheck(Environment env) {
this.env = env;
}
@PostConstruct
public void init() {
String url = env.getProperty("url");
String username = env.getProperty("username");
String password = env.getProperty("password");
log.info("env url = {}", url);
log.info("env username = {}", username);
log.info("env password = {}", password);
}
}
@Configuration
public class MyDataSourceEnvConfig {
private final Environment env;
public MyDataSourceEnvConfig(Environment env) {
this.env = 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");
Integer maxConnection = env.getProperty("my.datasource.etc.max-connection", Integer.class);
Duration timeout = env.getProperty("my.datasource.etc.timeout", Duration.class);
List<String> property = env.getProperty("my.datasource.etc.options", List.class);
return new MyDataSource(url, username, password, maxConnection, timeout, property);
}
}
사용 방법은 간단하다. Environment 인터페이스를 사용해서 그냥 빈을 자동으로 주입받아 사용하면 된다.
실행 중 breakpoint를 걸어 확인해보면 ApplicationEnvironment가 Environment에 할당된 것을 알 수 있다. 상속을 쭉 거슬러 올라가다보면 Environment 인터페이스가 있는 것을 확인할 수 있다.
getProperty() 메서드로 커맨드라인 인수든 OS 환경 변수든 그냥 key 값만 입력하면 원하는 value를 가져올 수 있다.
다만 중복되는 것은 우선순위에 따라 가져오는 값이 달라진다.
스프링에서 보통 우선 순위는
- 더 유연한 방법
- 더 구체적인 방법
이 높은 우선 순위를 갖는다고 한다. 다음 링크에서 우선 순위를 확인할 수 있다.
23. Externalized Configuration
The getters and setters are advisable, since binding is via standard Java Beans property descriptors, just like in Spring MVC. They are mandatory for immutable types or those that are directly coercible from String. As long as they are initialized, maps, c
docs.spring.io
@Value
@Slf4j
@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:2}")
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:2}") 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를 이용해서 따로 Environment를 사용하지 않아도 값을 바로 가져올 수 있다.
기본 값을 설정하고 싶으면 중괄호 안 마지막에 :기본값 을 입력하면 된다.
@Value는 멤버 변수에 사용할 수 있고 메서드 파라미터에도 사용할 수 있다.
@ConfigurationProperties
@Getter
@Validated
@ConfigurationProperties("my.datasource")
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 {
@Max(999)
@Min(1)
private int maxConnection;
@DurationMax(seconds = 60)
@DurationMin(seconds = 1)
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;
}
}
}
@ConfiguarionProperties는 Environment나 @Value를 사용하지 않고, JPA에서 엔티티와 자바 객체를 매핑하는 것처럼 설정 속성의 공통된 부분을 기준으로 자바 객체에 매핑해준다.
예를 들어, datasource는 spring.datasource.username / spring.datasource.username 등 여러 속성을 일일이 가져와야하는데 이를 spring.datasource를 @ConfiguarionProperties에 작성하여 객체로 다룰 수 있도록 해준다.
가져온 외부 설정을 덮어쓰면 안 되므로 setter는 지양하고 가급적 생성자만 사용하도록한다. 자바 빈 프로퍼티 규약을 따르므로 생성자나 setter가 있어야 하지만 setter는 가급적 사용하지 않기로 했으니 생성자만 만든다.
추가적으로 위와 같이 Validation 처리를 해줄 수도 있다.
@Slf4j
@EnableConfigurationProperties(MyDataSourcePropertiesV3.class)
public class MyDataSourceConfigV3 {
private final MyDataSourcePropertiesV3 propertiesV3;
public MyDataSourceConfigV3(MyDataSourcePropertiesV3 propertiesV3) {
this.propertiesV3 = propertiesV3;
}
@Bean
public MyDataSource dataSource() {
return new MyDataSource(propertiesV3.getUrl(), propertiesV3.getUsername(), propertiesV3.getPassword(),
propertiesV3.getEtc().getMaxConnection(), propertiesV3.getEtc().getTimeout(),
propertiesV3.getEtc().getOptions());
}
}
@ConfiguarionProperties를 붙인 클래스를 만들었으면 설정 클래스에서 이를 등록해야 한다. @Configuration을 사용하는 것처럼 만들면 되는데 다른 점은 @EnableConfigurationProperties라는 어노테이션을 사용한다는 점이다. 해당 어노테이션 내에 앞서 작성했던 @ConfiguarionProperties를 붙인 클래스를 작성한다.
@Enable~는 붙여도 되고 안 붙여도 되는데 안 붙이는 경우는 뒤에서 설명한다.
@Import(MyDataSourceConfigV3.class)
@ConfigurationPropertiesScan("hello.datasource")
@SpringBootApplication(scanBasePackages = {"hello.datasource", "hello.pay"})
public class ExternalReadApplication {
public static void main(String[] args) {
SpringApplication.run(ExternalReadApplication.class, args);
}
}
main() 메서드가 있는 클래스에서 해당 설정 정보를 포함시키면 된다. @Import를 사용해도 되고, 컴포넌트 스캔 위치를 지정해서 사용해도 된다.
다만 주의할 점은, @EnableConfigurationProperties(MyDataSourcePropertiesV3.class)와 같이 직접 해당 어노테이션을 작성하면 Application 클래스에서 따로 설정할건 없지만 일일이 클래스마다 작성해야 해서 불편하다는 단점이 있다. 그래서 @ConfigurationPropertiesScan이라는 어노테이션을 Application 클래스에 붙여서 자동으로 스캔해 사용할 수 있도록 할 수 있다.
'공부 > Spring' 카테고리의 다른 글
[Spring Boot] 그라파나(Grafana) 대시보드 사용하기 (1) | 2024.09.15 |
---|---|
[Spring Boot] 마이크로미터(Micrometer), 프로메테우스(Prometheus) 알아보기 (0) | 2024.09.09 |
[Spring Boot] 외부 설정 알아보기 (0) | 2024.08.31 |
[Spring Boot] @EnableAutoConfiguration, @ImportSelector 알아보기 (0) | 2024.08.30 |
[Spring Boot] 스프링 부트 자동 구성 및 @Conditional 알아보기 (0) | 2024.08.27 |