Spring

[Spring] 프로필과 프로퍼티 파일

랩실외톨이 2023. 12. 31. 19:20
반응형

 

 

 

 

이번 포스팅에서는 프로필과 프로퍼티 파일의 사용에 대해서 다룹니다.

 

개발을 진행하는 동안에는 실제 서비스에서 사용하고 있는 DB를 이용할 수 없습니다. 개발하는 동안에는 개발용 DB를 따로 사용하거나 개발 PC에 직접 DB를 설치해서 사용하게 됩니다. 여태까지의 포스팅에서도 로컬에 설치한 MySQL을 사용했었죠?

 

실제 서비스 환경에서는 웹 서버와 DB 서버가 서로 다른 장비에 설치된 경우가 많습니다.

개발용, 실 서비스용 DB가 다른 경우가 흔하기 때문에,

개발을 완료한 어플리케이션을 실제 서버에 배포하려면 실 서비스 환경에 맞는 JDBC 연결 정보로 변경해줘야 합니다.

 

이런 다양한 설정을 편리하게 적용하기 위해서 작성하는 것이 스프링의 프로필(profile) 기능입니다.

프로필은 논리적인 이름으로 설정 집합에 프로필을 지정할 수 있습니다.

스프링 컨테이너 설정 집합 중에서 지정한 이름을 사용하는 프로필을 선택하고 해당 프로필에 속한 설정을 이용해서 컨테이너를 초기화할 수 있습니다.

 

예를 들어 로컬 개발 환경을 위한 DataSource 설정을 dev, 실 서비스를 위한 환경을 real이라고 프로필을 지정한 뒤,

dev 프로필을 사용해서 스프링 컨테이너를 초기화할 수 있습니다.

그럼 스프링은 dev 프로필에 정의된 빈을 사용하게 됩니다.

 

 

 

 

반응형

 

 

 

 

 

@Configuration 애노테이션을 이용한 설정에서 프로필을 지정하려면 @Profile 애노테이션을 이용합니다.

 

DsDevConfig.java

 

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

	@Bean(destroyMethod = "close")
	public DataSource dataSource() {
		DataSource ds = new DataSource();
		ds.setDriverClassName("com.mysql.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;
	}
}

 

 

@Profile 애노테이션을 통해 dev 프로필로 설정해 줬습니다.

real로 하고 싶을 때는 애노테이션을 real로 수정하면 됩니다.

 

특정 프로필을 선택하려면 컨테이너가 초기화하기 전에 setActiveProfiles() 메서드를 사용해서 프로필을 선택해야 합니다.

 

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

 

 

getEnvironment() 메서드는 스프링 실행 환경을 설정하는 데 사용되는 Environment를 리턴합니다.

이는 setActiveProfiles 메서드를 사용해 사용할 프로필을 선택할 수 있습니다.

위 코드는 dev를 값으로 주었기 때문에 dev 프로필에 속한 설정이 사용됩니다.

즉, DsDevConfig, DsRealConfig 클래스에 정의되어 있는 dataSource 중 dev 프로필에 속하는 DsDevConfig에 정의된 dataSource 빈을 사용합니다.

 

어떤 프로필을 사용할지 지정을 해준 뒤에, register()로 설정 파일을 지정해 주고, refresh()로 컨테이너를 초기화했습니다.

이 순서를 지키지 않으면 프로필을 선택하기 전에 설정 정보를 먼저 전달하면 프로필을 지정한 설정이 사용되지 않기 때문에 설정을 읽어오는 과정에서 빈을 찾지 못해 익셉션이 발생합니다.

 

 

 

반응형

 

 

 

 

두 개 이상의 프로필을 활성화하고 싶다면 각 프로필의 이름을 메서드에 따라 파라미터로 전달합니다.

 

context.getEnvironment().setActiveProfiles("dev", "mysql");

 

 

프로필을 선택하는 또 다른 방법은 spring.profiles.active 시스템 프로퍼티에 사용할 프로필 값을 지정하는 것입니다.

 

java -Dspring.profiles.active=dev main.Main

 

 

이처럼 시스템 프로퍼티로 프로필을 설정하면 setActiveProfiles() 메서드를 사용하지 않아도 dev 프로필이 활성화됩니다.

 

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MemberConfig.class, DsDevConfig.class, DsRealConfig.class);

 

자바의 시스템 프로퍼티뿐만 아니라 OS의 spring.profiles.active 환경 변수에 값을 설정해도 됩니다. 프로필 우선순위는 다음과 같습니다.

 

  1. setActiveProfiles()
  2. 자바 시스템 프로퍼티
  3. OS 환경 변수

 

 

 

 

반응형

 

 

 

 

 

 

그럼 중첩 클래스를 이용해서 프로필 설정을 한 곳으로 모을 수 있는 예시를 보겠습니다.

 

MemberConfigWithProfile.java

 

@Configuration
@EnableTransactionManagement
public class MemberConfigWithProfile {
	@Autowired
	private DataSource dataSource;
	
	@Bean
	public PlatformTransactionManager transactionManager() {
		DataSourceTransactionManager tm = new DataSourceTransactionManager();
		tm.setDataSource(dataSource);
		return tm;
	}

	@Bean
	public MemberDao memberDao() {
		return new MemberDao(dataSource);
	}

	@Bean
	public MemberRegisterService memberRegSvc() {
		return new MemberRegisterService(memberDao());
	}

	@Bean
	public ChangePasswordService changePwdSvc() {
		ChangePasswordService pwdSvc = new ChangePasswordService();
		pwdSvc.setMemberDao(memberDao());
		return pwdSvc;
	}
	
	@Bean
	public AuthService authService() {
		AuthService authService = new AuthService();
		authService.setMemberDao(memberDao());
		return authService;
	}
	@Configuration
	@Profile("dev")
	public static class DsDevConfig {

		@Bean(destroyMethod = "close")
		public DataSource dataSource() {
			DataSource ds = new DataSource();
			ds.setDriverClassName("com.mysql.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.mysql.jdbc.Driver");
			ds.setUrl("jdbc:mysql://realdb/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이어야 한다는 점입니다.

 

스프링 설정은 두 개 이상의 프로필 이름을 가질 수 있고, 비활성화할 수도 있습니다.

 

// 두 개 이상의 프로필을 설정하는 법
@Profile("real,test")

//프로필을 비활성화 하는 법
@Profile("!real")

 

 

웹 어플리케이션의 경우에도 spring.profiles.active 시스템 프로퍼티나 환경 변수를 사용해서 사용할 프로필을 선택할 수 있습니다.

 

web.xml

 

<init-param>
	<param-name>spring.profiles.active</param-name>
	<param-value>dev</param-value>
</init-param>

 

 

이외에도 스프링은 외부의 프로퍼티 파일을 이용해서 스프링 빈을 설정하는 방법을 제공하고 있습니다.

 

db.properties

 

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

 

 

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

 

 

 

 

 

반응형

 

 

 

 

 

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

PropertySourcesPlaceholderConfigurer 빈 설정과 @Value 애노테이션으로 프로퍼티 값을 사용하는 것입니다.

 

PropertyConfig.java

 

@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;
  }

}

 

 

setLocations 메서드는 프로퍼티 파일 목록을 인자로 전달받습니다.

이때 스프링의 Resource 타입을 이용해서 파일 경로를 전달합니다.

db.properties 파일이 클래스 패스에 위치하고 있다면 ClassPathResource 클래스를 이용해서 프로퍼티 파일 정보를 전달합니다.

 

이때 메서드가 static으로 정의되어 있는데 PropertySourcesPlaceholderConfigurer 클래스가 특수한 목적의 빈이기 때문에 정적 메서드로 지정하지 않으면 원하는 방식으로 동작하지 않습니다.

 

PropertySourcesPlaceholderConfigurer 타입 빈은 setLocations 메서드로 전달받은 프로퍼티 파일 목록 정보를 읽어와 필요할 때 사용합니다. 이를 위한 것이 @Value 애노테이션입니다.

 

DsConfigWithProp.java

 

@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(driver);
		ds.setUrl(jdbcUrl);
		ds.setUsername(user);
		ds.setPassword(password);
		ds.setInitialSize(2);
		ds.setMaxActive(10);
		ds.setTestWhileIdle(true);
		ds.setMinEvictableIdleTimeMillis(60000 * 3);
		ds.setTimeBetweenEvictionRunsMillis(10 * 1000);
		return ds;
	}

}

 

 

PropertySourcesPlaceholderConfigurer는 플레이스홀더의 값을 일치하는 프로퍼티 값으로 치환합니다.

따라서 실제 빈을 생성하는 메서드는 @Value가 붙은 필드를 통해서 해당 프로퍼티의 값을 사용할 수 있습니다.

 

public class Info {

	private String version;

	public void printInfo() {
		System.out.println("version = " + version);
	}

	@Value("${info.version}")
	public void setVersion(String version) {
		this.version = version;
	}

}

 

 

이처럼 빈으로 사용할 클래스에도 @Value 애노테이션을 붙일 수 있습니다.

그렇게 되면 플레이스홀더에 해당하는 프로퍼티를 필드에 할당하게 됩니다.

 

 

 

 

 

출처: 초보 웹 개발자를 위한 스프링 5 프로그래밍 입문

 

 

 

 

반응형