파란하늘의 지식창고
반응형

springframework는 profile 설정을 통해 환경마다 다른 설정을 호출할 수 있는 기능을 제공한다.

spring application 을 실행 시 실행 변수로 spring.profiles.active를 선언하는 방식이다.

$ java -jar -Dspring.profiles.active=p1 application.jar

사용 방법 1 - SpEL로 profile 지정

profile 별 properties 파일을 호출하는 경우 Spring 에서 제공하는 @PropertySource을 아래와 같이 사용한다.

@Configuration
@PropertySource("classpath:sample-${spring.profiles.active}.properties")
public class Config {
}

SpEL 문법으로 active profile 별 properties 파일을 호출하는 방식이다.

이 방법은 문제가 있다.

spring.profiles.active는 단일 String 값이 아닌 배열 값 String[] 을 반환한다.

profile을 1개만 선언하여 사용하는 경우 위 설정은 문제없이 동작하지만 n개 이상을 선언하는 경우, 예를 들어 -Dspring.profiles.active=p1,p2 와 같이 선언하면 classpath:sample-p1,p2.properties 란 이름의 파일을 호출하게 된다.

@PropertySource에 ${spring.profiles.active}을 사용하면 1개의 profile 만 선언해야만 하는 강제 조건이 생기는 것이다.

사용 방법 2 - @Profile과 조합

이 문제를 피하기 위해 아래와 같이 명시적으로 사용하는 방법이 있다.

@Profile("p1")
@Configuration
@PropertySource("classpath:sample-p1.properties")
public class P1Config {
}

@Profile("p2")
@Configuration
@PropertySource("classpath:sample-p2.properties")
public class P2Config {
}

@Profile 어노테이션을 사용하면 조건에 해당하는 빈을 호출한다.

위처럼 사용하면 proifle을 N개를 선언하라도 해당하는 @Profile 조건에 대한 properties 파일을 호출 할 수 있다.

하지만 profile 의 종류와 @PropertySouce 선언할 위치에 비례해 복잡도가 급격히 증가하기 때문에 위 방법은 작은 규모의 프로젝트에서나 쓸만하다.

사용 방법 3 - 외부 호출 설정

spring boot 실행 시 추가 config location을 지정할 수도 있다.

$ java -jar -Dspring.profiles.active=p1 -Dspring.config.additional-location=classpath:p1.properties,classpath:p2.properties application.jar

스프링이 제공하는 application-{profile} 의 경우

Application Property Files

기본 config 설정 파일인 application-{profile}.properties 의 경우 profile 을 N개 선언해도 문제가 되지 않는다.

profile을 p1,p2 와 같이 선언하면 application-p1.properties, application-p2.properties 각각의 파일을 호출한다. (yml 파일도 마찬가지이다.)

application properties 파일은 기본 호출하는 파일이며 @PropertySource 를 선언할 필요가 없다.

application이 아닌 다른 파일 이름을 사용하고자 한다면 spring.config.name을 선언하면 된다.

 

module마다 동일한 이름의 application-{profile}.properties를 선언하여 중복 호출을 할 수 없다.

application 파일의 기본 호출 위치는 다음과 같다.

  1. classpath:/
  2. classpath:/config/
  3. file:./
  4. file:./config/

위 4개의 path에서 순서대로 호출한다.

(spring.config.additional-location가 설정되어 있으면 그 위치를 우선한다.)

application-{profile} properties는 되도록 가장 끝단의 사용 대상 module이 관리하는 것이 좋고 참조되는 module은 쓰지 않아야 한다.

multi module에서 properties 파일 관리

multi module에서 properties 파일을 사용하는 경우 각 module 별 이름 + profile 로 조합해서 관리하는 것이 좋다. (중복되지 않는 것이 관건이다.)

예를 들어 p1,p2란 profile을 사용하고 A, B, C, D란 module이 있다고 가정하면 아래와 같은 형태로 properties 파일을 관리한다.

module 이름 properties 관리
A module a-p1.properties
a-p2.properties
B module b-p1.properties
b-p2.properties
C module c-p1.properties
c-p2.properties
D module d-p1.properties
d-p2.properties

그러면 어떻게 N개의 profile을 사용하면 좋을까?

문제는 profile을 N개를 사용하는 경우 각 module 별 profile을 어떻게 호출하는 가이다.

SpEL로 직접 지정하는 경우 N개의 profile을 사용할 때 문제가 있기 때문에 가장 피해야하는 방식이고

@Profile과 조합하는 경우 module과 Profile이 많아질 수록 관리가 복잡해진다.

외부 호출로 지정하는 방식을 사용하는 경우 참조되는 module의 설정 관리를 사용 대상 module로 위임하는 경우기 때문에 바람직하지 않다.

위에 열거한 다양한 호출 방법들이 모두 N개의 profile을 사용하기에 좋은 방법이 아니다.

전제조건 - profile이 유형별로 분류가 된다면

N개의 profile이 유형별로 분류된다면 분류된 규칙 별로 호출된다고 가정할 수 있다.

예를 들어 아래와 같은 규칙이 있다고 가정하자.

profile 그룹 profile 그룹의 profile
p1 p1_a, p1_b, p1_c, p1_d
p2 p2_e, p2_f, p2_g
p3 p3_h, p3_i

multi 로 사용하는 profile은 총 3종류로 가정하고 각 profile마다 prefix가 붙은 형태의 profile을 사용한다는 조건이다.

profile 별로 개별 변수를 아래와 같이 부여한다.

profile 그룹 그룹 변수 명
p1 p1-profile
p2 p2-profile
p3 p3-profile

각각의 module에선 해당 module이 사용하는 profile 그룹 변수 명을 기준으로 properties 호출을 선언한다.

만약 해당 module이 p1 profile 그룹에 대해서만 종속적이라면 아래와 같이 사용한다.

@Configuration
@PropertySource("classpath:a-${p1-profile}.properties")
public class Config {
}

만약 해당 module이 p1, p2 profile 그룹에 대해 종속적이라면 아래와 같이 사용한다.

@Configuration
@PropertySource("classpath:a-${p1-profile}-${p2-profile}.properties")
public class Config {
}

이런 형태로 사용하면 각각의 modue들은 자신이 관련 있는 profile group에 대해서만 호출 선언이 가능해진다.

profile 조건이 추가되더라도 관련이 없는 module에는 아무 영향이 없다.

$ java jar -Dspring.profiles.active=p1_a,p2_f,other_profile application.jar

이렇게 그룹별로 정의된 profile을 사용하기 위해 spring boot에서 EnvironmentPostProcessor를 사용한다.

public class SampleEnvironmentPostProcessor implements EnvironmentPostProcessor {

	private static final String[] P1_PROFILES = new String[] { "p1_a", "p1_b", "p1_c", "p1_d" };
	private static final String[] P2_PROFILES = new String[] { "p2_e", "p2_f", "p2_g" };
	private static final String[] P3_PROFILES = new String[] { "p3_h", "p3_i"};
	
	private static final String P1_PROFILE = "p1-profile";
	private static final String P2_PROFILE = "p2-profile";
	private static final String P3_PROFILE = "p3-profile";

	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
		String[] activeProfiles = environment.getActiveProfiles();
		
		Properties properties = new Properties();
		
		
		for (String activeProfile : activeProfiles) {
			for (String profile : P1_PROFILES) {
				if (profile.equals(activeProfile)) {
					properties.setProperty(P1_PROFILE, profile);
				}
			}
			for (String profile : P2_PROFILES) {
				if (profile.equals(activeProfile)) {
					properties.setProperty(P2_PROFILE, profile);
				}
			}
			for (String profile : P3_PROFILES) {
				if (profile.equals(activeProfile)) {
					properties.setProperty(P3_PROFILE, profile);
				}
			}
		}
		
		// p1 profile은 필수로 설정한 경우
		if (!properties.containsKey(P1_PROFILES)) {
			throw new RuntimeException("Must specify at least one profile");
		}
		
		// p2 profile은 없으면 기본값 설정을 한 경우
		if (!properties.containsKey(P2_PROFILE)) {
			properties.setProperty(P2_PROFILE, "p2_e");
		}
		environment.getPropertySources().addFirst(new PropertiesPropertySource("sampleProperties", properties));
	}

}

EnvironmentPostProcessor는 Spring Boot에서 제공하는 기능이며 Boot 실행 시 Environment 가 설정되고 난 후 수행되는 프로세서이다.

spring.profiles.active String[]은 Environment에 등록되기 때문에 해당 등록 시점 이후 profile 그룹을 등록하는 처리를 위 설정을 통해 수행한다.

해당 프로세서가 동작하도록 META/INF/spring.factories 파일을 만들어 등록한다.

org.springframework.boot.env.EnvironmentPostProcessor=net.luversof.env.SampleEnvironmentPostProcessor

spring이 자체적으로 @PropertySource에서 ${spring.profiles.active} 같은 배열 형태에 대해 properties를 N개로 호출하는 기능을 제공해주는 것이 가장 좋지만 지원하지 않는 현재로선 multi module에서 multi profile 을 사용하기 위해 EnvironmentPostProcessor를 통한 profile 그룹을 등록하는 방법이 현재로선 좋은 방법이 아닐까 싶다.

반응형
profile

파란하늘의 지식창고

@Bluesky_

도움이 되었다면 광고를 클릭해주세요