프로필이란?
Spring이 제공하는 프로필(profile) 기능은 설정 집합에 지정 가능한 논리적인 이름이다. Spring 컨테이너는 설정 집합 중 지정한 이름을 사용하는 프로필을 선택하고, 해당 프로필에 속한 설정을 이용하여 컨테이너를 초기화할 수 있다.
프로필 사용의 목적
개발 진행 중에는 실제 서비스용으로 운영 중인 DB를 이용할 수 없다. 이 때문에 개발 도중에는 별도의 개발용 DB를 사용하거나 개발용 PC에 직접 DB를 설치한다. 이 때문에 개발 완료된 어플리케이션을 실제로 배포할 때는 실 서비스 환경에 맞는 별도의 JDBC 연결 정보를 활용해야 한다.
물론 실제 배포 직전에 설정을 변경할 수도 있다. 하지만 설정 정보를 변경하는 동안 오타를 입력하거나, 실제 배포용 설정을 유지한 채로 개발을 진행하는 실수를 저지를 수 있다는 문제가 있다.
Spring이 제공하는 프로필 기능은 개발용 설정과 실제 서비스용 설정을 처음부터 구분 가능하게 하므로 이와 같은 실수를 방지한다.
주요 어노테이션
@Profile("프로필")
: 이 어노테이션은@Configuration어노테이션이 붙은 설정 클래스에 사용할 수 있다.
해당 설정 클래스를 이 어노테이션이 지정한 이름을 사용하는 프로필로 지정한다.@Profile({"프로필1, 프로필2, ..."})
: Spring 컨테이너에서 사용할 프로필이 이 어노테이션에서 나열한 이름들 중 하나를 가질 때,
이 어노테이션이 붙은 설정 클래스를 사용한다.@Profile("!프로필")
: 이 어노테이션에서 지정한 이름을 사용하는 프로필을 Spring 컨테이너가 사용하지 않을 때, 이 어노테이션을 사용한 설정 클래스에서 지정한 설정을 사용한다.
DsDevConfig: dev 프로필로 지정할 설정
/* dev 프로필로 지정 */
@Configuration
@Profile("dev")
public class DsDevConfig {
@Bean(destroyMethod = "close")
public DataSource dataSource() {
DataSource ds = new DataSource();
/* JDBC 드라이버 클래스로 MySQL 드라이버 클래스를 사용 */
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
/* JDBC URL을 지정하고, MySQL의 DB와 연동 시 사용할 캐릭터셋을 utf8로 지정 */
ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8");
/* DB 연동 시 사용할 사용자 계정과 암호 지정 */
ds.setUsername("spring5");
ds.setPassword("spring5");
... 코드 생략
return ds;
}
}
DsRealConfig: real 프로필로 지정할 설정
/* real 프로필로 지정 */
@Configuration
@Profile("real")
public class DsRealConfig {
@Bean(destroyMethod = "close")
public DataSource dataSource() {
DataSource ds = new DataSource();
/* JDBC 드라이버 클래스로 MySQL 드라이버 클래스를 사용 */
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
/* JDBC URL을 지정하고, MySQL의 DB와 연동 시 사용할 캐릭터셋을 utf8로 지정 */
ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8");
/* DB 연동 시 사용할 사용자 계정과 암호 지정 */
ds.setUsername("spring5");
ds.setPassword("spring5");
... 코드 생략
return ds;
}
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext();
// 사용할 프로필 선택(여러 개도 가능)
context.getEnvironment().setActiveProfiles("dev");
// 반드시 setActiveProfiles() 메서드보다 나중에 실행
context.register(MemberConfig.class, DsDevConfig.class, DsRealConfig.class);
context.refresh();
}
}
getEnvironment()
: Spring 실행 환경을 설정하기 위한Environment객체 리턴setActiveProfiles("프로필1"[, "프로필2", ...])
: 1개 이상의 활성화할 프로필을 선택한다.register(설정클래스1.class[, 설정클래스2.class, ...])
: 설정 정보를 전달할 1개 이상의 설정 클래스를 지정한다.setActiveProfiles()메서드보다 먼저 호출될 시 프로필 지정이 반영되지 않아 설정을 읽어올 때 적합한 Bean을 찾아내지 못하게 되므로NoUniqueBeanDefinitionException등의 Exception이 발생할 수 있다.
콘솔창을 통해 java 파일을 실행할 경우 다음과 같이 -D 옵션을 사용하여 spring.profiles.active 시스템 프로퍼티의 값을 설정한다.
java -Dspring.profiles.active=프로필1[,프로필2,...] 실행할_클래스_파일
setActiveProfiles()메서드로 지정한 프로필- java
spring.profiles.active시스템 프로퍼티의 값으로 지정한 프로필- OS의
spring.profiles.active환경 변수의 값으로 지정한 프로필
다음과 같이 중첩 클래스를 사용하면 프로필 설정을 1개의 클래스로 모아 한번에 파악하기 쉽다.
이 방법을 사용할 경우, 프로필로 지정된 중첩 클래스에 관한 정보를 실행 이전 컴파일 단계부터 미리 파악해야 하므로 중첩 클래스는 static이어야 한다.
MemberConfigWithProfile
@Configuration
@EnableTransactionManagement
public class MemberConfigWithProfile {
/* 활성화한 프로필의 DataSource를 자동 주입 */
@Autowired
private DataSource dataSource;
@Bean
public MemberDao memberDao() {
return new MemberDao(dataSource);
}
/* 설정 클래스 내부에 dev 프로필 설정 추가, static 사용 */
@Configuration
public static class DsDevConfig {
@Bean
public DataSource dataSource() {
DataSource ds = new DataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8");
ds.setUsername("spring5");
ds.setPassword("spring5");
... 코드 생략
return ds;
}
}
/* 설정 클래스 내부에 real 프로필 설정 추가, static 사용 */
@Configuration
public static class DsRealConfig {
@Bean
public DataSource dataSource() {
DataSource ds = new DataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8");
ds.setUsername("spring5");
ds.setPassword("spring5");
... 코드 생략
return ds;
}
}
... 코드 생략
}
웹 어플리케이션에서의 프로필 설정은 web.xml에서 다음과 같이 spring.profiles.active 초기화 파라미터를 추가하는 방식으로 수행할 수 있다.
web.xml
... 코드 생략
<servlet>
<servlet-name>dispatcher</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>
<!-- 초기화에 사용할 Spring 컨테이너 선택 -->
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<!-- 사용할 설정 클래스 지정(프로필로 지정한 설정 클래스 추가 필요) -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
config.MemberConfig
config.MvcConfig
config.ControllerConfig
<!-- "dev" 프로필과 "real" 프로필 -->
config.DsDevConfig
config.DsRealConfig
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
... 코드 생략
java 설정에서 프로퍼티 파일을 사용하기 위해서는 다음의 작업을 순서대로 수행해야 한다.
PropertySourcePlaceholderConfigurerBean 설정하기
: 해당 Bean은 이 클래스의setLocation()메서드를 활용하여 프로퍼티 파일 목록을 인자로 전달받은 프로퍼티 파일 목록 정보를 읽어온다.
runtime 이전에 프로퍼티 정보를 미리 읽어와야 하므로 해당 Bean 메서드는static이어야 한다.@Value("${프로퍼티}")어노테이션 사용하기
: 이 어노테이션은 필드에 사용할 수 있다. 이 어노테이션이 붙은 필드는 이 어노테이션이 지정한 프로퍼티의 값을 사용한다.
src/main/resources/db/dev.properties 파일
dev.driver=com.mysql.cj.jdbc.Driver
dev.url=jdbc:mysql://localhost/spring5fs?characterEncoding=utf8
dev.user=spring5
dev.password=spring5
DsDevConfig: 프로퍼티 파일 사용 예시
@Configuration
@Profile("dev")
public class DsDevConfig {
/* @Value를 통해 dev.properties 파일 내 프로퍼티의 값 사용 */
@Value("${dev.driver}")
private String driver;
@Value("${dev.url}")
private String jdbcUrl;
@Value("${dev.user}")
private String user;
@Value("${dev.password}")
private String password;
/* PropertySourcePlaceholderConfigurer Bean 설정 */
@Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer configurer =
new PropertySourcesPlaceholderConfigurer();
/* src/main/resources/db 폴더 내 dev.properties 파일을 사용 대상으로 설정 */
configurer.setLocation(new ClassPathResource("/db/dev.properties"));
return configurer;
}
/* 프로퍼티의 값을 받은 필드를 사용 */
@Bean(destroyMethod = "close")
public DataSource dataSource() {
DataSource ds = new DataSource();
ds.setDriverClassName(driver);
ds.setUrl(jdbcUrl);
ds.setUsername(user);
ds.setPassword(password);
... 코드 생략
return ds;
}
}
org.springframework.core.io.Resource 인터페이스
해당 인터페이스는 Spring에서 자원을 표현할 때 사용하며, 다음의 2가지가 대표적인 구현 클래스이다.
org.springframework.core.io.ClassPathResource
: 클래스패스(src/main/resource 폴더)에 위치한 자원으로부터 데이터를 읽는다.
단, 클래스패스의 하위 폴더에 저장된 파일은 해당 하위 폴더를 경로명에 명시해야 한다.
(ex:src/main/resources/db/dev.properties파일의 경우,
new ClassPathResource(/db/dev.properties)로 작성)org.springframework.core.io.FileSystemResource
: 파일 시스템에 위치한 자원으로부터 데이터를 읽어온다.
Bean으로 등록한 클래스에서도 @Value 어노테이션을 사용할 수 있다. 이 때 @Value 어노테이션은 Bean 클래스의 필드나 setter에 사용되며, 해당 어노테이션에서 지정한 프로퍼티의 값을 이 어노테이션이 붙은 필드 또는 setter의 매개변수에 할당한다.