스프링 부트 - 핵심 원리와 활용 : 외부 설정과 프로필

jkky98·2024년 11월 1일
0

Spring

목록 보기
66/77

외부 설정의 필요성

어플리케이션을 개발할 때에 보통 로컬-개발-테스트-운영 등의 환경이 존재한다. 개발 환경과 운영환경에서 사용하는 DB가 다르므로 각 DB url을 환경마다 다르게 설정해주어야 한다.

각 환경에 맞는 설정을 내부에 포함하여 빌드한 파일로 그에 맞는 서버에서 실행시킬 수 있다.(오래된 방식)
이러한 방식은 환경에 따라 여러번의 빌드가 필요하고, 빌드 결과물이 환경마다 달라지게 된다.

단일jar + 외부설정 정보

빌드 결과물은 동일하게 두고 빌드 결과물이 jar 외부 설정에 대해 다르게 동작하도록 유도한다면 빌드 결과물이 달라지는 문제를 해결할 수 있다.

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

처음 예시가 안좋은 구조인 이유는 변하지 않는 부분인 내부 소스 코드에 변하는 부분인 환경에 따른 약간의 설정변화가 포함되어 있기 때문이다.

외부 설정

OS 환경 변수

OS 환경변수는 해당 OS를 사용하는 모든 프로그램에서 읽을 수 있는 설정값이다. 사용범위가 가장 넓다고 볼 수 있고 전역변수의 효과가 존재하기에 해당OS에서 사용될 하나의 프로그램에 대해 OS 환경 변수로 하여금 애플리케이션 설정 값을 조절하는 것은 주의해야한다.(비추천)

Java 시스템 속성

-D VM 옵션을 통해 key=value을 주어 외부 설정을 줄 수 있다. 아래와 같이 jar실행시 -Dkey=value를 통해 설정정보를 주입할 수 있다.

java -Durl=dev -jar app.jar`

커맨드 라인 인수

public static void main(String[] args) {
        
    }

자바 main메서드를 만들때 항상 파라미터로 args를 받아왔다. 이를 그냥 출력해보면 빈 배열을 나타내지만 다음과 같이 jar를 실행한다면 args에 데이터가 담기게 된다.

java -jar app.jar dataA dataB

다만 이 방식은 기존의 VM option 방식이나 OS환경변수를 가져오는 것과 달리 key=value 형식이 아니기 때문에(통문자로 제공된다.) 우리가 이를 key=value 형식으로 가져오기 위해서는 통 문자를 Map 형식으로 바꾸어주는 코딩이 필요하다.

커맨드 라인 옵션 인수

위와 같은 자료 형식문제 때문에 스프링은 커맨드 라인 옵션 인수라는 표준 방식을 정의한다.

스프링은 커맨드 라인에 - (dash) 2개( -- )를 연결해서 시작하면 key=value 형식으로 정하고 이것을 커맨드 라인 옵션 인수라 한다.

--url=devdb --username=dev_user --password=dev_pw mode=on

위와 같이 커맨드 라인 인수를 입력하고 이를 추출하도록 한다면

@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("NonOptionsArgs = {}", appArgs.getNonOptionArgs());
        log.info("OptionsNames = {}", appArgs.getOptionNames());

        Set<String> optionNames = appArgs.getOptionNames();
        for (String optionName : optionNames) {
            log.info("option arge {}={}", optionName, appArgs.getOptionValues(optionName));
        }

        List<String> url = appArgs.getOptionValues("url");
        List<String> username = appArgs.getOptionValues("username");
        List<String> password = appArgs.getOptionValues("password");
        List<String> mode = appArgs.getOptionValues("mode");

        log.info("url={}", url);
        log.info("username={}", username);
        log.info("password={}", password);
        log.info("mode={}", mode);
    }
}

key=value 형식으로 외부 설정을 읽어올 수 있다.

스프링 통합

외부 설정을 주입하는 방식으로 OS환경변수, VM option, 커맨드 라인 옵션 인수 등이 존재한다. 이들을 추출하는 방법은 모두 제각각이기에 스프링은 이를 통합하여 어떤 방식으로 외부 설정을 주입하더라도 동일하게 추출할 수 있도록 Environment,PropertySource인터페이스를 제공한다.

Environment

@Slf4j
@Component
@RequiredArgsConstructor
public class EnvironmentCheck {

    private final Environment env;

    @PostConstruct
    public void init() {
        String url = env.getProperty("url");
        String username = env.getProperty("username");
        String password = env.getProperty("password");
        String test = env.getProperty("test");

        log.info("env url={}", url);
        log.info("env username={}", username);
        log.info("env password={}", password);
        log.info("env test={}", test);
    }
}

위와 같이 우리는 Environment의 getProperty(key) 메서드를 통해 어떻게 외부 설정을 주입하더라도 동일하게 value를 빼내올 수 있도록 사용할 수 있다.

외부 설정 파일

jar를 생성하고나면 build/libs에 jar파일이 생성된다. jar파일 바로 옆에 application.properties파일을 생성하여 그 안에 설정 정보를 입력한다면 jar 실행시 jar 옆에 존재하는 applicaiton.properties를 읽어 외부 설정을 적용해준다.

물론 Enviroment가 이를 문제없이 읽어낸다.

설정파일을 외부에 관리하는 것은 매우 번거로운 일이다. jar와 함께 항상 같이 설정파일이 존재하는 구조이기 때문에 만약 여러대의 서버에서 설정파일 수정이 일어나야하는 경우 모든 설정파일을 수정해주어야 한다.

내부 파일 분리 + 프로필

위에서 언급한 문제를 해결할 수 있는 방법으로는 jar 내부에 propreties파일에 설정 정보를 나타내는 것이다.

기존에 존재하는 application.properties파일에 다음과 같이 설정정보를 작성할 수 있다.(이 예시는 모든 시나리오를 포함한다.)

url=local.db.com
username=local_user
password=local_pw
#---
spring.config.activate.on-profile=dev
url=dev.db.com
username=dev_user
password=dev_password
#---
spring.config.activate.on-profile=prod
url=prod.db.com
username=prod_user
password=prod_password
#---
spring.config.activate.on-profile=test
url=hello.db.com
test=hello.test

스프링은 이 설정내용을 위에서부터 아래로 읽어나간다. #---로 구분기준을 줄 수 있고 이 형식을 지키도록 하자.

처음에는 spring.config.activate.on-profile=XXX와 같은 줄이 존재하지 않는다. 무조건 적용인 것이다. 외부설정이 존재하던 존재하지 않던 일단 local_XXX들을 적용한다.

그리고 만약 외부 설정정보로 dev가 들어온다면 dev의 내용으로 중복시에는 바꿔치기, 새로운 내용이라면 추가를 한다.

만약 dev에 더불어 prod까지 적용되어있다면 다시 동일한 논리로 덮어쓰기 or 추가를 진행한다.

예로 만약 커맨드라인 옵션 인수를 다음과 같이 주었다고 가정해보자.
이때의 결과를 유추해보자면 prod가 모두 적용된 후 test에서 url이 바꿔치기되고 test가 추가될 것이다.

이렇게 하나의 설정파일을 두고 외부설정변수를 최소화하여 jar안에서 모두 해결할 수 있게 되었다.

많은 외부 설정 변수를 사용하는 방법을 배웠는데 이에 대한 설정 변수의 우선순위가 정해지는 기준이 존재한다.

더 유연한 것이 우선순위를 가지고 범위가 넓은 것보다 좁은 것이 우선권을 가진다. 즉 OS환경변수보다 자바VMoption이 우선권이 있으며 자바VMoption보다 커맨드 라인 옵션 인수가 우선권이 있다.

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

profile
자바집사의 거북이 수련법

0개의 댓글