[Spring Boot] 외부 설정
개발한 애플리케이션을 실행할 때는 개발 / 로컬 / 운영 환경으로 나눠서 실행하고, 환경마다 사용하는 데이터베이스나 기타 설정을 다르게 설정한다.
변하는 환경 값들을 실행하는 시점에서 외부에서 넣어 주는 방식을 사용하는 편이 합리적이다.
대부분 애플리케이션 빌드는 한 번만 하고, 애플리케이션을 실행할 때 설정 정보를 외부에서 넣어 주는 방식을 사용한다.
외부 설정으로는 크게 4가지 방법이 있다.
1. OS 환경 변수 : 운영체제에서 제공하고, 해당 운영체제를 쓰는 모든 프로세스에서 사용된다.
System.getenv() 메서드로 Map을 반환받고 OS가 제공하는 환경 변수를 출력할 수 있다.
데이터베이스 접근 URL을 OS의 환경 변수에 설정해두고 애플리케이션 실행 시 읽어들이는 방식을 사용할 수 있다.
2. 자바 시스템 속성 : 자바에서 제공하고 해당 JVM 내부에서 사용된다.
java -Durl=dev -jar app.jar
-D VM 옵션을 줘서 키-값 형식으로 url-dev 속성을 추가했다.
값을 받아올 때는 System.getProperties() 메서드를 통해 받아온다.
애플리케이션 구동 환경에 따라서 다른 속성을 줘서 구분지을 수 있다.
3. 자바 커맨드 라인 인수 : 커맨드 라인에서 사용되고 main(String[] args) 의 args로 사용된다.
java -jar app.jar data1 data2
애플리케이션이 실행될 때 args로 매개변수를 전달하는 방식이다.
시스템 속성을 사용하는 것과 비슷하지만, 키-값 형식으로 입력되지 않는데... 스프링 표준 방식으로 커맨드 라인 옵션 인수를 사용하면 키-값 형식으로 입력할 수 있다.
ApplicationArguments 인터페이스와 그 구현체를 사용해 args를 다루고 args를 입력할 때는 --url=urlvalue 형식으로 입력한다.
자바 표준이 아니고 스프링 표준 방식이고, args를 입력할 때 -- 를 붙여야 함에 주의하자.
ApplicationArguments는 스프링 빈으로 등록되니 args로 들어온 값들을 애플리케이션 내부에서 사용할 수 있다.
4. 외부 파일 : 외부에 파일을 하나 두고 프로그램에서 외부 파일을 읽어서 사용하도록 한다.
개발 서버와 운영 서버에 각각 외부 파일 (properties, yml) 을 작성하고 스프링이 해당 파일을 읽는 방식이다. (내부에 있는 파일이 아니다)
원래는 개발자가 외부 파일을 스프링이 읽을 수 있도록 설정 해 줘야 하지만, 부트는 이 부분도 자동화한다.
개발자는 자바를 실행하는 위치에 외부 파일을 넣어 주기만 하면 스프링이 해당 파일을 알아서 읽는다.
설정 파일을 외부에 두게 되면 설정 파일에 수정사항이 발생했을 때 서버의 개수만큼 수정사항을 반영해야 해 관리하기 힘들다.
스프링은 내부에서 설정 파일을 관리하는 기능도 제공한다. (가장 많이 사용하는 기능이다)
애플리케이션 소스코드 내부에 개발 / 운영 / 로컬 전용 설정 파일을 포함시키면 설정 파일을 관리하기 훨씬 쉬워진다.
설정 파일을 구분하기 위해서 외부에서 프로필을 넣어주고, 프로필을 바탕으로 어떤 설정 파일을 적용할 지 구분한다. (spring.profiles.active 를 외부에서 입력해준다)
url=def.db.com
username=def_user
password=def_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
위의 예시처럼 하나의 파일에 여러 profile에 대한 설정을 집어넣을 수 있다. (구분은 #--- 으로, YAML파일도 가능하다)
jar를 실행할 때 프로필을 지정하지 않으면 default 프로필로 지정된다.
스프링은 설정 파일을 읽을 때 위에서 아래로 읽고, 프로필 관련 정보가 지정되지 않은 설정 정보는 기본값으로 사용된다. (위의 예시에서는 최상단에 위치한 부분이고, 프로필 정보가 없는 설정 정보는 최상단이든 말든 상관없이 기본값으로 사용된다)
설정 정보로 지정된 부분과 기본값으로 사용되는 부분에서 충돌이 발생할 경우 아래 부분이 우선된다. (위에서 아래)
@Profile 애너테이션으로 설정 정보에 따라 다른 빈을 등록하는 식으로 프로그래밍 할 수 있으니 필요 시 사용하자.
외부 설정은 모두 키-값 형식으로 입력되지만 외부 설정을 어떻게 진행했는지에 따라서 읽어내는 방식이 다르다.
스프링 부트는 Environment와 PropertySource 객체를 사용해 외부 설정을 추상화한다.
애플리케이션이 실행 될 때 필요한 PropertySource를 생성하고 Environment에서 사용할 수 있도록 연결해둔다.
Environment 객체로 특정 외부 설정에 종속되지 않은 상태로 일관성 있게 키-값 형태로 설정 정보를 읽어온다.
즉, 개발자들은 외부 설정이 어디서 정의되어있든 상관하지 않고 Environment를 통해 설정 정보를 읽어올 수 있다. (추상화)
설정 값이 중복되는 경우 더 유연하고 범위가 좁은 설정 값이 우선적으로 선택된다. (스프링 공식 문서 참고)
@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");
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);
}
}
DataSource를 만들 때 Environment 객체를 통해 외부 설정값을 가져오는 예시이다.
Environment 객체를 주입받아서 사용하는데... 좀 귀찮다.
@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:12}") 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를 주입받지 않고 애너테이션 기반으로 외부 설정 값을 가져올 수 있다.
(내부적으로는 Environment 객체를 사용하고, 파라미터에도 사용할 수 있다)
max-connection:12 로 설정 시 기본값으로 12가 주입된다.
스프링은 외부 설정 정보를 객체를 통해 다루는 기능도 제공한다.
@Data
@ConfigurationProperties("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;
private List<String> options = new ArrayList<>();
}
}
///
my.datasource.url=local.db.com
my.datasource.username=username
my.datasource.password=password
my.datasource.etc.max-connection=1
my.datasource.etc.timeout=3500ms
my.datasource.etc.options=CACHE,ADMIN
@EnableConfigurationProperties(MyDataSourcePropertiesV1.class)
public class MyDataSourceConfig {
private final MyDataSourceProperties properties;
public MyDataSourceConfig(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());
}
}
아래와 같은 설정 파일을 위와 같이 @ConfigurationProperties 애너테이션을 사용해 자바 객체로 다룰 수 있다.
etc 계층은 따로 클래스를 작성해서 분리한다.
@EnableConfigurationProperties 애너테이션에 해당 클래스를 지정해 줘야 스프링 빈으로 등록되고 @ConfigurationProperties로 작성한 외부 설정 객체를 사용할 수 있다.
(빈으로 등록할 때는 생성자가 있다면 생성자를, 없다면 setter를 사용한다.
생성자가 하나인 경우 @ConstructorBinding 애너테이션을 생략해도 된다)
@EnableConfigurationProperties 애너테이션을 사용하는 대신 @ComponentScan 처럼 @ConfigurationPropertiesScan 애너테이션을 사용해 @Configuration @Componet 애너테이션을 스캔하듯 @ConfigurationProperties 애너테이션으로 작성한 외부 설정 값을 쉽게 사용할 수 있다.
@ConfigurationProperties로 만든 설정은 자바 객체이기에 자바 빈 검증기를 사용할 수 있다.
설정 클래스에 @Valid 애너테이션과 다른 애너테이션을 적절히 사용해 값을 넣어줄 때 검증할 수 있다.
객체로 값을 저장해 타입 안정성을 보장하고, 값을 검증할 수 있어 외부 설정값을 안전하게 사용할 수 있다. (내부적으로 @Condition이 사용된다)
'Spring > Spring Boot' 카테고리의 다른 글
[Spring Boot] 자동 구성 (0) | 2023.05.26 |
---|---|
[Spring Boot] 라이브러리의 선택과 버전 관리 (0) | 2023.05.23 |
[Spring Boot] 부트와 내장 WAS (0) | 2023.05.22 |
[Spring Boot] 스프링과 웹 서버 (0) | 2023.05.21 |
[Spring Boot] 스프링 부트의 도입 (0) | 2023.05.14 |
댓글
이 글 공유하기
다른 글
-
[Spring Boot] 자동 구성
[Spring Boot] 자동 구성
2023.05.26 -
[Spring Boot] 라이브러리의 선택과 버전 관리
[Spring Boot] 라이브러리의 선택과 버전 관리
2023.05.23 -
[Spring Boot] 부트와 내장 WAS
[Spring Boot] 부트와 내장 WAS
2023.05.22 -
[Spring Boot] 스프링과 웹 서버
[Spring Boot] 스프링과 웹 서버
2023.05.21