티스토리 뷰

Spring

[Spring] 외부설정과 프로필 1

hyuuny 2023. 4. 2. 19:27

하나의 애플리케이션을 여러 다른 환경에서 사용해야 할 때가 있다. 대표적으로 개발이 잘 진행되고 있는지 내부에서 확인하는 용도의 개발 환경, 그리고 실제 고객에게 서비스하는 운영 환경이 있다.

  • 개발 환경: 개발 서버, 개발 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 옵션 추가

  1. Modify options를 선택한다.
  2. Add VM options를 선택한다.
  3. 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
링크
«   2025/04   »
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
글 보관함