과거
- OS환경 변수
- 자바 시스템 속성 : JVM안에서 사용
- 자바 커맨드 라인 인수 : 커맨드 라인에서 전달하는 외부설정, 실행시 main(args)에서 사용
- 외부파일: 외부파일 직접읽어서 사용
-> 각각의 외부설정 방식은 따로 존재하지만 여기서는 설명하지 않겠다.
Environment 를 통해서 특정 외부 설정에 종속되지 않고, 일관성 있게 key=value 형식의 외부 설정에 접근할 수 있다
environment.getProperty(key) 를 통해서 값을 조회할 수 있다.
같은 값이 있을 경우를 대비해 스프링에서는 우선순위를 정해 놨다.
우리가 자주 사용하는 application.properties/ yml 도 PropertySource에 추가된다. 따라서 Environment 를 통해 접근 가능
1.커맨드 라인 옵션인수
--url=proddb --username=prod_user --password=prod_pw
2.자바 시스템 속성
-Durl=devdb -Dusername=dev_user -Dpassword=dev_pw
-> JVM안에서 모두 접근할수 있는 반면 커맨드 라인 옵션 인수는 main(args) 통해 들어오기 떄문에 접근 범위가 더 좁다.
url=dev.db.com
username=dev_user
password=dev_pw
지금까지 알아본 방법들은 외부설정 값이 늘어 날수록 관리가 어렵다
그래서 외부 설정값들을 하나의 파일에 넣어 관리하는 방법을 사용한다.
properties는 캐밥 표기법 사용
로딩 시점에 각각에 해당하는 환경에 따라 다르게 읽는다(개발,운영)
applicaion.properties 를 만들어주면 스프링에서는 이 파일을 읽어서 사용할 수 있는 PropertySource의 구현체를 제공함
당연히 Environment 를 통해서도 조회 가능
하지만 이렇게 외부파일은 파일자체 관리 문제가 생김, 예를들어 변경이력, 변경이력이 코드에 어떤 영향을 주는지 등등
yml,properties를 내부에 두는 방식
이 방식은 설정파일의 변경사항도 함께 배포 가능
개발,운영 설정파일을 같이 빌드하고 외부설정값으로 읽을 설정파일을 정함
이 두 설정 파일을 구분하기 위해 스프링은 프로필 이라는 개념 지원
spring.profiles.active=dev -> application-dev.properties
spring.profiles.active=prod -> application-prod.properties
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
스프링은 application.properties 파일 안에서 논리적으로 영역을 구분하는 방법 제공
properties : #--- 또는 !--
yml : ---
별거없다, 그냥 개발,운영 외부설정 파일을 함께 관리 하는 방법
프로필에 값을 주지 않으면 기본은 Default라는 값이 들어옴
url=local.db.com
username=local_user
password=local_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=local.db.com
username=local_user
password=local_pw
#---
spring.config.activate.on-profile=dev
url=dev.db.com
- Environment
- @Value
- @ConfigurationProperties
@Slf4j
@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.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);
MyDataSource를 스프링 빈으로 등록
Environment.getProperty(key,type) -> 타입정보를 주면 해당 타입으로 변환시켜줌
application.properties 생성 -> Environment 를 통해 외부설정 값 조회 -> MyDataSource 만듬
-> 이 방법은 직접 Env를 사용해야 한다는 단점이 있다. 이를 해결하기 위해 스프링은 @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;
@Bean
public MyDataSource myDataSource1() {
return new MyDataSource(url, username, password);
}
@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}") 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);
}
ex) @Value("${my.datasource.etc.max-connection:1}")
-> @Value 방식도 편하지만 ,하나하나 외부 설정 값들을 불러와야 한다는 단점이 있다. 정보의 묶음, 즉 객체로 변환해서 사용해보자
스프링은 외부 설정을 묶음, 즉 객체로 변환하는 기능을 제공한다.
객체를 사용하면 타입을 사용할 수 있어서 잘못된 타입이 들어오는 문제도 방지할 수 있다.
외부설정을 자바코드로 관리하는법
@Data
@ConfigurationProperties("my.datasource")
public class MyDataSourcePropertiesV1 {
private String url;
private String username;
private String password;
private Etc etc = new Etc();
@Data
public static class Etc {
private int maxConnection;
private Duration timeout;
private List<String> options = new ArrayList<>();
}
@ConfigurationProperties가 있으면 외부 설정을 주입 받는 객체라는 뜻이다.
여기서 외부 설정 key의 묶음 시작점인 my.datasource를 적어준다.
@EnableConfigurationProperties(MyDataSourcePropertiesV1.class)
public class MyDataSourceConfigV1 {
private final MyDataSourcePropertiesV1 properties;
public MyDataSourceConfigV1(MyDataSourcePropertiesV1 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());
@EnableConfigurationProperties(외부설정객체.class) 로 스프링에게 사용할 @ConfigurationProperties를 지정해준다. 빈으로 자동 등록 되서 주입받아 사용 가능하다.
이 방법은 하나하나 직접 등록 할때고 , 특정 범위로 자동 등록할 때는 @ConfigurationPropertiesScan 을 사용하면 된다.
외부설정은 초기화때 한번 설정되고 변경되서는 안된다. 하지만 위에 예제를 보면 setter가 존재 함으로 변경될 가능성이 있다. 자바빈 프로퍼티 방식이 아니라 생성자를 통해서 객체를 만들자
application.properties 에 필요한 외부 설정을 추가하고, @ConfigurationProperties 의 생성자 주입을 통해서 값을 읽어들였다. Setter 가 없으므로 개발자가 중간에 실수로 값을 변경하는 문제가 발생하지 않는다.
타입검증은 가능하지만 범위에 대한 해결책은 아직 없다. 이문제에 대해서는 @Validation을 사용해 해결해주자. 이 글에서는 @Validation에 대해 자세히 다루지 않겠다.
my:
datasource:
url: local.db.com
username: local_user
password: local_pw
etc:
maxConnection: 2
timeout: 60s
options: LOCAL, CACHE
---
spring:
config:
activate:
on-profile: dev
my:
datasource:
url: dev.db.com
username: dev_user
password: dev_pw
etc:
maxConnection: 10
timeout: 60s
options: DEV, CACHE
---
spring:
config:
activate:
on-profile: prod
my:
datasource:
url: prod.db.com
username: prod_user
password: prod_pw
yml도 ---를 사용해 여러개의 설정값 설정 가능
spring.config.active.on-profile을 통해 프로필 설정 가능
-> 외부설정값에 따라 설정데이터들이 달라짐
@Slf4j
@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();
}
@Component
@RequiredArgsConstructor
public class OrderRunner implements ApplicationRunner {
private final OrderService orderService;
@Override
public void run(ApplicationArguments args) throws Exception {
orderService.order(1000);
}
}
하나의 인터페이의 구현체가 각각 다른 상황
환경마다 설정값이 다른것은 해결했다. @Profile은 외부 설정값에 따른 빈등록을 할때 사용
외부 설정값을 주지 않으면 default, prod로 주면 두번째 코드가 빈 등록이 된다.
외부설정 값에 따라 빈등록이 달라짐
ApplicationRunner 인터페이스를 사용하면 스프링은 빈 초기화가 모두 끝나고 애플리케이션 로딩이완료되는 시점에 run(args) 메서드를 호출