이번에는 Spring의 profile에 대해서 집중적으로 다룬다.
관련 내용을 이 글에서 어느정도 다룬 적이 있다. property에 대한 내용인데, 이 글도 property file이랑 관련된 내용이 있기에 참고하면 도움이 많이 될거다.
profile은 현재 프로그램을 돌리는 목표 (혹은 프로그램을 돌리는 환경)에 따라 container에 등록하는 bean을 달리해서 애플리케이션에서 필요로 하는 bean만 생성하는 용도로 사용된다. 디버깅 할때만 필요한 bean이나 실제 애플리케이션 돌릴때만 필요한 bean이라든가 등 용도에 따라 몇몇 bean은 필요하거나 없을 수 있는데 이를 조절해서 애플리케이션이 쓸데없는 bean을 만들지 않도록 조정하는 것이다.
이 profile을 어떻게 사용하는지, 구체적으로 뭐가 있는지에 대해 이번에 다룰 것이다.
@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 이름 앞에 붙인다. 밑은 DevDatasourceConfig
라는 bean이 dev
profile이 active하지 않을때만 container에 등록하라는 것을 의미한다.@Component
@Profile("dev")
public class DevDatasourceConfig
<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>
profile에 bean을 등록한다고 끝나는 것이 아니다. 애플리케이션 실행시 무슨 profile을 사용할 것인지 지정할 수 있어야 한다. 우리가 지금 사용하는 목적과 관련된 profile만 activate시켜야 application에서 그 profile들 안의 bean만 등록, 결국 우리가 필요로 하는 bean만 등록할 것이기 때문이다.
이는 여러가지 방법이 있다.
WebApplicationInitializer
Interface먼저 WebApplicationInitializer
이라는 인터페이스를 활용해서 activate 할 profile을 지정하는 것이 가능하다...만 javadocs
사실 이 인터페이스는 ServletContext
를 web.xml
로 구성하는 대신 프로그램으로 구성하고 싶을 때 사용되는 인터페이스다. 관련 기능 중에 ServletContext
의 profile을 등록하는 기능이 있어서 이 글에서 언급한 것이다.
실제 아키텍처 상으로 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다.
ConfigurableEnvironment
Environment
에다가 activate할 profile을 직접 지정하는 것도 가능하다. 밑은 someProfile
을 ConfigurableEnvironment
인 env
를 활용해 등록하는 과정. 애플리케이션 관련 Environment
를 주입받기 위해 @Autowired
를 사용하고 있다는 점 유의.@Autowired
private ConfigurableEnvironment env;
...
env.setActiveProfiles("someProfile");
ConfigurableEnvironment
는Environment
의 하위 interface로, active/default profile을 설정 및 이전 글에서 얘기한 property 설정을 하는 것이 주된 용도인 인터페이스다. 거의 대부분의Environment
파생 타입들이 이를 implement한다. javadoc
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>
-Dspring.profiles.active=dev
spring_profiles_active
라는 환경 변수를 activate 할 profile로 설정해가지고 activate 할 profile을 지정하는 것도 가능하다. 단 Unix 환경이어야 한다.export spring_profiles_active=dev
이건 별로 권장하지 않는 방법인데, maven profile을 활용하는 것도 있다.
maven은 build 용도로 쓰이는게 아니냐고 할 수 있는데... 뭐 맞다. 다만 profile 관련 기능도 제공을 할 뿐. 즉 저 제목은 정확히 말하면 maven의 profile 기능을 사용해서 등록할 bean을 지정하는 것이다.
이 방식을 사용할 경우 activate 할 profile이 다르면 그때마다 build를 새로 해야 한다. 그러면 비효율적이라 볼 수 있지만, 다양한 profile에 대해서 구별지어 배포를 하고 싶은 경우에는 유용할 수 있다는 점 참고.
여기서는 이걸 조금 희한하게 사용했다. 먼저 pom.xml
에 <profiles>
이라는 영역을 만들어야 한다. 밑은 dev
랑 prod
라는 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@
pom.xml
에 다음과 같은 부분을 넣어 resource filtering을 true로 설정한다. 저걸 설정해야 @...@
placeholder이 작동하기 때문. directory를 저렇게 설정한 이유는 application.properties
가 src/main/resources
에 들어가 있기 때문이라는 점 참고.<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
...
</build>
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 관련 요소를 넣으려고 한다면 이 방식을 쓰는것도 의미가 있지 않을까 싶다.
@ActiveProfile
in Tests테스트 한정으로는 저 annotation을 사용해 무슨 profile을 activate할지 설정이 가능하다. 저 annotation의 정확한 역할은 ApplicationContext
가 테스트 용도를 위해 로드 될 때 어떤 profile을 사용할건지 지정하는 것이다.
밑은 dev
profile을 사용하겠다고 지정하는 것. 보통 @ContextConfiguration
위치에 같이 사용한다. 자세한건 이 documentation 내용 참고
@ActiveProfiles("dev")
web.xml
에서 activate한 profileWebApplicationInitializer
에서 activate 한 profile어떤 profile에 들어간다고 명시하지 '않은' bean들은 모두 default profile에 해당한다.
아무 profile이 적용되지 않으면 activate하는 profile인데... 만약 아무 profile이 적용되지 않을 때 적용하는 profile을 바꾸고 싶으면 spring.profiles.default
값을 설정하면 된다.
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);
}
}
}
@Value
를 활용해 취득 후 저장하는 것도 가능하다. 이 때 active한 profile이 여러개라면 쉼표로 여러개의 profile을 나타내는 String
문자열 하나가 저장이 된다. ("dev, prod"
)@Value("${spring.profiles.active}")
private String activeProfile;
@Value
에서 관련 property를 찾지 못하게 되어 오류를 뱉게 된다. 그래서 default value를 설정해야 할 필요가 있음. 글에서는 빈 문자열로 설정했다.@Value("${spring.profiles.active:}")
private String activeProfile;
List
형태로 저장하고 싶으면 String
의 split
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);
}
}
}
자, application에서 DB 접근을 하기 전에 관련 datasource 환경 설정을 하는 일은 흔하다. 이 때 dev
랑 prod
에서 수행해야할 Datasource 관련 환경 설정이 다르다고 해보자.
그러면 dev
관련 환경설정 bean이 prod
때 존재할 이유는 없다. 반대도 마찬가지. 그래서 사용하는것만 bean을 만들고 다른건 만들지 않아야 하는데 이를 profile로 관리해보도록 하자.
먼저 datasource 환경 설정 관련 기본 틀을 만들도록 하자.
public interface DatasourceConfig {
public void setup();
}
dev
랑 prod
환경에서의 환경설정 관련 bean을 하나씩 만들도록 하자. 그리고 각각을 dev
랑 prod
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. ");
}
}
datasourceConfig
에 현 profile 관련 환경설정 bean이 들어가게 될 것이다.public class SpringProfilesWithMavenPropertiesIntegrationTest {
@Autowired
DatasourceConfig datasourceConfig;
public void setupDatasource() {
datasourceConfig.setup();
}
}
먼저 좀 사소한 차이점들. spring.profiles.active
를 application.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을 말그대로 추가하는 것이지 제거하거나 바꾸지는 않는다.
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
앞은 사실 별볼일 없는 기능이고 이 부분이 중요하다. profile별로 사용할 property를 다르게 설정하는게 가능하다.
각 profile별로 사용할 application.properties
을 application-{profileName}.properties
로 설정이 가능하다. 예를들면 application-dev.properties
라든가.
2.4 이전에서는 profile-specific document에서 profile을 activate시키는 것이 가능했지만 이제는 불가능하다는 점 유의. 일반 application.properties
에서 profile을 activate 시켜야 한다. 아니면 뭐 다른 방식으로 하든가.
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
2.4에서 제공하는 추가 기능. 비슷한 profile들기리 묶어주는 기능이다. spring.profiles.group
를 활용.
예를 들어 production
이라는 profile과 연관된 proddb
, prodquartz
라는 profile이 있다고 해보자. 저렇게 분리한 이유는 가독성 및 모듈성을 위해서일 확률이 높다. 그러면 저 셋을 묶고 싶으면 다음과 같이 하면 된다.
spring.profiles.group.production=proddb,prodquartz
production
이 activate되면 저 둘도 같이 activate된다. proddb
나 prodquartz
가 activate된다고 나머지가 activate되는 것은 아니라는 점 참고.