1.5.4 런타임 환경 추상화와 프로파일
- 외부 리소스나 서버환경과 관련이 깊은 애플리케이션 인프라 빈의 클래스와 속성은 같은 애플리케이션이라고 하더라도 실행환경에 따라 달라질 수 있음
환경에 따른 빈 설정정보 변경 전략과 환계
- 보통 개발환경, 테스트환경, 운영환경 정도로 구분
- 같은 개발환경이라도 WAS에서 수동 테스트를 할때와 자동화된 테스트를 수행할 때 런타임 환경 구성을 다르게 가짐
빈 설정파일의 변경
- 가장 손쉬운 방법은 메타정보를 담은 XML이나 클래스를 따로 준비하는 방법
- 각 환경에서 스프링을 실행할 때 다른 설정파일을 사용
- 개발이나 유지보수가 계속 진행되면서 설정정보가 지속적으로 달라지는 경우 조금씩 내용이 다른 여러 벌의 설정 메타정보를 관리하는 것은 번거롭고 위험
프로퍼티 파일 활용
- 환경에 따라 달라지는 정보를 담은 프로퍼티 파일을 활용하는 방법
- XML이나 @Configuration 클래스는 애플리케이션 로직이 바뀌지 않는 한 건드리지 않고 환경에 따라 달라지는 외부 정보만 프로퍼티 파일 등에 두고 사용하는 방법
- XML에 DataSource 클래스로 빈을 등록하고 실제 속성에 넣을 값은 프로퍼티 파일에서 읽어서 지정하도록 프로퍼티 치환자를 사용해서 정의
<bean id="dataSource" class="...SimpleDriverDataSource">
<property name="driverClass" value="${db.driverClass}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
- 프로퍼티 파일만 별도로 준비하는 경우에 배포시 주의가 필요(운영에 개발 프로퍼티 X)
런타임 환경과 프로파일
- 스프링 3.1을 사용하면 환경에 따라 빈 설정정보가 달라지는 문제를 깔끔하게 해결가능
- 런타임 환경은 애플리케이션 컨텍스트에 새롭게 도입된 개념
- 컨텍스트 내부에 Environment 인터페이스를 구현한 런타임 환경 오브젝트가 만들어져서 빈을 생성하거나 의존관계를 주입할 때 사용
- 런타임 환경은 프로파일과 프로퍼티로 구성
- 프로파일의 개념은 환경에 따라 다르게 구성되는 빈들을 다른 이름을 가진 프로파일 안에 정의하고 애플리케이션 컨텍스트가 시작될 때 지정된 프로파일에 속한 빈들만 생성
- 환경에 따라 다른 프로파일을 활성화하면 각기 다른 빈들이 사용 됌
- 프로파일은 XML과 자바 클래스를 이용한 설정 모두 적용이 가능
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframwork.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="userDao" class="springbook.user.dao.UserDaoJdbc">
<property name="dataSource" ref="dataSource" />
</bean>
<beans profile="spring-test">
<jdbc:embedded-database id="dataSource" type="HSQL">
<jdbc:script l;ocation="schema.sql" />
</jdbc:embedded-database>
</beans>
<beans profile="dev">
<bean id="dataSource" class="org.apache.common.dbcp.BasicDataSource">
<property name="driverClassName" value="${db.driverClass}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
<context:property-placeholder location="databse.properties"/>
</beans>
<beans profile="production">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/DefaulttDS" />
</beans>
</beans>
- 중첩되어 나오는 <beans>가 루트의 <beans>의 다른점은 profile 속성을 가짐
- profile 조건에 따라 내부에 정의된 빈을 사용하는 빈 그룹으로 보면 됌
- DAO가 사용할 DataSource를 정의하는 빈들이지만 각각 프로파일마다 다르게 구성
활성 프로파일 지정 방법
- 특정 프로파일에 정의된 빈을 사용하고 싶으면 프로파일을 활성 프로파일로 만들어주면 됌
- 프로파일 관련 정보는 Environment 타입 오브젝트가 갖고 있음
Environment
는 애플리케이션 컨텍스트에서 가져오며, 활성 프로파일 정보를 지정할 수 있는setActiveProviers()
가 있어 여기에 사용할 프로파일 이름을 지정
- 프로파일은 XML이 로딩되거나
@Configuration
클래스가 적용되는refresh()
가 컨텍스트에 실행되기 전에 지정 필요
GenericXmlApplicationContext ac = new GenericXmlApplicationContext();
ac.getEnvironment().setActiveProviles("dev");
ac.load(getClass(), "applicationContext.xml");
ac.refresh();
- 활성 프로파일을 지정하지 않으면 profile이 붙은 <beans>의 내용은 다 무시
- 애플리케이션 컨텍스트 서버에서 web.xml을 통해 간접적으로 생성하는 경우가 대부분
- 이때는 활성 프로파일을 시스템 프로퍼티나 환경변수를 통해 지정이 가능
- WAS 시동 스크립트에 spring.profile.active에 dev를 넣으면 dev로 생성
- 환경변수를 설정하는 대신 JVM의 컨맨드라인 파라미터를 이용해서 지정도 가능
-Dspring.profiles.active=dev
- 만약 하나의 WAS에 여러 개의 스프링이 올라가는데 가기 다른 프로파일로 지정하고 싶다면 JVM 레벨의 프로퍼티 대신 서블릿 컨텍스트 레벨이나 서블릿 레벨 설정이 필요
- 아래와 같이 서블릿 컨텍스트 파라미터로 활성 프로파일을 지정하면 루트 애플리케이션 컨텍스트와 서블릿 컨텍스트에 모두 적용
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>
- 서블릿 컨텍스트에만 활성 프로파일을 지정하려면 아래와 같이 설정
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</init-param>
</servlet>
- 웹 애플리케이션에 활성 프로파일을 지정하기 위해 web.xml 수정하는건 추천하지 않음
- web.xml은 빈 설정파일처럼 자주 변경되지 않으므로 셋업할 때 한번만 변경, 그 후 변경X
- 배치할 때마다 압축된 war 파일을 사용하는 경우라면 web.xml을 한 번 수정해두고 다른 수정된 파일만 업데이트해가며 사용하기가 어려움
- 이런 경우 WAS의 JNDI 환경 값을 이용하는 방법을 사용, 톰캣 같은 가벼운 서블릿 컨테이너에서도 스트링 타입의 JNDI 값을 지정 가능
- 활성 프로파일을 지정하는 방법은 여러가지이지만 여러개를 동시에 사용하는 경우 우선순위가 가장 높은 방법으로 지정된 것이 적용
- 서블릿 초기화 파라미터인 <init-param>, 물론 서블릿 컨텍스트에만 적용
- 서블릿 컨텍스트 파라미터, 웹 어플리케이션의 루트와 모든 서블릿 컨텍스트에 영향
- JNDI 프로퍼티 값
- 시스템 프로퍼티
- 환경변수
- 특별한 이유가 없다면 혼란을 줄 수 있으니 두가지 이상 방법을 지정하지 않도록 주의
프로파일 활용 전략
- 프로파일은 한 번에 두가지 이상을 활성화 가능
- 활성 프로파일에 적용할 프로파일을 모두 넣는 것이 가능
- DB 연결과 관련된 빈은 dsDev, dsTest, dsProduct 세가지 프로파일로 구분
- 메일 서버와 관련된 빈은 mailServer와 테스트용 mockMailServer로 구분
<context-param> <param-name>spring.profiles.active</param-name> <param-value>deDev, mockMailServer</param-value> </context-param>
- <beans>의 프로파일 이름에도 하나 이상 넣는것이 가능
<beans profile="dev, test">
- 프로파일을 적용하고 나면 애플리케이션에 활성 프로파일과 그로 인해 등록된 빈이 어떤것이 있는지 확인해보는 것이 좋음
- 컨텍스트 오브젝트의
getEnvironment()
런타임 환경 오브젝트를 가져와getActiveProfiles()
를 실행하면 활성 프로파일 목록 조회가 가능
- 컨텍스트 오브젝트의
- @Configuration이 붙은 클래스를 이용해 빈을 정의하는 경우, 프로파일 지정 가능
@Configuration @Profile("dev") public class DevConfig { ... }
- XML에서는 프로파일의 적용을 받지 않는 빈들을 최상위 <beans>에 넣고 프로파일을 적용할 빈을 중첩된 <beans>로 정의
- @Configuration에서는 세가지 방법으로 사용 가능
- 첫번쨰 방법
- AnnotationConfigWebApplicationContext를 사용해 @Configuration 클래스를 설정 메타정보로 사용하려면 클래스의 위치를 contextConfigLocation으로 지정
- contextConfigLocation을 패키지 레벨로 지정하면 패키지 내의 클래스 중에 @Configuration이 붙은 것이 적용 후보가 됌
- 클래스에 @Profile이 있으면 컨텍스트의 활성 프로파일과 비교해 적용 대상인 경우만 사용되고 나머지는 버려짐
- 최상위 루트에 <beans>에 속한 <bean>은 @Profile이 붙지 않은 클래스로 정의
- 3개의 프로파일 <beans>는 DevConfig, SpringTestConfig, ProductConfig 클래스를 만들고 @Profile을 이용해 프로파일 이름을 지정
- 두번째 방법
- 패키지 대신 기준 @Configuration 클래스를 직접 지정하는 경우라면 AppConfig 클래스에 @Import를 사용해 3개의 클래스를 가져오게 지정
- @Import로 지정된 클래스라도 @Profile이 있으면 활성 프로파일의 적용 대상인지부터 확인하기 때문에 활성화된 메타정보만 가져옴
- 세번째 방법
- 프로파일이 붙은 3개의 클래스를 AppConfig의 스태틱 중첩 클래스로 정의
- 각 프로파일에 들어가는 빈의 개수가 많지 않다면 괜찮은 방법
@Configuration public class AppConfig { @Bean UserDao userDao() { ... } @Configuration @Profile("spring-test") public static class SpringTestConfig { ... } @Configuration @Profile("dev") public static class DevConfig { ... } @Configuration @Profile("production") public static class ProductionConfig { ... } }
- 첫번쨰 방법
'개발서적 > 토비 스프링 3.1-Vol.2' 카테고리의 다른 글
[토비의 스프링 - Vol.2] 2장 - 2.1 공통개념 (0) | 2022.01.17 |
---|---|
[토비의 스프링 - Vol.2] 1장 - 1.5.5 프로퍼티 소스 (0) | 2022.01.17 |
[토비의 스프링 - Vol.2] 1장 - 1.5.3 웹 애플리케이션의 새로운 IoC 컨테이너 구성 (0) | 2022.01.17 |
[토비의 스프링 - Vol.2] 1장 - 1.5.2 컨테이너 인프라 빈을 위한 자바 코드 메타정보 (0) | 2022.01.17 |
[토비의 스프링 - Vol.2] 1장 - 1.5 스프링 3.1의 IoC 컨테이너와 DI (0) | 2022.01.17 |