티스토리 뷰
하나의 애플리케이션을 여러 다른 환경에서 사용해야 할 때가 있다. 대표적으로 개발이 잘 진행되고 있는지 내부에서 확인하는 용도의 개발 환경, 그리고 실제 고객에게 서비스하는 운영 환경이 있다.
- 개발 환경: 개발 서버, 개발 DB 사용
- 운영 환경: 운영 서버, 운영 DB 사용
문제는 각각의 환경에 따라서 서로 다른 설정값이 존재한다는 점이다. 예를 들어서 애플리케이션이 개발 DB에 접근하려면 dev.db.com
이라는 url 정보가 필요한데, 운영 DB에 접근하려면 prod.db.com
이라는 서로 다른 url을 사용해야 한다.
이 문제를 해결하기 위한 방법으로는 각 환경에 맞추어 실행 시점에 외부 설정값을 주입하는 방법이 있다.

- 배포 환경과 무관하게 하나의 빌드(여기서는 app.jar를 빌드) 결과물을 만든다. 이 안에는 설정값을 두지 않는다.
- 설정값은 실행 시점에 각 환경에 따라 외부에서 주입한다.
- 개발 서버:
app.jar
를 실행할 때dev.db.com
값을 외부 설정으로 주입한다. - 운영 서버:
app.jar
를 실행할 때prod.db.com
값을 외부 설정으로 주입한다.
- 개발 서버:
이렇게 하면 빌드도 한번만 하면 되고, 개발 버전과 운영 버전의 빌드 결과물이 같기 때문에 개발환경에서 검증되면 운영 환경에서도 믿고 사용할 수 있다. 또한, 이후에 새로운 환경이 추가되어도 별도의 빌드 과정 없이 기존 app.jar
를 사용해서 손쉽게 새로운 환경을 추가할 수 있다.
유지보수하기 좋은 애플리케이션 개발의 가장 기본 원칙은 변하는 것과 변하지 않는 것을 분리하는 것이다.
유지보수하기 좋은 애플리케이션을 개발하는 단순하면서도 중요한 원칙은 변하는 것과 변하지 않는 것을 분리하는 것이다. 각 환경에 따라 변하는 외부 설정값은 분리하고, 변하지 않는 코드와 빌드 결과물은 유지했다. 덕분에 빌드 과정을 줄이고, 환경에 따른 유연성을 확보하게 되었다.
외부 설정

외부 설정은 일반적으로 다음 4가지 방법이 있다.
- OS 환경 변수: OS에서 지원하는 외부 설정, 해당 OS를 사용하는 모든 프로세스에서 사용
- 자바 시스템 속성: 자바에서 지원하는 외부 설정, 해당 JVM안에서 사용
- 자바 커맨드 라인 인수: 커맨드 라인에서 전달하는 외부 설정, 실행시
main(args)
메서드에서 사용 - 외부 파일(설정 데이터): 프로그램에서 외부 파일을 직접 읽어서 사용
- 애플리케이션에서 특정 위치의 파일을 읽도록 해둔다.
- 예) data/hello.txt
- 각 서버마다 해당 파일안에 다른 설정 정보를 남겨둔다.
- 개발 서버
hello.txt
:url=dev.db.com
- 운영 서버
hello.txt
:url=prod.db.com
- 개발 서버
외부 설정 - OS 환경 변수
OS 환경 변수(OS environment variables)는 해당 OS를 사용하는 모든 프로그램에서 읽을 수 있는 설정값으로써, 다른 외부 설정과 비교해서 사용 범위가 가장 넓다.
조회 방법
- 윈도우 OS:
set
- MAC, 리눅스 OS:
printenv
내 MAC에서 printenv
명령을 실행해본 결과, 아래와 같은 OS 환경 변수 값이 출력되었다.

이번에는 애플리케이션에서 OS 환경 변수의 값을 읽어보겠다.
OsEnv
@Slf4j
public class OsEnv {
public static void main(String[] args) {
Map<String, String> envMap = System.getenv();
envMap.keySet()
.forEach(key -> log.info("env {}={}", key, System.getenv(key)));
}
}
System.getenv()
: 전체 OS 환경 변수를 Map 으로 조회할 수 있다.System.getenv(key)
: 특정 OS 환경 변수의 값을 String 으로 조회할 수 있다.
실행 결과

OS 환경 변수를 설정하고, 필요한 곳에서 System.getenv()
를 사용하면 외부 설정을 사용할 수 있다. 이제 데이터베이스 접근 URL과 같은 정보를 OS 환경 변수에 설정해두고 읽어들이면 된다. 예를 들어서 개발 서버에서는 DBURL=dev.db.com
과 같이 설정하고, 운영 서버에서는 DBURL=prod.db.com
와 같이 설정하는 것이다. 이렇게 하면 System.getenv("DBURL")
을 조회할 때 각각 환경에 따라서 서로 다른 값을 읽게 된다.
하지만 OS 환경 변수는 전역 변수 같은 효과가 있기 때문에, 이 프로그램 뿐만 아니라 다른 프로그램에서도 사용할 수 있다. 여러 프로그램에서 사용하는 것이 맞을 때도 있지만, 해당 애플리케이션을 사용하는 자바 프로그램 안에서만 사용되는 외부 설정값을 사용하고 싶을 때도 있다. 다음에는 특정 자바 프로그램안에서 사용하는 외부 설정을 알아보자.
외부 설정 - 자바 시스템 속성
자바 시스템 속성(Java System properties)은 실행한 JVM 안에서 접근 가능한 외부 설정이며, 자바가 내부에서 미리 설정해두고 사용하는 속성들도 있다.
자바 시스템 속성은 다음과 같이 자바 프로그램을 실행할 때 사용한다.
- 예)
java -Durl=dev -jar app.jar
-D
VM 옵션을 통해서key=value
형식을 주면 된다.- 순서에 주의해야 한다.
-D
옵션이-jar
보다 앞에 있다.
JavaSystemProperties
@Slf4j
public class JavaSystemProperties {
public static void main(String[] args) {
Properties properties = System.getProperties();
properties.keySet()
.forEach(key -> log.info("prop {}={}", key, System.getProperty(String.valueOf(key))));
}
}
System.getProperties()
를 사용하면Map
과 유사한(Map
의 자식 타입)key=value
형식의Properties
를 받을 수 있다. 이것을 통해서 모든 자바 시스템 속성을 조회할 수 있다.System.getProperty(key)
를 사용하면 속성값을 조회할 수 있다.
실행 결과

- 자바가 기본으로 제공하는 수 많은 속성들이 추가되어 있는 것을 확인할 수 있다.
- 자바는 내부에서 필요할 때 이런 속성들을 사용하는데, 예를 들어서
file.encoding=UTF-8
를 통해서 기본적인 파일 인코딩 정보 등으로 사용한다.
이번에는 사용자가 직접 정의하는 자바 시스템 속성을 추가해보자.
JavaSystemProperties - url, username, password 시스템 속성 추가
@Slf4j
public class JavaSystemProperties {
public static void main(String[] args) {
Properties properties = System.getProperties();
properties.keySet()
.forEach(key -> log.info("prop {}={}", key, System.getProperty(String.valueOf(key))));
String url = System.getProperty("url");
String username = System.getProperty("username");
String password = System.getProperty("password");
log.info("url={}", url);
log.info("username={}", username);
log.info("password={}", password);
}
}
IDE에서 실행시 VM 옵션 추가

Modify options
를 선택한다.Add VM options
를 선택한다.VM options
에 다음을 추가한다.-Durl=devdb -Dusername=hyuuny -Dpassword=dev_pw
실행 결과

- 실행해보면
-D
옵션을 통해 추가한 자바 시스템 속성들을 확인할 수 있다.
외부 설정 - 커맨드 라인 인수
커맨드 라인 인수(Command line arguments)는 애플리케이션 실행 시점에 외부 설정값을 main(args)
메서드의 args
파라미터로 전달하는 방법이다.
CommandLineV1
@Slf4j
public class CommandLineV1 {
public static void main(String[] args) {
for (String arg : args) {
log.info("arg {}", arg);
}
}
}
IDE에서 실행시 커맨드 라인 인수 추가

- 여기에
url=devdb username=hyuuny password=dev_pw
를 입력하고 실행하자. - 커맨드 라인 인수는 공백(space)으로 구분한다.
Program arguments
가 보이지 않는다면 바로 위에 있는 파란색의Modify options
버튼을 눌러서 추가할 수 있다.
실행 결과

실행 결과를 보면 알겠지만 커맨드 라인 인수는 key=value
형식이 아니다. 애플리케이션을 개발할 때는 보통 key=value
형식으로 데이터를 받는 것이 편리하지만, 위 방법은 파싱되지 않은 통 문자가 출력되는 것을 확인할 수 있다. 이 경우 개발자가 =
을 기준으로 직접 데이터를 파싱해서 key=value
형식에 맞도록 분리해야 한다. 그리고 형식이 배열이기 때문에 루프를 돌면서 원하는 데이터를 찾아야 하는 번거로움도 발생한다. 실제 애플리케이션을 개발할 때는 주로 key=value
형식을 자주 사용하기 때문에 결국 파싱해서 Map
같은 형식으로 변환하도록 직접 개발해야 하는 번거로움이 있다.
외부 설정 - 커맨드 라인 옵션 인수
일반적인 커맨드 라인 인수
커맨드 라인에 전달하는 값은 형식이 없고, 단순히 띄어쓰기로 구분한다.
aaa bbb
->[aaa, bbb]
값 2개hello world
->[hello, world]
값 2개"hello world"
->[hello world]
(공백을 연결하려면"
를 사용하면 된다.) 값 1개key=value
->[key=value]
값 1개
커맨드 라인 옵션 인수(command line option arguments)
스프링에서는 커맨드 라인 인수를 key=value
형식으로 편리하게 사용할 수 있도록 스프링 만의 표준 방식을 정의했는데, 그것이 바로 커맨드 라인 옵션 인수이다.
스프링은 커맨드 라인에 dash 2개(--
)를 연결해서 시작하면 key=value
형식으로 정하고, 이것을 커맨드 라인 옵션 인수라 한다.
--key=value
형식으로 사용한다.--username=userA --username=userB
하나의 키에 여러 값도 지정할 수 있다.
CommandLineV2
@Slf4j
public class CommandLineV2 {
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("OptionNames={}", appArgs.getOptionNames());
appArgs.getOptionNames()
.forEach(name -> log.info("option arg {}={}", name, appArgs.getOptionValues(name)));
List<String> url = appArgs.getOptionValues("url");
List<String> username = appArgs.getOptionValues("username");
List<String> password = appArgs.getOptionValues("password");
log.info("url={}", url);
log.info("username={}", username);
log.info("password={}", password);
}
}
스프링이 제공하는 ApplicationArguments
인터페이스와 DefaultApplicationArguments
구현체를 사용하면 커맨드 라인 옵션 인수를 규격대로 파싱해서 편리하게 사용할 수 있다.
실행 결과

arg
: 커맨드 라인의 입력 결과를 그대로 출력한다.SourceArgs
: 커맨드 라인 인수 전부를 출력한다.OptionNames = [password, url, username]
:key=value
형식으로 사용되는 옵션 인수다.--
를 앞에 사용했다.
참고
- 옵션 인수는 하나의 키에 여러 값을 포함할 수 있기 때문에
appArgs.getOptionValues(key)
의 결과는 리스트(List
)를 반환한다. - 커맨드 라인 옵션 인수는 자바 언어의 표준 기능이 아니라 스프링이 편리함을 위해 제공하는 기능이다.
Reference
김영한. 스프링 부트 - 핵심 원리와 활용. 인프런.
'Spring' 카테고리의 다른 글
[Spring] 외부 설정 방법 (0) | 2023.04.06 |
---|---|
[Spring] 외부설정과 프로필 2 (0) | 2023.04.03 |
[Spring] build.gradle 라이브러리 관리 (0) | 2023.03.13 |
[Spring] 스프링 부트 내장 톰캣 (0) | 2023.03.12 |
[Spring] WAR VS JAR (0) | 2023.03.10 |
- Total
- Today
- Yesterday
- 백준
- 릿코드
- 스프링부트
- 알고리즘
- spring boot
- webflux
- 데이터베이스
- Spring
- 인프런
- 문자열
- MySQL
- 파이썬
- 구현
- leetcode
- 노마드코더
- 스프링 부트
- 스프링
- Algorithm
- kotlin
- 노마드
- 북클럽
- 정렬
- Real MySQL
- 코틀린
- mysql 8.0
- 리팩토링
- 자료구조
- 코테
- 김영한
- 그리디
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |