Baeldung의 이 글을 정리 및 추가 정보를 넣은 글입니다.

1. Overview

  • 이번에는 Spring의 profile에 대해서 집중적으로 다룬다.

  • 관련 내용을 이 글에서 어느정도 다룬 적이 있다. property에 대한 내용인데, 이 글도 property file이랑 관련된 내용이 있기에 참고하면 도움이 많이 될거다.

  • profile은 현재 프로그램을 돌리는 목표 (혹은 프로그램을 돌리는 환경)에 따라 container에 등록하는 bean을 달리해서 애플리케이션에서 필요로 하는 bean만 생성하는 용도로 사용된다. 디버깅 할때만 필요한 bean이나 실제 애플리케이션 돌릴때만 필요한 bean이라든가 등 용도에 따라 몇몇 bean은 필요하거나 없을 수 있는데 이를 조절해서 애플리케이션이 쓸데없는 bean을 만들지 않도록 조정하는 것이다.

  • 이 profile을 어떻게 사용하는지, 구체적으로 뭐가 있는지에 대해 이번에 다룰 것이다.

2. Use @Profile on a Bean

  • 앞에서 언급한 글이랑 용도를 생각해보자. 그러면 이 개념을 제대로 써먹으려면 bean을 특정 profile에 등록해야 한다는 것을 알 수 있다. 각 profile이 activate될 때마다 사용할 bean을 결정하려면 그 profile에 사용할 bean을 profile에 등록해야 하기 때문이다.

  • 이 때 사용하는 것이 @Profile annotation이다. javadocs

  • 사용법은 간단하다. @Component에다가 @Profile을 붙이고, 어떤 profile에 속할지를 괄호 안에다가 넣으면 된다. 참고로 여러개의 profile에 한번에 등록하는 것도 가능한데 이 경우 쉼표로 구별지으면 된다. 밑은 DevDatasourceConfig라는 bean이 dev profile에 속한다는 것을 구현, 해당 profile이 active일 때만 container에 등록하라는 것을 의미한다.

@Component
@Profile("dev")
public class DevDatasourceConfig
  • 반대로 특정 profile이 active하지 '않을 때' IoC container에 등록하라고 지정하는 것도 가능하다. 이 때는 !을 profile 이름 앞에 붙인다. 밑은 DevDatasourceConfig라는 bean이 dev profile이 active하지 않을때만 container에 등록하라는 것을 의미한다.
@Component
@Profile("dev")
public class DevDatasourceConfig

3. Declare Profiles in XML

  • XML에서 profile에 속할 bean들을 정의하는 것도 가능하다. 정확히는 <beans>라는 태그를 활용해야 하는데, 거기에 profile이라는 성분이 있다. 거기에 profile 이름을 작성하면 해당 <beans> 안에 있는 bean들은 전부 그 profile에 속한다는 것을 의미. 이것도 쉼표를 사용해서 여러개의 profile에 bean을 등록하는 것도 가능하다. 앞의 방식이랑 가장 큰 차이점은 한번에 여러개의 bean을 등록할 수 있다는 것. 밑은 dev라는 profile에 DevDatasourceConfig라는 bean이 속해있다는 것을 표기.
<beans profile="dev">
    <bean id="devDatasourceConfig" 
      class="org.baeldung.profiles.DevDatasourceConfig" />
</beans>

4. Set Profiles

  • profile에 bean을 등록한다고 끝나는 것이 아니다. 애플리케이션 실행시 무슨 profile을 사용할 것인지 지정할 수 있어야 한다. 우리가 지금 사용하는 목적과 관련된 profile만 activate시켜야 application에서 그 profile들 안의 bean만 등록, 결국 우리가 필요로 하는 bean만 등록할 것이기 때문이다.

  • 이는 여러가지 방법이 있다.

4.1 Programmatically bia WebApplicationInitializer Interface

  • 먼저 WebApplicationInitializer이라는 인터페이스를 활용해서 activate 할 profile을 지정하는 것이 가능하다...만 javadocs

  • 사실 이 인터페이스는 ServletContextweb.xml로 구성하는 대신 프로그램으로 구성하고 싶을 때 사용되는 인터페이스다. 관련 기능 중에 ServletContext의 profile을 등록하는 기능이 있어서 이 글에서 언급한 것이다.

  • ServletContext에 대해서는 예전 글에 간략히 설명한 적이 있으니 참고.

  • 실제 아키텍처 상으로 ServletContext가 (Web) Application '전체'가 사용하는 context를 의미하는건 아니지만, 결국 servlet들 모두가 공유하는 context라서 추상적으로 Web Application '전체'를 의미하는 context라고 볼 수 있다.

  • Web Application의 실제 동작 구성요소인 servlet이 무슨 bean을 사용할지 지정하는게 목표라면, Application 전체를 포괄하는 ApplicationContext를 건드리기 보다는, 실질적인 Web Application영역과 관련된 ServletContext를 건드리는 것이 맞다. 그래서 servlet 활용 Web Application 개발이 목표라면 이 interface를 활용해서 사용할 profile을 지정하는 것이 이상적이라 볼 수 있다.

  • 밑은 activate할 profile을 dev로 설정하는 과정이다.

@Configuration
public class MyWebApplicationInitializer 
  implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
 
        servletContext.setInitParameter(
          "spring.profiles.active", "dev");
    }
}

참고로 이 WebApplicationInitializer의 탐지는 SpringServletContainerInitializer이라는 녀석이 담당한다. (javadoc) jakarta EE (java EE)에서 제공하는 ServletContainerInitializer을 spring에서 implement한 class다. 우리가 WebApplicationInitializer을 설정하든 안하든 일단 그걸 탐지해서 그걸 기반으로 servlet container을 만들려고 시도, 아예 없는 경우 아무것도 하지 않는 class다.

4.2 Programmatically via ConfigurableEnvironment

  • 아니면 그냥 Environment에다가 activate할 profile을 직접 지정하는 것도 가능하다. 밑은 someProfileConfigurableEnvironmentenv를 활용해 등록하는 과정. 애플리케이션 관련 Environment를 주입받기 위해 @Autowired를 사용하고 있다는 점 유의.
@Autowired
private ConfigurableEnvironment env;
...
env.setActiveProfiles("someProfile");

ConfigurableEnvironmentEnvironment의 하위 interface로, active/default profile을 설정 및 이전 글에서 얘기한 property 설정을 하는 것이 주된 용도인 인터페이스다. 거의 대부분의 Environment 파생 타입들이 이를 implement한다. javadoc

4.3 Context Parameter in web.xml

  • 웹 애플리케이션의 경우, web.xml<context-param>을 활용해가지고 activate할 profile을 지정하는 것이 가능하다.
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/app-config.xml</param-value>
</context-param>
<context-param>
    <param-name>spring.profiles.active</param-name>
    <param-value>dev</param-value>
</context-param>

4.4 JVM System Parameter

  • JVM 시스템 parameter에 activate할 profile을 전달하는 것도 가능하다. 이러면 애플리케이션 시작 후에 자동으로 해당 profile들이 activate 된다. 밑을 애플리케이션 실행시 추가하면 된다.
-Dspring.profiles.active=dev

4.5 Environment Variable

  • spring_profiles_active라는 환경 변수를 activate 할 profile로 설정해가지고 activate 할 profile을 지정하는 것도 가능하다. 단 Unix 환경이어야 한다.
export spring_profiles_active=dev

4.6 Maven Profile

  • 이건 별로 권장하지 않는 방법인데, maven profile을 활용하는 것도 있다.

  • maven은 build 용도로 쓰이는게 아니냐고 할 수 있는데... 뭐 맞다. 다만 profile 관련 기능도 제공을 할 뿐. 즉 저 제목은 정확히 말하면 maven의 profile 기능을 사용해서 등록할 bean을 지정하는 것이다.

  • 이 방식을 사용할 경우 activate 할 profile이 다르면 그때마다 build를 새로 해야 한다. 그러면 비효율적이라 볼 수 있지만, 다양한 profile에 대해서 구별지어 배포를 하고 싶은 경우에는 유용할 수 있다는 점 참고.

  • maven profile에 대한 더 자세한 내용은 이 글 참고

  • 여기서는 이걸 조금 희한하게 사용했다. 먼저 pom.xml<profiles>이라는 영역을 만들어야 한다. 밑은 devprod라는 profile을 명시하는 부분이다.

<profiles>
    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <spring.profiles.active>dev</spring.profiles.active>
        </properties>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <spring.profiles.active>prod</spring.profiles.active>
        </properties>
    </profile>
</profiles>
  • 여기서 구현한 방식이 좀 웃긴게 뭐냐면, 원래 <profile> 다음의 <id>에 profile의 이름을 지정하는건 맞다. 그 다음에 해당 profile에서만 사용할 요소들을 지정해야 하는데 여기서는 spring.profiles.active라는 요소를 지정하고, 거기에 본인의 이름을 그대로 넣었다. 이게 어디에 사용되는지는 후술.

activeByDefault를 통해 추후 소개할 명시가 없으면 dev를 사용하도록 설정한것도 유의.

  • 이후 application.properties에 밑의 항목을 넣는다. 여기서 @...@${...}와 똑같은 placeholder 역할이다.
spring.profiles.active=@spring.profiles.active@
  • 목표는 저 placeholder에 그냥 profile의 이름이 그대로 들어가는 것이다. 어떻게 들어가냐고요? 먼저 pom.xml에 다음과 같은 부분을 넣어 resource filtering을 true로 설정한다. 저걸 설정해야 @...@ placeholder이 작동하기 때문. directory를 저렇게 설정한 이유는 application.propertiessrc/main/resources에 들어가 있기 때문이라는 점 참고.
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
    ...
</build>
  • 이후 다음과 같은 명령어를 쓰면 prod를 active profile로 설정해서 application을 packaging할 것이다. 이 때 해당 profile에 spring.profiles.active라는 성분에 profile 이름이 들어가 있으니 placeholder에도 profile의 이름, 즉 prod가 들어가게 된다. -P option은 profile의 약자로 pom.xml-P랑 같이 붙어있는 profile과 관련된 부분만 사용하겠다는 것을 명시.
mvn clean package -Pprod
  • 즉 이 글에서는 maven profile이 단순히 spring.profiles.active에서 사용할 값을 지정하는 용도로만 쓰인 것이다. 효과적이긴 하나 굳이 maven까지 써가면서 이래야 하냐라고 물어보면 잘 모르겠다. profile 바꿀 때마다 maven을 다시 실행해야 하기도 하고.

  • Spring 외부와 관련된 profile 관련 요소를 넣으려고 한다면 이 방식을 쓰는것도 의미가 있지 않을까 싶다.

4.7 @ActiveProfile in Tests

  • 테스트 한정으로는 저 annotation을 사용해 무슨 profile을 activate할지 설정이 가능하다. 저 annotation의 정확한 역할은 ApplicationContext가 테스트 용도를 위해 로드 될 때 어떤 profile을 사용할건지 지정하는 것이다.

  • 밑은 dev profile을 사용하겠다고 지정하는 것. 보통 @ContextConfiguration 위치에 같이 사용한다. 자세한건 이 documentation 내용 참고

@ActiveProfiles("dev")

priority of profile activating

  • 앞의 여러 profile activate 방식들을 우선순위가 높은것부터 나열해보면
    • 1순위 : web.xml에서 activate한 profile
    • 2순위 : WebApplicationInitializer에서 activate 한 profile
    • 3순위 : JVM system parameter로 activate한 profile
    • 4순위 : 환경 변수
    • 5순위 : maven profile

5. The Default Profile

  • 어떤 profile에 들어간다고 명시하지 '않은' bean들은 모두 default profile에 해당한다.

  • 아무 profile이 적용되지 않으면 activate하는 profile인데... 만약 아무 profile이 적용되지 않을 때 적용하는 profile을 바꾸고 싶으면 spring.profiles.default값을 설정하면 된다.

6. Get Active Profiles

  • 현재 active한 profile을 획득하는 것도 가능하다.

6.1 Using Environment

  • 먼저 Environment object의 getActiveProfiles() method를 활용하는 방법이 있다.
public class ProfileManager {
    @Autowired
    private Environment environment;

    public void getActiveProfiles() {
        for (String profileName : environment.getActiveProfiles()) {
            System.out.println("Currently active profile - " + profileName);
        }  
    }
}

6.2 Using spring.profiles.active

  • 그리고 active한 profile을 저장하는 property도 있는데, 이것을 @Value를 활용해 취득 후 저장하는 것도 가능하다. 이 때 active한 profile이 여러개라면 쉼표로 여러개의 profile을 나타내는 String 문자열 하나가 저장이 된다. ("dev, prod")
@Value("${spring.profiles.active}")
private String activeProfile;
  • 그러나... 첫번째 예제랑 다르게 여기서 고려해야 하는 것이 하나 더 있는데 active한 profile이 없는 경우다. 첫번째 예제는 그냥 for-loop를 탈출하겠지만 위의 경우에는 @Value에서 관련 property를 찾지 못하게 되어 오류를 뱉게 된다. 그래서 default value를 설정해야 할 필요가 있음. 글에서는 빈 문자열로 설정했다.
@Value("${spring.profiles.active:}")
private String activeProfile;
  • List형태로 저장하고 싶으면 Stringsplit method를 사용하면 된다.
public class ProfileManager {
    @Value("${spring.profiles.active:}")
    private String activeProfiles;

    public String getActiveProfiles() {
        for (String profileName : activeProfiles.split(",")) {
            System.out.println("Currently active profile - " + profileName);
        }
    }
}

7. Example: Separate Data Source Configurations Using Profiles

  • 자, application에서 DB 접근을 하기 전에 관련 datasource 환경 설정을 하는 일은 흔하다. 이 때 devprod에서 수행해야할 Datasource 관련 환경 설정이 다르다고 해보자.

  • 그러면 dev 관련 환경설정 bean이 prod때 존재할 이유는 없다. 반대도 마찬가지. 그래서 사용하는것만 bean을 만들고 다른건 만들지 않아야 하는데 이를 profile로 관리해보도록 하자.

  • 먼저 datasource 환경 설정 관련 기본 틀을 만들도록 하자.

public interface DatasourceConfig {
    public void setup();
}
  • 이후 devprod환경에서의 환경설정 관련 bean을 하나씩 만들도록 하자. 그리고 각각을 devprod profile에 등록.
@Component
@Profile("dev")
public class DevDatasourceConfig implements DatasourceConfig {
    @Override
    public void setup() {
        System.out.println("Setting up datasource for DEV environment. ");
    }
}
@Component
@Profile("production")
public class ProductionDatasourceConfig implements DatasourceConfig {
    @Override
    public void setup() {
       System.out.println("Setting up datasource for PRODUCTION environment. ");
    }
}
  • 이러면 끝. 테스트해보고 싶으면 밑의 class(bean)도 집어넣자. datasourceConfig에 현 profile 관련 환경설정 bean이 들어가게 될 것이다.
public class SpringProfilesWithMavenPropertiesIntegrationTest {
    @Autowired
    DatasourceConfig datasourceConfig;

    public void setupDatasource() {
        datasourceConfig.setup();
    }
}

8. Profiles in Spring Boot

  • Spring Boot만의 추가적인 profile 관련 기능이 잇다.

8.1 Activating or Setting a Profile

  • 먼저 좀 사소한 차이점들. spring.profiles.activeapplication.properties에서 설정해가지고 active profile을 바꾸는 것이 가능하다.

  • 다만 2.4부터는 저걸 쓰지 말고, spring.config.activate.on-profile을 사용하도록 하자...라고 했지만 사실 간단한 경우면 그냥 위에처럼 사용해도 되고 뒤의 복잡한 경우에 spring.config.activate.on-profile을 사용하면 된다. 둘의 혼용은 안된다.

  • 프로그램 차원으로는 SpringApplication이라는 class를 활용해서 activate할 profile 설정이 가능하다. 그런데 글에 있는 코드에서 사용한 method 자체가 맞긴 한데... 이거 static method가 아니라 instance method다. 그런데 instance를 주입받고 사용한게 아니라서 틀린것 같다.

SpringApplication.setAdditionalProfiles("dev");

한가지 더 덧붙이자면, 위는 active profile을 말그대로 추가하는 것이지 제거하거나 바꾸지는 않는다.

  • Maven을 써도 된다. maven profile을 쓰는건 아니고 spring-boot-maven-plugin을 활용한 방식이다.
<plugins>
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <profiles>
                <profile>dev</profile>
            </profiles>
        </configuration>
    </plugin>
    ...
</plugins>
mvn spring-boot:run

8.2 Profile-specific Properties File

  • 앞은 사실 별볼일 없는 기능이고 이 부분이 중요하다. profile별로 사용할 property를 다르게 설정하는게 가능하다.

  • 각 profile별로 사용할 application.propertiesapplication-{profileName}.properties로 설정이 가능하다. 예를들면 application-dev.properties라든가.

  • 2.4 이전에서는 profile-specific document에서 profile을 activate시키는 것이 가능했지만 이제는 불가능하다는 점 유의. 일반 application.properties에서 profile을 activate 시켜야 한다. 아니면 뭐 다른 방식으로 하든가.

8.3 Multi-Document Files

  • 이전 글에서 다뤘던 내용이다. 하나의 application.properties파일에 각기 다른 profile에서 사용할 property를 설정하는 것을 가능하게 해준다. 자세한건 이전 글을 참고.
my.prop=used-always-in-all-profiles
#---
spring.config.activate.on-profile=dev
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db
spring.datasource.username=root
spring.datasource.password=root
#---
spring.config.activate.on-profile=production
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa

8.4 Profile Groups

  • 2.4에서 제공하는 추가 기능. 비슷한 profile들기리 묶어주는 기능이다. spring.profiles.group를 활용.

  • 예를 들어 production이라는 profile과 연관된 proddb, prodquartz라는 profile이 있다고 해보자. 저렇게 분리한 이유는 가독성 및 모듈성을 위해서일 확률이 높다. 그러면 저 셋을 묶고 싶으면 다음과 같이 하면 된다.

spring.profiles.group.production=proddb,prodquartz
  • 이러면 production이 activate되면 저 둘도 같이 activate된다. proddbprodquartz가 activate된다고 나머지가 activate되는 것은 아니라는 점 참고.
profile
안 흔하고 싶은 개발자. 관심 분야 : 임베디드/컴퓨터 시스템 및 아키텍처/웹/AI

0개의 댓글