[스프링] 스프링5 프로그래밍 입문 - 17 장 : 프로필과 프로퍼티 파일

June·2021년 6월 22일
0

개발을 진행하는 동안에는 실제 서비스 목적으로 운영중인 DB를 이용할 수는 없다. 개발하는 동안에는 개발용 DB를 따로 사용하거나 개발 PC에 직접 DB를 설치해서 사용한다.

실제 서비스 환경에서는 웹 서버와 DB 서버가 서로 다른 장비에 설치된 경우가 많다. 즉 개발을 완료한 어플리케이션을 실제 서버에 배포하려면 실 서비스 환경에 맞는 JDBC 연결 정보를 사용해야 한다.

처음부터 개발 목적 설정과 실 서비스 목적의 설정을 구분해서 작성할 수 있다. 이를 위한 스프링 기능이 프로필(profile)이다.

프로필은 논리적인 이름으로서 설정 집합에 프로필을 지정할 수 있다. 스프링 컨테이너는 설정 집합 중에서 지정한 이름을 사용하는 프로필을 선택하고 해당 프로필에 속한 설정을 이용해서 컨테이너를 초기화할 수 있다. 예를 들어 로컬 개발 환경을 위한 DataSource 설정을 "dev" 프로필로 지정하고 실 서비스 환경을 위한 DataSource 설정을 "real" 프로필로 지정한 뒤, "dev" 프로필을 사용해서 스프링 컨테이너를 초기화할 수 있다.

@Configuration 설정에서 프로필 사용하기

DsDevConfig

@Configuration
@Profile("dev")
public class DsDevConfig {

    @Bean(destroyMethod = "close")
    public DataSource dataSource() {
        DataSource ds = new DataSource();
        ds.setDriverClassName("com.mysdql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8");
        ds.setUsername("spring5");
        ds.setPassword("spring5");
        ds.setInitialSize(2);
        ds.setMaxActive(10);
        ds.setTestWhileIdle(true);
        ds.setMinEvictableIdleTimeMillis(60000 * 3);
        ds.setTimeBetweenEvictionRunsMillis(10 * 1000);
        return ds;
    }
}

DsRealConfig

@Configuration
@Profile("real")
public class DsRealConfig {

    @Bean(destroyMethod = "close")
    public DataSource dataSource() {
        DataSource ds = new DataSource();
        ds.setDriverClassName("com.mysdql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8");
        ds.setUsername("spring5");
        ds.setPassword("spring5");
        ds.setInitialSize(2);
        ds.setMaxActive(10);
        ds.setTestWhileIdle(true);
        ds.setMinEvictableIdleTimeMillis(60000 * 3);
        ds.setTimeBetweenEvictionRunsMillis(10 * 1000);
        return ds;
    }
}

두 "dataSource" 빈 중에서 어떤 빈을 사용할지는 활성화한 프로필에 따라 달라진다. "dev" 프로필을 활성화하면 @Profile("dev") 애노테이션을 붙인 설정 클래스의 dataSource 빈을 사용하고 "real" 프로필을 활성화하면 @Profile("real") 애노테이션을 붙인 설정 클래스의 dataSource 빈을 사용한다.

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("dev");
context.register(MemberConfig.class, DsDevConfig.class, DsRealConfig.class);
context.refresh();

getEnvironment() 메서드는 스프링 실행 환경을 설정하는데 사용되는 Environment를 리턴한다. 이 Environment의 setActiveProfiles() 메서드를 사용해서 사용할 프로필을 선택할 수 있다.

프로필을 사용할 때 주의할 점은 설정 정보를 전달하기 전에 어떤 프로필을 사용할지 지정해야 한다는 점이다. 위 코드를 보면 setActiveProfiles() 메서드로 "dev" 프로필을 사용한다고 설정한 뒤에 register() 메서드로 설정 파일 목록을 지정했다. 그런 뒤 refresh() 메서드를 실행해서 컨테이너를 초기화했다. 이 순서를 지키지 않고 프로필을 선택하기 전에 설정 정보를 먼저 전달하면 프로필을 지정한 설정이 사용되지 않기 때문에 설정을 읽어오는 과정에서 빈을 찾지 못해 익셉션이 발생한다.

@Configuration을 이용한 프로필 설정

MemberConfigWithProfile

@Configuration
public class MemberConfigWithProfile {
    
    @Autowired
    private DataSource dataSource;
    
    @Bean
    public MemberDao memberDao() {
        return new MemberDao(dataSource);
    }

    @Configuration
    @Profile("dev")
    public static class DsDevConfig {

        @Bean(destroyMethod = "close")
        public DataSource dataSource() {
            DataSource ds = new DataSource();
            ds.setDriverClassName("com.mysdql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8");
            ds.setUsername("spring5");
            ds.setPassword("spring5");
            ds.setInitialSize(2);
            ds.setMaxActive(10);
            ds.setTestWhileIdle(true);
            ds.setMinEvictableIdleTimeMillis(60000 * 3);
            ds.setTimeBetweenEvictionRunsMillis(10 * 1000);
            return ds;
        }
    }

    @Configuration
    @Profile("real")
    public static class DsRealConfig {

        @Bean(destroyMethod = "close")
        public DataSource dataSource() {
            DataSource ds = new DataSource();
            ds.setDriverClassName("com.mysdql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8");
            ds.setUsername("spring5");
            ds.setPassword("spring5");
            ds.setInitialSize(2);
            ds.setMaxActive(10);
            ds.setTestWhileIdle(true);
            ds.setMinEvictableIdleTimeMillis(60000 * 3);
            ds.setTimeBetweenEvictionRunsMillis(10 * 1000);
            return ds;
        }
    }
}

중첩된 @Configuration 설정을 사용할 때 주의할 점은 중첩 클래스는 static이어야 한다는 점이다.

다수 프로필 설정

스프링 설정은 두 개 이상의 프로필 이름을 가질 수 있다.

@Configuration
@Profile("real, test")
public class DataSourceJndiConfig {
 ...

real 프로필을 사용할 때와 test 프로필을 사용할 때 모두 해당 설정을 사용한다.

@Configuration
@Profile("real")
public class DsDevConfig {
  ...

"!real" 값은 "real" 프로필이 활성화되지 않을 때 사용한다는 것을 의미한다.

어플리케이션에서 프로필 설정하기

web.xml에서 spring.profiles.active 초기화 파라미터를 이용해서 프로필을 선택할 수 있다.

프로피터 파일을 이용한 프로퍼티 설정

스프링은 외부의 프로퍼티 파일을 이용해서 스프링 빈을 설정하는 방법을 제공하고 있다. 예를 들어 다음과 같은 db.properties 파일이 있다고 하자.

db.driver = com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost/spring5fs?characterEncoding=utf8
db.user=spring5
db.password=spring5

이 파일의 프로퍼티 값을 자바 설정에서 사용할 수 있으며 이를 통해 설정 일부를 외부 프로퍼티 파일을 사용해서 변경할 수 있다.

@Configuration 애노테이션 이용 자바 설정에서의 프로퍼티 사용

자바 설정에서 프로퍼티 파일을 사용하려면 다음 두 가지를 설정한다.

  1. PropertySourcesPlaceholderConfigurer 빈 설정
  2. @Value 애노테이션으로 프로퍼티 값 사용

PropertyConfig

@Configuration
public class PropertyConfig {
    
    @Bean
    public static PropertySourcesPlaceholderConfigurer properties() {
        PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
        configurer.setLocations(new ClassPathResource("db.properties"), new ClassPathResource("info.properties"));
        return configurer;
    }
}

PropertySourcesPlaceholderConfigurer#setLocations() 메서드는 프로퍼티 파일 목록을 인자로 전달받는다. 이때 스프링의 Resource 타입을 이용해서 파일 경로를 전달한다. db.properties 파일이 클래스 패스에 위치하고 있다면, ClassPathResource 클래스를 이용해서 프로퍼티 파일 정보를 전달한다.

Resource 인터페이스
o.s.core.io.Resource 인터페이스는 스프링에서 자원을 표현할 때 사용한다. 대표적인 구현 클래스로 다음의 두 가지가 있다.
1. o.s.c.io.ClassPathResource: 클래스 패스에 위치한 자원으로부터 데이터를 읽음
2. o.s.c.io.FileSystemResource: 파일 시스템에 위치한 자원으로부터 데이터를 읽음

주의할 점은 PropertySourcePlaceholderConfigurer 타입 빈을 설정하는 메서드가 정적(static) 메서드라는 것이다. 이는 PropertySourcesPlaceholderConfigurer 클래스가 특수한 목적의 빈이기 때문이며 정적 메서드로 지정하지 않으면 원하는 방식으로 동작하지 않는다.

DsConfigWithProp


@Configuration
public class DsConfigWithProp {
    @Value("${db.driver}")
    private String driver;
    @Value("${db.url}")
    private String jdbcUrl;
    @Value("${db.user}")
    private String user;
    @Value("${db.password}")
    private String password;
    
    @Bean(destroyMethod = "close")
    public DataSource dataSource() {
        DataSource ds = new DataSource();
        ds.setDriverClassName("com.mysdql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost/spring5fs?characterEncoding=utf8");
        ds.setUsername("spring5");
        ds.setPassword("spring5");
        ds.setInitialSize(2);
        ds.setMaxActive(10);
        ds.setTestWhileIdle(true);
        ds.setMinEvictableIdleTimeMillis(60000 * 3);
        ds.setTimeBetweenEvictionRunsMillis(10 * 1000);
        return ds;
    }
}

@Value 애노테이션이 ${구분자} 형식의 플레이스홀더를 값으로 갖고 있다. 이 경우 PropertySourcesPlaceholderConfigurer는 플레이스홀더의 값을 일치하는 프로퍼티 값으로 치환한다. 이 예의 경우 ${db.driver} 플레이스홀더를 db.properties에 정의되어 있는 "db.driver" 프로퍼티 값으로 치환한다.

0개의 댓글