Spring : 외부 설정이란?
- Spring/Spring
- 2023. 10. 21.
들어가기 전
이 글은 김영한님의 인프런 강의를 복습하며 작성한 글입니다.
외부 설정이란?
- 하나의 어플리케이션을 여러 환경에서 사용해야 할 때가 있음. (개발, Beta, Release, Rc Phase 등)
- 각 환경마다 서로 다른 설정값을 사용해야하는 경우가 많음
어떻게 이 문제를 해결할 수 있을까?
- 가장 단순한 방법 : 각각의 환경에 맞게 어플리케이션을 빌드.
- 이 방법은 좋은 방법이 아님.
- 환경에 따라 빌드를 여러 번 해야함. (빌드 결과물이 다름. A-beta.jar, A-rc.jar )
- 이 경우, 개발 환경에서 검증이 다 되었다고 생각할 수 있지만 A-rc.jar는 새로운 아티팩트이기 때문에 개발과 똑같이 동작하지 않을 수도 있음.
- 즉, 유연성이 떨어지는 방법
- 이 방법은 좋은 방법이 아님.
- 주로 사용하는 방법 : 빌드는 한번만 하고, 필요한 설정값은 외부 설정값으로 주입.
- 하나의 빌드 결과물을 이용하고, 설정값은 실행 시점에 외부 설정값으로 주입한다.
- 같은 빌드 결과물을 사용하기 때문에 운영 환경에서도 믿고 쓸 수 있고, 유연하게 처리할 수 있다.
유지보수하기 좋은 어플리케이션 개발의 기본 원칙은 변하는 것 / 변하지 않는 것을 분리하는 것이다. 각 환경에 따라 변하는 외부 설정값은 분리, 변하지 않는 코드 + 빌드 결과물은 유지하는 방법이다.
외부 설정할 수 있는 방법?
필요한 설정값을 외부에서 어떻게 주입할 수 있을까?
- OS 환경 변수 : OS에서 지원하는 외부 설정. 해당 OS를 사용하는 모든 프로세스에서 함께 사용
- 자바 시스템 속성 : 자바에서 지원하는 외부 설정. 해당 JVM 안에서 사용
- 자바 커맨드 라인 인수 : 커맨드 라인에서 전달하는 외부 설정, 실행 시 'main(args)' 메서드에서 사용
- 외부 파일(설정 데이터): 프로그램에서 외부 파일을 직접 읽어서 사용.
- 어플리케이션에서 특정 위치의 파일을 읽도록 해둔다. (data/hello.txt)
- 그리고 각 서버마다 해당 파일안에 다른 설정 정보를 남겨둔다.
- 개발 서버 : 'hello.txt': 'url=dev.db.com'
- 운영 서버 : 'hello.txt': 'url=prod.db.com'
외부 설정 - OS 환경 변수
- 조회 방법
- 윈도우 : `set`
- Mac, Linux : `printenv`
- 어플리케이션에서 환경 변수를 읽는 방법을 공부해보자.
public static void main(String[] args) {
// 환경변수를 읽을 수 있음.
Map<String, String> envMap = System.getenv();
for (String key : envMap.keySet()) {
log.info("env {}={}", key, System.getenv(key));
}
}
- OS 환경 변수는 다른 프로그램에서도 사용할 수 있는 전역변수 같은 느낌이다.
- 다른 프로그램에서도 동일한 환경 변수에 다른 값을 넣어서 사용할 수도 있다. → 문제 발생함.
- 여러 프로그램에서 같은 환경변수를 사용하는 경우가 있을 수도 있으나, 자바 프로그램 안에서만 사용되는 외부 설정값이 필요할 때도 있다.
외부 설정 - 자바 시스템 속성 → 불러오기 / 설정하기
- 자바 시스템 속성(Java System Properties)는 실행된 JVM 안에서 접근 가능한 외부 설정이다. 추가로 자바가 내부에서 미리 설정해두고 사용하는 속성들도 있다.
- 자바 시스템 속성은 다음 형태로 사용한다.
- 예시 ) java -Durl=dev -jar app.jar
- `-D` VM 옵션을 통해서 key=value 형식으로 자바 시스템 속성을 줄 수 있음.
- -D 옵션은 항상 -jar 옵션보다 앞에 있어야 함.
// 자바 시스템 속성을 코드에서 불러오기
public static void main(String[] args) {
Properties properties = System.getProperties();
for (Object key : properties.keySet()) {
log.info("prop {}={}", key, System.getProperty(String.valueOf(key)));
}
}
- 입력된 자바 System Properties는 System.getProperty()를 이용해 조회할 수 있음.
- 인텔리제이에서 Modify options → add VM Options를 클릭한 후, -Durl 형식으로 자바 시스템 속성을 추가한다.
// 자바 시스템 속성을 코드에서 불러오기
String url = System.getProperty("url");
String username = System.getProperty("username");
String password = System.getProperty("password");
- 이렇게 추가한 -D 옵션은 getProperty()를 이용해 자바 시스템 속성에서 불러올 수 있다.
- Jar로 빌드되어 있는 경우 java -Durl=hello -jar app.jar 같은 형식으로 할 수 있다.
// 코드 내에서 자바 시스템 속성 등록하기
System.setProperty("HelloKey", "HelloValue");
log.info("HelloKey = {}", System.getProperty("HelloKey"));
코드 내에서 자바 시스템 속성을 등록하는 방법도 있다.
외부 설정 - 커맨드 라인 인수 (Key=Value 형식이 안됨)
커맨드 라인 인수는 어플리케이션 실행 시점에 args로 값을 받음.
- 다음과 같이 사용함. java -jar app.jar dataA dataB
- 인텔리제이의 경우 Modify Options → Program Arguments를 이용하면 됨.
// 커맨드라인 옵션 읽어오기.
public static void main(String[] args) {
for (String arg : args) {
log.info("arg = {}", arg);
}
}
- 커맨드 라인 인수는 main() 함수의 args를 읽어오기만 하면 된다.
커맨드 라인 인수 Key=Value 형식 입력받으면?
- 어플리케이션을 개발할 때는 보통 Key=Value 형식으로 하는 것이 편리하다.
- url=dev_db로 실행해보면 값이 통으로 들어오는 것을 볼 수 있다.
- 통으로 들어오는 값을 '='을 기준으로 파싱해서 Map 형태로 바꿔야하는 불편함이 있다.
- 이 불편함을 스프링이 해결해줌.
외부 설정 - 커맨드 라인 옵션인수
- 앞서서 커맨드라인에 key=value 형식으로 값을 주었을 경우, 하나의 문자열로 전달되어서 직접 파싱해야했다.
- 스프링에서는 커맨드라인 인수를 key=value 형식으로 편리하게 사용할 수 있도록 스프링 만의 표준 방식을 정의해서 지원해줌.
- 스프링은 --key=value 형식으로 제공해주면 됨.
- DefaultApplicationArguments 객체를 생성하며, 받은 인수 args를 넘겨주면 됨.
- DefaultApplicationArguments에게 필요한 인수를 가져다 쓰면 됨.
- --는 Optional 인수가 되고, --가 없는 경우는 통문자로 사용됨.
// 프로그램 인수 --url=dev_db --username=dev_user mode=on
public static void main(String[] args) {
// 자바의 기본 기능
for (String arg : args) {
log.info("arg = {}", arg);
}
// 스프링의 커맨드라인 인수 지원 기능
ApplicationArguments appArgs = new DefaultApplicationArguments(args);
log.info("sourceArgs = {}", List.of(appArgs.getSourceArgs()));
// --를 사용하지 않은 경우
log.info("NonOptionArgs = {}", appArgs.getNonOptionArgs());
// 짝대기 없어지고 Key 값만 남음.
Set<String> optionNames = appArgs.getOptionNames();
for (String optionName : optionNames) {
log.info("option arg {}={}", optionName, appArgs.getOptionValues(optionName));
}
}
외부 설정 - 커맨드 라인 옵션인수와 스프링부트
- 스프링부트는 ApplicationArguments(커맨드라인 옵션 인수를 활용할 수 있는 객체)를 스프링 빈으로 등록해둠.
- 따라서 스프링부트 프로젝트에서는 ApplicationArguments 스프링 빈을 주입받으면 어디서든 설정값을 이용할 수 있다.
@Slf4j
@Component
public class CommandLineBean {
private final ApplicationArguments arguments;
public CommandLineBean(ApplicationArguments arguments) {
this.arguments = arguments;
}
@PostConstruct
public void init() {
log.info("source {}", List.of(arguments.getSourceArgs()));
for (String optionName : arguments.getOptionNames()) {
log.info("option Args {}={}", optionName, arguments.getOptionValues(optionName));
}
}
}
>>>>>>>>>>>>>>>
// 옵션 --username=hello --password=ballo
// 스프링부트 동작 시, 로그 확인됨.
hello.CommandLineBean : option Args password=[ballo]
hello.CommandLineBean : option Args username=[hello]
위에서 ApplicationArguments를 주입받아서, 커맨드라인 인수가 정상적으로 주입된 것을 로그로 확인할 수 있다.
외부 설정 - 스프링 통합 (외부 환경 설정 읽는 방법의 추상화)
- 대부분의 외부 환경 설정값은 key=value 형식으로 제공이 된다.
- 그렇지만 OS 환경변수인지, JVM 시스템 속성인지, 커맨드라인 인자인지에 따라서 읽는 방법이 달라진다. 스프링은 이 부분을 추상화시켜준다.
- 시스템 OS : System.getEnv()
- JVM : System.getProperty()
- 커맨드라인 : args
- 어느 순간 시스템 OS에서 값을 읽어오다가, 커맨드라인에서 값을 읽어오도록 정책이 바뀌는 경우가 있을 수 있다. 그럴 때 마다 외부설정 값을 읽어오는 코드를 모두 수정해야하는 불상사가 일어난다.
- 이런 문제를 해결하기 위해 스프링은 Environment, PropertySource라는 추상화를 통해서 해결한다.
PropertySource
- 이 녀석은 추상 클래스다.
- 커맨드라인 옵션 인수, 자바 시스템 속성, OS 환경 변수, 설정 데이터 파일을 읽을 수 있는 PropertySource 추상 클래스를 구현한 클래스를 제공한다.
- CommandLinePropertySource
- 스프링은 로딩 시점에 필요한 PropertySource 들을 생성하고 Environment에서 사용할 수 있게 연결해둔다.
- 설정데이터 파일 (application.properties, application.yaml)도 PropertySource에 추가된다.
Environment
- Environment를 통해서 특정 외부 설정에 종속되지 않고, 일관성있게 'Key=Value' 형식으로 외부 설정에 접근할 수 있게 해줌.
- Environment는 내부에서 여러 과정을 거쳐서 PropertySource들에 접근함.
- 같은 값이 있을 경우를 대비해 스프링은 미리 우선순위를 정해둠.
- 우선순위는 상식 선에서 딱 2가지만 기억하면 됨.
- 더 유연한 것이 우선권을 가진다. (변경하기 어려운 파일보다 실행 시 원하는 값을 줄 수 있는 자바 시스템 속성이 우선권을 가짐)
- 범위가 넓은 것보다 좁은 것이 우선권을 가짐. (자바 시스템 속성은 해당 JVM 안에서 모두 접근할 수 있음. 반면에 커맨드 라인 온셥 인수는 main의 arg를 통해서 들어오기 때문에 접근 범위가 더 좁다)
- 커맨드 라인옵션 인수 > 자바 시스템 속성 인수
- 우선순위는 상식 선에서 딱 2가지만 기억하면 됨.
- 모든 외부 설정은 이제 Environment를 통해서 조회하면 된다.
@Slf4j
@Component
@RequiredArgsConstructor
public class EnvironmentCheck {
private final Environment env;
@PostConstruct
public void init() {
// 아래 환경 인수를 다 읽음.
// --url=dev --username=myuser --password=hello
// -Durl=dev -Dusername=myuser -Dpassword=Hello
log.info("url = {}, username = {}, password = {}",
env.getProperty("url"),
env.getProperty("username"),
env.getProperty("password"));
}
}
예시 코드는 위와 같다.
'Spring > Spring' 카테고리의 다른 글
스프링 부트 - 자동 구성 관련 요약 (0) | 2023.09.17 |
---|---|
스프링 부트 - 자동구성 4 (0) | 2023.09.17 |
스프링 부트 - 자동구성 2 (0) | 2023.09.17 |
스프링 부트 - 자동구성 3 (2) | 2023.09.17 |
스프링 부트 - 자동구성 1 (0) | 2023.09.14 |