자바/스프링 부트

외부설정과 프로필1

UroJem 2023. 3. 23. 08:26

외부설정이란

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

  • 개발환경: 개발 서버, 개발 DB사용. 개발시 팀내 각 작업본을 취합해서 프로그램이 잘 실행되는지 확인하는 환경
  • 운영환경: 운영서버, 운영 DB 사용. 실제로 서비스를 런칭하여 운영중인 환경
  • 로컬환경: 개발자 개인 컴퓨터에서 프로그램 개발시 작업하는 환경

각각의 환경에 따라 서로 다른 설정값을 가지는데  DB 접속정보나 로그 레벨 등을 환경별로 설정값을 다르게 줄 수 있다.

방법은 각각 환경에 맞게 애플리케이션을 빌드하는 것이다.

  • 개발 환경에는 dev.db.com이 필요하므로 이 값을 애플리케이션 코드에 넣은 다음 빌드해서 개발app.jar를 만든다.
  • 운영 환경에는 prod.db.com이 필요하므로 이 값을 애플리케이션 코드에 넣은 다음 빌드해서 운영.app.jar를 만든다.

이렇게 하면 각 환경에 맞는 개발app.jar, 운영app.jar가 만들어지므로 해당 파일들을 각 환경별로 배포하면 된다.

하지만 이것은 좋은 방법이 아닌 유연성이 떨어지는 방법인데

  • 환경에 따라서 빌드를 여러번 해야 한다.
  • 개발 버전과 운영 버전의 빌드 결과물이 다르다. 각각 환경에 맞는 jar로 빌드했기 때문에 같은 jar파일을 배포하는게 아니어서 개발 환경에서 검증이 되더라도 운영 환경에서 다른 빌드 결과를 사용하기 떄문에 예상치 못한 문제가 발생할 수 있다. 개발용 빌드가 끝나고 검증한 다음 운영용 빌드를 해야 하는데 그 사이 누군가 다른 코드로 변경하게 되고 깃이나 소스트리같은 버전 관리 시스템에서 변경코드가 적용 된지 모르고 빌드하여 배포될 가능성이 있기 때문에 진짜 같은 소스코드에서 나온 결과물인지 검증하기 어렵다.
  • 각 환경에 맞추어 최종 빌드가 되어 나온 빌드 결과물은 다른 환경에서 사용할 수 없어서 유연성이 떨어지고 추후 다른 환경이 추가되면 그 환경에 맞는 설정값으로 빌드를 해야하는 번거로움이 있다.

 

요즘은 빌드는 한번만 하고 각 환경에 맞추어 실행 시점에 외부 설정값을 주입한다.

  • 배포 환경과 무관하게 하나의 빌드 결과물을 만든다. 여기서는 app.jar를 빌드한다. 이 안에는 설정값을 두지 않는다.
  • 설정값은 실행 시점에 각 환경에 따라 외부에서 주입한다.
    • 개발 서버: app.jar를 실행할 때 dev.db.com 값을 외부 설정으로 주입한다.
    • 운영 서버: app.jar를 실행할 때 prod.db.com 값을 외부 설정으로 주입한다.

이렇게 하면 빌드도 한번만 하면 되고, 개발 버전과 운영 버전의 빌드 결과물이 같기 때문에 개발환경에서 검증되면 운영 환경에서도 믿고 사용할 수 있다.

그리고 그 이후에 새로운 환경이 추가되어도 별도의 빌드 과정 없이 기존 app.jar를 사용해서 손쉽게 새로운 환경을 추가할 수 있다.

 

유지보수하기 좋은 애플리케이션 개발의 가장 기본 원칙은 변하는 것과 변하지 않는 것을 분리하는 것이다.

각 환경에 따라 변하는 외부 설정값은 분리하고, 변하지 않는 코드와 빌드 결과물은 유지했다. 덕분에 빌드 과정을 줄이고, 환경에 따른 유연성을 확보하게 되었다.

 

외부 설정

애플리케이션을 실행할 때 필요한 설정값을 외부에서 어떻게 불러와서 애플리케이션에 전달할 수 있을까?

외부 설정은 일반적으로 다음 4가지 방법이 있다.

  • OS 환경 변수: OS에서 지원하는 외부 설정, 해당 OS를 사용하는 모든 프로세스에서 사용. 보통 JAVA_HOME 잡을 때 설정한 고급 시스템 설정의 환경변수의 시스템 변수 설정하는 부분
  • 자바 시스템 속성: 자바에서 지원하는 외부 설정, 해당 JVM 안에서 사용
  • 자바 커맨드 라인 인수: 커맨드 라인에서 전달하는 외부 설정, 실행시 main(args) 메서드에서 사용
  • 외부 파일(설정 데이터): 프로그램에서 외부 파일을 직접 읽어서 사용
    • 애플리케이션에서 특정 위치의 파일을 읽도록 해둔다. 예)서버 디렉토리 어딘가를 지정하여 data/hello.txt
    • 각 서버마다 해당 파일 안에 다른 설정 정보를 남겨둔다.
      • 개발서버 hello.txt 파일에 db접속정보 url=dev.db.com 작성한다.
      • 운영서버 hello.txt 파일에 db접속정보 url=prod.db.com 작성한다.

 

외부설정 - OS 환경 변수

OS 환경 변수(OS environment variables)는 해당 OS를 사용하는 모든 프로그램에서 읽을 수 있는 설정값이다.

한마디로 다른 외부 설정과 비교해서 사용 범위가 가장 넓다.

 

조회 방법

윈도우 OS: set

MAC, 리눅스 OS: printenv

 

커맨드 창에서 set 치면 OS에 설정된 OS 환겨 변수 값들을 모두 확인할 수 있다.

 

이 OS 환경 변수 값을 애플리케이션에서 알아오는 방법

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보다 앞에 있다.

System.getProperties()를 사용하면 Map과 유사한(Map의 자식 타입) key=value 형식의 Properties를 받을 수 있다. 

이것을 통해서 모든 자바 시스템 속성을 조회할 수 있다.

System.getProperty(key)를 사용하면 속성값을 조회할 수 있다.

자바가 기본으로 제공하는 수 많은 속성들이 추가되어 있는 것을 확인할 수 있다. 자바는 내부에서 필요할 때 이런 속성들을 사용하는데 예를 들어 user.timezone=Asia/Seoul을 통해 현재 사용중인 나라의 시간으로 오늘날짜를 받을 수 있다.

 

 

JavaSystemProperties 사용자 정의 추가

정의할 url, username, password를 출력하는 코드를 추가 후

실행 설정에서 Add VM options로 속성을 추가한다.

-Durl=devdb -Dusername=dev_user -Dpassword=dev_password

사용자가 정의한 속성들을 확인할 수 있다.

 

 

Jar 실행

jar로 빌드되어 있다면 실행시 다음과 같이 자바 시스템 속성을 추가할 수 있다.

java -Durl=devdb -Dusername=dev_user -Dpassword=dev_pw -jar app.jar

 

자바 시스템 속성을 자바 코드로 설정하기

자바 시스템 속성은 앞서 본 것 처럼 -D 옵션을 통해 실행 시점에 전달하는 것도 가능하고, 다음과 같이 자바 코드 내부에서 추가하는 것도 가능하다. 

코드에서 추가하면 이후에 조회시에 값을 조회할 수 있다.

  • 설정: System.setProperty(propertyName, "propertyValue")
  • 조회: System.getProperty(propertyName)

이 방식은 코드 안에서 사용하는 것이기 때문에 외부로 설정을 분리하는 효과는 없다.

 

외부 설정 - 커맨드 라인 인수

커맨드 라인 인수(Command Line arguments)는 애플리케이션 실행 시점에 외부 설정값을 main(args)메서드의 args 파라미터로 전달하는 방법이다.

  • 예 java -jar app.jar dataA dataB
  • 필요한 데이터를 마지막 위치에 스페이스로 구분해서 전달하면 된다. 이 경우 dataA, dataB 2개의 문자가 args에 전달된다.

CommandLineV1

메인메서드에서 받은 커맨드 라인 인수를 출력하는 반복문을 만들고

실행 설정에서 커맨드라인 인수 값을 띄어쓰기를 CSV로 넣어서 구분해준다.

 

Jar 실행

Jar로 빌드되어 있다면 실행시 다음과 같이 커맨드 라인 인수를 추가할 수 있다.

java -jar project.jar dataA dataB

 

key=value 형식 입력

그러나 커맨드 라인 인수는 key=value 형식이 아닌 띄어쓰기로 구분한 value 값만 있다. 

애플리케이션 개발할 때는 보통 key=value 형식으로 데이터를 받는게 편리하다.

url=devdb username=dev_user password=dev_pw

실행결과를 보자면 커맨드 라인 인수는 key=value 형식이 아니다. 단순히 문자를 여러개 받는 형식인 것.

그래서 3가지 문자가 입력되었다.

이 경우 개발자가 '=' 을 기준으로 직접 데이터를 파싱해서 key=value 형식에 맞도록 분리해야한다.

배열로 받는 값이기 때문에 루푸를 돌면서 원하는 데이터를 찾아야 하는 번거로움도 발생한다.

실제 애필리케이션 개발시 파싱하여 Map 같은 형식으로 변환하도록 직접 개발해야 한다.

 

 

외부 설정 - 커맨드 라인 옵션 인수

스프링 부트는 커맨드 라인을 포함해서 커맨드 라인 옵션 인수를 활용할 수 있는 ApplicationArguments를 스프링 빈으로 등록해둔다. 그리고 그 안에 입력한 커맨드 라인을 저장해둔다. 그래서 해당 빈을 주입 받으면 커맨드 라인으로 입력한 값을 어디서든 사용할 수 있다.

 

ApplicationArguments : SpringApplication을 실행할 때 program arguments에 대해 추상화된 액세스 기능을 제공하는 인터페이스이다. 특정 argument가 존재하는지 확인하거나 argument 리스트 가져오는 기능 등등 제공

 

스프링부트가 실행될 때 ApplicationArguments를 제공해주는데 CommandLineBean이 컴포넌트 스캔 대상으로 생성이 되면 디폴트 생성자가 없고  ApplicationArguments 인자를 받는 생성자로 생성이 되며 해당 빈을 주입받는다. 주입되는 대상은 ApplicationArguments의 구현체인 DefaultApplicationArguments 객체를 주입 받는다.

 

@PostConstruct : 종속성 주입이 완료된 후 실행되어야 하는 메서드에 사용된다. 이 어노테이션은 다른 리소스에서 호출되지 않아도 수행된다. CommangLineBean이 생성된 후 arguments 적용된 값을 확인해보기 위해 사용되었다.

 

실행 설정창에서 Program arguments로 속성값을 준다.

입력한 커맨드라인 인수, 커맨드라인 옵션 인수를 확인할 수 있다.

 

스프링 부트가 뜰 때 main 메서드가 실행되고 SpringApplication.run(ExternalApplication.class, args); 해당 메서드실행시 스프링이 여러개의 빈을 등록하는데 그 중 ApplicationArguments도 등록이 되어서 등록된 빈이 주입이 되는 것.

어느 위치에서든지 ApplicationArguments를 주입받아 쓰면 입력된 arguments를 확인하는 기능을 사용할 수 있다.

 

 

외부 설정 - 스프링 통합

지금까지 살펴본 커맨드 라인 옵션 인수, 자바 시스템 속성, OS 환경변수는 모두 외부 설정을 key=value 형식으로 사용할 수 있는 방법이다. 그런데 이 외부 설정값을 읽어서 사용하는 개발자 입장에서 단순하게 생각해보면, 모두 key=value 형식이고, 설정값을 외부로 뽑아둔 것이다.

그런데 어디에 있는 외부 설정값을 읽어야 하는지에 따라서 각각 읽는 방법이 다르다는 단점이 있다.

예를 들어 OS 환경 변수에 두면 System.getenv(key) 를 사용해야 하고, 자바 시스템 속성을 사용하면 System.getProperty(key)를 사용해야 한다. 만약 OS에 환경 변수를 두었는데, 이후에 정책이 변경되어서 자바 시스템 속성에 환경 변수를 두기로 했다고 가정해보자. 그러면 해당 코드들을 모두 변경해야한다.

 

외부 설정값이 어디에 위치하든 상관없이 일관성 있고, 편리하게 key=value 형식의 외부 설정값을 읽을 수 있으면 사용하는 개발자 입장에서 더 편리하고 또 외부 설정값을 설정하는 방법도 더 유연해질 수 있다. 예를 들어서 외부 설정값을 OS 환경변수를 사용하다가 자바 시스템 속성으로 변경하는 경우에 소스코드를 다시 빌드하지 않고 그대로 사용할 수 있다.

 

스프링은 이 문제를 Environment와 PropertySource라는 추상화를 통해서 해결한다.

PropertySource

  • org.springframework.core.env.PropertySource
  • 스프링은 PropertySource라는 key=value 형식으로 읽을 수 있는 추상 클래스(Environment)를 제공하고, 커맨드 라인 옵션인수, 자바 시스템 속성, OS 환경변수, 설정 데이터 등 외부 설정을 조회하는 XxxPropertySource 구현체를 만들어두었다.
    • 예) CommandLinePropertySource, SystemEnvironmentPropertySource
  • 스프링은 로딩 시점에 필요한 PropertySource들을 생성하고 Environment에서 사용할 수 있게 연결해둔다.

Environment

  • org.springframework.core.env.Environment
  • 개발자는 Environment를 통해서 특정 외부 설정에 종속되지 않고, 일관성 있게 key=value 형식의 외부 설정에 접근할 수 있다.
    • environment.getProperty(key)를 통해서 값을 조회할 수 있다.
    • Environment는 내부에서 여러 과정을 거쳐서 PropertySource들에 접근한다.
    • 같은 값이 있을 경우를 대비해서 스프링은 미리 우선순위를 정해두었다.
  • 모든 외부 설정은 이제 Environment를 통해서 조회하면 된다.

설정 데이터(파일)

여기에 우리가 잘 아는 application.properties, application.yml도 PropertySource에 추가되어 Ebnviroment를 통해 접근할 수 있다.

 

EnvironmentCheck

커맨드 라인 옵션 인수로 설정된 값들 가져옴

코드 변경 없이 자바 시스템 속성인 VM 옵션으로 설정한 값들 가져옴

 

커맨드 라인 옵션 인수, 자바 시스템 속성 모두 Enviroment를 통해서 동일한 방법으로 읽을 수 있는 것을 확인했다.

스프링은 Environment를 통해서 외부 설정을 읽는 방법을 추상화했다. 덕분에 자바 시스템 속성을 사용하다가 만약 커맨드라인 옵션 인수를 사용하도록 읽는 방법이 변경되어도 개발 소스 코드는 전혀 변경하지 않아도 된다.

 

어떻게? 클라이언트가 설정값을 조회하면 스프링에서 제공해주는 Environment가 PropertySource을 우선순위에 맞춰서 뒤지다 설정값을 찾으면 해당 value를 반환해준다.

 

우선순위

예를 들어서 커맨드 라인 옵션 인수와 자바 시스템 속성을 다음과 같이 중복 설정하면?

  • 커맨드 라인 옵션 인수 실행
    • --url=proddb --username=prod_user --password=prod_pw
  • 자바 시스템 속성 실행
    • -Durl=devdb -Dusername=deb_user -Dpassword=deb_pw

커맨드 라인 옵션 인수가 출력된다.

우선 순위는 상식 선에서 딱 2가지만 기억하면 된다.

  • 더 유연한 것이 우선권을 가진다.(변경하기 어려운 파일 보다 실행시 원하는 값을 줄 수 있는 자바 시스템 속성이 더 우선권을 가진다.)
  • 범위가 넓은 것 보다 좁은 것이 우선권을 가진다.(자바 시스템 속성은 해당 JVM 안에서 모두 접근할 수 있다. 반면에 커맨드라인 옵션 인수는 main의 args를 통해서 들어오기 때문에 접근 범위가 더 좁다.)

 

자바 시스템 속성과 커내드 라인 옵션 인수의 경우 커맨드 라인 옵션 인수의 범위가 더 좁기 때문에 커맨드 라인 옵션 인수가 우선권을 가진다.

 

 

설정 데이터1 - 외부 파일

OS 환경 변수, 자바 시스템 속성, 커맨드라인 옵션인수는 실무에서는 수십개의 설정값을 사용하므로 프로그램 실행 때마다 입력해야 되면 번거롭고 관리도 어렵다.

 

그래서 등장한 대안으로 설정값을 파일에 넣어서 관리하는 방법이다.

그리고 애플리케이션 로딩 시점에 해당 파일을 읽어들이면 된다. 그중에서도 .properties 파일은 key=value 형식을 사용해서 설정값을 관리하기에 아주 적합하다.

application.properties 파일을 jar안에 있는 것을 사용하는게 아닌 각각 개발서버, 운영서버에 같은 경로에 넣어두면 애플리케이션 로딩시 해당 파일을 읽어서 그 속에 있는 값들을 외부 설정값으로 사용하면 된다.

파일 이름이 같으므로 애플리케이션 코드는 그대로 유지할 수 있다.

 

스프링과 설정 데이터

개발자가 파일을 읽어서 설정값으로 사용할 수 있도록 개발을 해야하지만 스프링 부트에서 이미 다 구현해놨다.

개발자는 application.properties 라는 이름의 파일을 자바를 실행하는 위치에 만들어두기만 하면 해당 파일을 읽어서 사용할 수 있는 PropertySource의 구현체를 제공한다. 스프링에서는 이러한 application.properties 파일을 설정 데이터(Config data)라 한다. 설정 데이터도 Enviroment를 통해서 조회할 수 있다.

*application.properties 대신 yml 형식의 application.yml에도 동일하게 적용되는 내용들이다.

*지금부터 나오는 내용은 어플리케이션 내부에 있는 application.property 파일을 말하는 것이 아닌 외부에 둘 파일을 말하는 것이다.

 

동작확인

  1. ./gradlew clean build
  2. cd ./build/libs/
  3. 해당 위치에 application.properties 파일 생성
  4. java -jar external-0.0.1-SNAPSHOT.jar 실행

파일 만든 후 노트패드에서 정보 입력 후 저장
프로젝트/build/libs 폴더에 application.properties 파일 생성

username이 설정한 값과 다르다 아마 다른 곳의 우선순위에 이미 설정된 key 값이어서 해당 값이 출력되는 듯 하다.

 

소스코드 외부 설정값을 username 이 아닌 name으로 변경 후 다시 빌드하고 application.properties 파일 key 값을 변경하여 실행해 보았다.

원래는 개발자가 파일을 놓을 경로, 읽어들일 파일명을 지정하여 PropertySource로 만들어 Environment 객체로 불러다 쓸 수 있게 만들어야 하지만, 스프링에서 build/libs 하위에 있는 application.properties 파일이 있으면 해당 객체를 읽어서 Environment 객체를 통해 받아올 수 있게 만들어 놓았다.

 

남은 문제

  • 외부 설정을 별도의 파일로 관리하게 되면 설정 파일 자체를 관리하기 번거로운 문제가 생긴다.
  • 서버가 10대면 변경 사항이 있을 때 10대 서버의 설정 파일을 모두 각각 변경해야 하는 불편함이 있다.
  • 설정 파일이 별도로 관리되기 때문에 설정값의 이력을 확인하기 어렵다. 특히 설정값의 변경 이력이 프로젝트 코드들과 어떻게 영향을 주고 받는지 그 이력을 확인하기 어렵다.

 

설정 데이터2 - 내부 파일 분리

설정 파일을 외부에 관리하면 설정을 변경할 때마다 서버에 들어가서 각각 변경사항을 수정해야해서 상당히 번거로울 수 있다.

 

설정파일을 프로젝트 내부에 포함해서 관리하게되면 깃으로 이력도 남겨지고 빌드 시점에 함께 빌드되어 배포만 하면 애플리케이션 배포할 때 설정파일의 변경사항도 함께 배포되어 편리하게 관리할 수 있다.

jar 하나로 설정 데이터까지 포함해서 관리하는 것이다.

  1. 프로젝트 안에 소스 코드 뿐만 아니라 각 환경에 필요한 설정 데이터도 함께 포함해서 관리한다.
    • 개발용 설정 파일: application-dev.properties
    • 운영용 설정 파일: application-prod.properties
  2. 빌드 시점에 개발, 운영 설정 파일을 모두 포함해서 빌드한다.
  3. app.jar는 개발, 운영 두 설정 파일을 모두 가지고 배포된다.
  4. 실행할 때 어떤 설정 데이터를 읽어야 할지 최소한의 구분은 필요하다.
    • 개발 환경이라면 application-dev.properties 를 읽어야 하고,
    • 운영 환경이라면 application-prod.properties 를 읽어야 한다.
    • 실행할 때 외부 설정을 사용해서 개발 서버는 dev라는 값을 제공하고 운영 서버는 prod 라는 값을 제공하자. 편의상 이 값을 프로필이라 하자.
      • dev 프로필이 넘어오면 applicaiton-dev.properties 를 읽어서 사용하고
      • prod 프로필이 넘어오면 application-prod.properties 를 읽어서 사용한다.

외부 설정으로 넘어온 프로필 값이 dev라면 application-dev.properties를 읽고 prod라면 application-prod.properties를 읽어서 사용하면 된다. 스프링은 이미 설정 데이터를 내부에 파일로 분리해두고 외부 설정값(프로필)에 따라 각각 다른 파일을 읽는 방법을 다 구현해두었다.

 

2개의 파일을 resources 밑에 만든다

 

프로필

스프링은 이런 곳에서 사용하기 위해 프로필이라는 개념을 지원한다.

spring.profiles.active 외부 설정에 값을 넣으면 해당 프로필을 사용한다고 판단한다.

그리고 프로필에 따라서 다음과 같은 규칙으로 해당 프로필에 맞는 내부 파일(설정 데이터)을 조회한다.

  • application-{profile}.properties
  • spring.profiles.active=dev
    • dev 프로필이 활성화되면 application-dev.properties 를 설정 데이터로 사용
  • spring.profiles.active=prod
    • prod 프로필이 활성화 되면 application-prod.properties 를 설정 데이터로 사용

실행

  • IDE에서 커맨드 라인 옵션 인수 실행
    • --spring.profiles.active=dev
  • IDE에서 시스템 속성 실행
    • -Dspring.profiles.active=dev
  • Jar 실행
    • ./gradlew clean build
    • cd /build/libs
    • java -Dspring.profiles.active=dev -jar external-0.0.1-SNAPSHOT.jar
    • java -jar external-0.0.1-SNAPSHOT.jar --spring.profiles.active-dev

커맨드라인 옵션 인수 실행
자바 시스템 속성 실행

 

터미널에서

java -Dspring.profiles.active=dev -jar ./external-0.0.1-SNAPSHOT.jar 는 에러가나고

java -jar ./external-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev 는 프로필 설정이 잘 된다.

으로 실행이 안된다 ClassNotFoundException이 나는데 왜.. 알 수 없다.

자바 시스템 속성을 준게 . 접근 연산자로 클래스를 찾는 것 같은데..

Error: Could not find or load main class .profiles.active=dev
Caused by: java.lang.ClassNotFoundException: /profiles/active=dev

해결 방법은 아니지만 인텔리제이 터미널에서 PowerShell을 사용 했는데 Git Bash로 변경하니 자바 시스템 변수로 설정한 자바 실행이 잘 된다.

해결

같이 스터디하는 능력자분이 찾아주신 window 명령창에는 -부분이 명령어로 인식하니 자바시스템 속성을 ""로 감싸서 실행하면 된다.

 

 

설정 데이터3 - 내부 파일 합체

설정 파일을 각각 분리해서 관리하면 한눈에 전체가 들어오지 않는 단점이 있는데 스프링은 이런 단점을 보완하기 위해 물리적인 하나의 파일 안에서 논리적으로 영역을 구분하는 방법을 제공한다.

  • 기존에는 dev 환경은 application-dev.properties, prod 환경은 application-prod.properties 파일이 필요했다.
  • 스프링은 하나의 application.properties 파일 안에서 논리적으로 영역을 구분하는 방법을 제공한다.
    • application.properties 구분 방법은 #--- 또는 !---
    • application.yml 구분 방법은 ---
  • 그림의 오른쪽 application.properties는 하나의 파일이지만 내부에 2개의 논리 문서로 구분되어 있다.
    • dev 프로필이 활성화 되면 상위 설정 데이터 사용
    • prod 프로필이 활성화 되면 하위 설정 데이터 사용
  • 프로필에 따라 논리적으로 구분된 설정 데이터를 활성화 하는 방법
    • spring.config.activate.on-profile에 프로필 값 지정

 

설정 데이터 하나의 파일로 통합

기존 내용은 # 으로 주석 처리하고 application.properties에다 합친다.

  • 속성 파일 구분 기호에는 선행 공백이 없어야 하며 정확히 3개의 하이픈 문자가 있어야 한다.
  • 구분 기호 바로 앞과 뒤의 줄은 같은 주석 접두사가 아니어야 한다.

 

실행

  • 커맨드 라인 옵션 인수 실행
    • --spring.profiles.active=dev
  • 자바 시스템 속성 실행
    • -Dspring.profiles.active=dev
  • Jar 실행
    • ./gradlew clean build
    • cd ./build/libs
    • java -Dspring.profiles.active=dev -jar ./external-0.0.1-SNAPSHOT.jar
    • java -jar ./external-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev

 

우선순위 - 설정 데이터

만약 프로필을 적용하지 않는다면 default 프로필이 적용이 되는데 defualt로 선언된 프로필 설정이 없으니 PropertySource 값들은 null이 찍히게 된다.

 

기본값

내 PC에서 개발하는 것을 보통 로컬(local) 개발 환경이라 한다. 이 때도 항상 프로필을 지정하면서 실행하지 않고 기본값으로 지정하여 사용하는 것이 덜 번거롭다.

스프링은 문서를 위에서 아래로 순서대로 읽으면서 설정한다.

처음에 나오는 논리문서는 spring.config.activate.on-profile 과 같은 프로필 정보가 없어서 프로필과 무관하게 설정 데이터를 읽어서 사용한다.

이렇게 프로필 지정과 무관하게 사용되는 것을 기본값이라 한다.

실행 결과 특정 프로필이 없기 때문에 기본값이 사용 된다.

 

프로필을 dev로 지정하고 실행한다면 당연히 해당 프로필의 정보를 사용하는데 이는 application.properties를 처음부터 읽으면서 기본 설정된 값으로 세팅 되지만 지정된 프로필의 문서를 만나면 해당 프로필로 적용이 되어 우선권을 가지게 된다.

그 이후 prod 프로필 설정 정보를 읽으며 지정된 프로필 값과 다르니 해당 프로필 정보는 무시하게 되는 것.

 

설정 데이터 적용 순서

스프링은 문서를 읽을 때 절차지향 방식으로 위에서 아래로 순서대로 읽으며 사용할 값을 설정한다.

  1. 스프링은 순서상 위에 있는 local 관련 논리 문서의 데이터들을 읽어서 설정한다. 커맨드 라인 옵션이나 자바 시스템 속성과 같은 설정 값을 지정하지 않았기 때문에 프로필과 무관하게 항상  값을 사용하도록 설정한다.
  2. 그 다음의 dev 논리 문서를 읽는데 만약 dev 프로필이 설정 되어있다면 기존 데이터를 dev 관련 논리 문서 값으로 대체 한다. dev 프로필 설정하지 않으면 dev 관련 논리문서는 무시되고 그 값도 사용하지 않는다.
  3. 스프링은 그 다음 순서로 prod 관련 논리 문서를 읽는데 만약 prod 프로필이 설정되어 있다면 기존 데이터를 prod 관련 논리 문서의값으로 대체한다. prod 프로필을 설정하지 않으면 prod 관련 논리문서는 무시되고 그 값도 사용하지 않는다.

참고로 프로필을 둘 이상 설정하는 것도 가능하다

--spring.profiles.active=dev,prod

 

정리

  • 단순하게 문서를 위에서 아래로 읽으면서 값을 설정한다. 이 때 기존 데이터가 있으면 덮어쓴다.
  • 논리 문서에 spring.config.activate.on-profile 옵션이 있으면 해당 프로필을 사용할 때만 논리 문서를 적용한다.

 

속성 부분 적용

만약 프로필에서 일부 내용만 교체하게 되면?

프로필 dev 일 때
프로필 prod 일 때

기본적으로 처음 입력된 설정 부분을 저장하고 각 프로필 설정에 따른 부분들로 설정이 덮어씌인 것을 알 수 있다. 

기본적인 셋팅을 기본 프로필이 적용되는 곳에 선언하고 중복적인 것들은 다른 프로필에 작성할 필요 없이 변경되야 할 부분만 각 프로필에 작성해주면 일단 기본설정된 값을 가져온 후 별도로 선언된 설정값이 적용되어 중복적인 설정을 피할 수 있다.

 

 

우선순위 - 전체

외부설정 우선순위 스프링 공식 문서

https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config

 

Core Features

Spring Boot lets you externalize your configuration so that you can work with the same application code in different environments. You can use a variety of external configuration sources including Java properties files, YAML files, environment variables, a

docs.spring.io

우선순위는 위에서 아래로 적용된다. 아래가 더 우선순위가 높다.

 

자주 사용하는 우선순위

  • 설정 데이터 (application.properties)
  • OS 환경 변수
  • 자바 시스템 속성
  • 커맨드 라인 옵션 인수
  • @TestPropertySource (테스트에서 사용)

설정 데이터 우선순위

  • jar 내부 application.properties
  • jar 내부 프로필 적용 파일 application-{profile}.properties
  • jar 외부 application.properties
  • jar 외부 프로필 적용 파일 application-{profile}.priperties

설정 데이터 우선순위 스프링 공식 문서

https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config.files

 

우선순위는 상식선에서 딱 2가지만 생각하면 된다.

  • 더 유연한 것이 우선권을 가진다. (변경하기 어려운 파일 보다 실행시 원하는 값을 줄 수 있는 자바 시스템 속성이 더 우선권을 가진다.)
  • 범위가 넓은 것보다 좁은 것이 자바 시스템 속성이 우선권이 있다.
    • OS 환경변수 보다 자바 시스템 속성이 우선권이 있다.
    • 자바 시스템 속성 보다 커맨드 라인 옵션 인수가 우선권이 있다.

 

추가 또는 변경되는 방식

Environment를 통해서 조회하는 관점에서 보면 외부 설정값들은 계속 추가되거나 기존 값을 덮어서 변경하는 것 처럼 보인다. 물론 실제 값을 덮어서 변경하는 것은 아니고, 우선순위가 높은 값이 조회되는 것

자바 시스템속성 -Dname=local_user 적용하고 커맨드 라인 옵션 인수를 --url=dev.dv.com 으로 적용했다.

프로필 설정은 하지 않아 default로 적용되고 자바시스템  속성에서 기존에 없던 키값을 추가했기 때문에 name 속성이 추가되었고 커맨드 라인 옵션 인수는 기존에 있던 url이라는 키값을 사용했기 때문에 기존에 있던 값이 새로운 값으로 변경되었다.

 

정리

우선순위에 따라 설정을 추가하거나 변경하는 방식은 상당히 편리하면서도 유연한 구조를 만들어준다.

실무에서 개발자들은 application.properties에 외부 설정값들을 보관한다. 이렇게 설정 데이터를 기본으로 사용하다가 일부 속성을 변경할 필요가 있다면 더 높은 우선순위를 가지는 자바 시스템 속성이나 커맨드 라인 옵션 인수를 사용하면 되는 것이다.

또는 기본적으로 application.properties를 jar 내부에 내장하고 있다가 특별한 환경에서는 application.properties 를 외부 파일로 새로 만들고 변경하고 싶은 일부 속성만 입력해서 변경하는 것도 가능하다.

 

 

 

 

 

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%ED%95%B5%EC%8B%AC%EC%9B%90%EB%A6%AC-%ED%99%9C%EC%9A%A9/dashboard