오늘은 Application을 여러가지 환경에 배포하기 위한 Profile설정에 대해서 정리한다.

먼저 여러 가지 환경이라는 것은 하나의 Application을 한국에 배포할 때, 일본에 배포할 때, Containerize하여 배포할 때 등등의 환경을 말한다 (간단하게 Region이라고 칭함).
각각의 Region에서는 서로 다른 외부|내부 의존성을 가질 수 있으며 그러기 위해서는 Region별로 Profile 분리 하여 빌드, 실행 할때 알맞는 의존성을 가지도록 설정해야 한다.

예시)

  • 각각의 Region에서 서로 다른 Database를 사용함.
  • 각각의 Region에서 Api 호출에 대한 권한 데이터 확인을 서로 다른 방식을 사용한다.(File base, DB base, Memory base 등등)


Profile은 두가지가 존재한다.

  1. Build 타임에 동작하는 Build(Gradle, Maven) Profile.
  2. Run 타임에 동작하는 Spring Profile.


Build(Gradle, Maven) Profile

Build Profile은 말그대로 빌드시 적용되는 프로파일로 어떤 리소스를 빌드에 포함시킬지 결정한다.
예를 들어 Spring 프로젝트에 아래와 같이 디렉토리를 구성한다.

.
├── src
│   ├── main
│   │   ├── java
│   │   ├── resources
│   │   ├── resources-local
│   │   ├── resources-kr
│   │   ├── resources-jp
│   │   ├── resources-container
│   │   ...
│ ...
  • resources 폴더는 spring기본 classpath에 속하며, 빌드완료시 classpath root에 하위 파일들이 놓이게 된다.
  • 추가적으로 resources-{region} 디렉토리를 생성한다.

위와 같이 생성된 디렉토리들을 빌드시 Profile변수에 따라 포함시킨다.

Maven (pom.xml)

...
<profiles>
    <profile>
        <id>local</id>
        <properties>
            <env>local</env>
        </properties>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <profile>
        <id>kr</id>
        <properties>
            <env>kr</env>
        </properties>
    </profile>
    <profile>
        <id>jp</id>
        <properties>
            <env>jp</env>
        </properties>
    </profile>
    <profile>
        <id>container</id>
        <properties>
            <env>container</env>
        </properties>
    </profile>
</profiles>
...
<build>
    <resources>
        <resource>
            <directory>src/main/resources-${env}</directory>
        </resource>
    </resources>
    ...
</build>

위와 같이 설정하고 아래 명령어로 빌드를 시도한다.

mvn clean package -P kr

Gradle (build.gradle.kts - 코틀린 파일로 작성함.)

Gradle kotlin의 경우 project라는 객체가 제공된다 (참고: Project API)
project객체가 가진 hasProperty라는 메소드로 profile변수가 있는지 확인한다.
아래의 코드는 직접 여러가지 profile을 읽을수 있게 kotlin으로 작성된 것이다.

fun getProfile():String {
    return try {
        if (project.hasProperty("profile")) {
            project.property("profile").toString()
        } else {
            "local"
        }
    } catch (e: Exception) {
        "local"
    }
}
val profile = getProfile()
sourceSets {
    main {
        java.srcDirs("src/main/java")
        resources {
            srcDirs(listOf("src/main/resources", "src/main/resources-$profile"))
        }
    }
    ...
}

위처럼 getProfile 함수를 호출한 결과값을 이용하여 sourceSets.main.resources에 포함시키는 과정이다.

위와 같이 설정하고 아래 명령어로 빌드를 시도한다.

gradle clean build -Pprofile=kr

위의 두 예제의 결과는 resource-kr 디렉토리에 있는 리소스들이 빌드시에 포함되어서 빌드된다.

그렇다면 어떤 리소스들을 포함시켜서 빌드하며, 어떤식으로 로드하는지 다음 Spring Profile에서 알아보자.



Spring Profile

Spring Profile은 실제 Spring Application이 구동될때 어떤 resource를 로드할지, 어떤 property를 적용할지에 대한 설정이다.

아래와 같이 구성된 디렉토리가 있을 때

.
├── src
│   ├── main
│   │   ├── java
│   │   ├── resources
│   │   │   ├── application.yml
│   │   ├── resources-kr
│   │   │   ├── application-kr.yml
│   │   │   ├── application-db.yml
│   │   │   ├── application-memory.yml
│   │   │   ...
│   │   ├── resources-jp
│   │   ├── resources-container
│   │   ...
│ ...

위 에서 설명한 Build Profilekr로 설정하여 빌드하게 되면 빌드 결과물엔 아래와 같은 리소스들이 포함된다.

application.yml
application-kr.yml
application-db.yml
application-memory.yml
  1. application.yml 파일은 spring이 기본적으로 로드하는 property파일 이다.
  2. application-***.yml을 로드하기 위해서는 spring.profile 설정이 필요하다.


Spring profile은 여러가지 방법으로 설정이 가능하다.

  1. application.yml에 정의
  2. spring실행시 argument로 전달.

application.yml에 정의

Spring이 기본적으로 로드하는 application.yml 파일에 아래와 같이 profile을 정의하여 추가적인 리소스application-{profile}.yml를 로드할 수 있다.

spring:
  profiles:
    active: kr
    include:
      - db

Spring 실행시 argument로 전달.

profile은 spring 실행시 JAVA_OPTS나 RUN_ARGS로 전달이 가능하다.

  1. RUN_ARGS: --spring.profiles.active=kr,db
  2. JAVA_OPTS: -Dspring.profiles.active=kr,db

위의 두 방법으로 profile을 정의할 시 application.yml(기본), application-kr.yml, application-db.yml이 로드된다.



정리

여기까지의 두 방법 Build Profile, Spring Profile 으로 환경별로 리소스 파일 분리가 가능하다.

여기서 다시 처음에 제시했던 예시들을 돌아보자.

  1. 각각의 Region에서 서로 다른 Database를 사용함.
  2. 각각의 Region에서 Api 호출에 대한 권한 데이터 확인을 서로 다른 방식을 사용한다.(File base, DB base, Memory base 등등)

서로 다른 Database를 사용하는 부분은 파일을 분리하여 간단히 해결이 가능하다.
application.yml

spring:
  profile:
    active: kr

application-kr.yml

db:
  host: 1.1.1.1
  port: 3306
  user: test
  password: test

application-jp.yml

db:
  host: 2.2.2.2
  port: 3306
  user: test1
  password: test1

동일한 property에 대해서 두 파일로 분리하며 각 환경에 맞게 로드하면 된다.



하지만 2번째 예시 `각각의 Region에서 Api 호출에 대한 권한 데이터 확인을 서로 다른 방식을 사용한다.를 해결하기 위해서는 약간 부족하다. 이 예시를 해결하기 위해서는 추상화된 모듈 Bean을 Region별로 다르게 로드하며, 거기에 맞는 Property 로드가 필요하다.

이 내용은 다음 2장에 다루도록 하겠다.

BELATED ARTICLES

more