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

Spring Boot 1.x 에서 잘 사용하고 있던 RelaxedPropertyResolver가 2.0 이후 Deprecated 되었다.

Binder를 사용하는 것을 Migration Guide에서 권장하고 있다.

Spring Boot 2.0 Migration Guide #Relaxed Binding 참고

Relaxed Bind 2.0 위키 문서


Spring Boot 1.x에서 RelaxedPropertyResolver를 사용하던 방식은 다음과 같다.

@Bean
public Map<String, Object> uiPropertiesMap(Environment environment) {
	return new RelaxedPropertyResolver(environment, "ui.").getSubProperties("");
}

단순히 environment에서 특정 문자열로 시작하는 properties를 모아 Map으로 반환하는 형태이다.


2.0 이후 제공하는 Binder를 사용하면 다음과 같다.

@Bean
public Map<String, Object> uiPropertiesMap(Environment environment) {
	return Binder.get(environment).bind("ui", Bindable.of(Map.class)).get();
}

하지만 기대하던 것과 결과가 다르게 나올 수도 있다.

아래와 같이 properties를 관리하고 있었다고 하면

ui.a=a value
ui.b=b value
ui.b.b1=b1 value
ui.c.c1=c1 value
ui.c.c2=c2 value

Spring Boot 1.x 에서 RelaxedPropertyResolver를 사용한 호출 결과는 아래와 같다.

{
	"a" : "a value",
	"b" : "b value",
	"b.b1" : "b1 value",
	"c.c1" : "c1 value",
	"c.c2" : "c2 value"
}

Spring Boot 2.x 에서 Binder를 사용한 호출 결과는 아래와 같다.

{
	"a" : "a value",
	"b" : "b value",
	"c" : {
		"c1" : "c1 value",
		"c2" : "c2 value"
	}
}

기존엔 key/value 로만 일괄 처리되던 부분이 하위 Object로 맵핑되어 계층 구조로 바인딩이 되게 되었다.

하지만 이 경우 b.b1의 값이 Binder를 통한 호출 시엔 사라진다.

이유는 이미 b라는 key를 통해 "b value"라는 value가 이미 b key에 등록된 이후 동일 키에 대해 하위 계층 구조가 value에 key/value 형태로 저장하려는 경우 이미 존재한 값을 overwrite 하지 않고 넘어가기 때문이다.

반대로 b.b1을 먼저 선언한 경우라면 아래와 같이 하위 계층 구조가 바인딩되고 b key에 저장된 "b value" 라는 value가 반대로 overwrite 되지 않고 넘어가게 된다.

{
	"a" : "a value",
	"b" : {
		"b1" : "b1 value"
	},
	"c" : {
		"c1" : "c1 value",
		"c2" : "c2 value"
	}
}

binder가 좀더 구조적으로 명확하게 관리를 해주는 대신 value 위치에서 sub key와 value를 섞어 쓴 경우 이와 같은 문제가 발생한다.

key와 value의 위치를 혼합해서 사용하지 않았다면 별 문제없이 binder로 이전이 되지만 그렇지 않은 경우 어쩔 수 없이 기존 RelaxedPropertyResolver를 별도로 구현하는 형태로 사용할 수 밖에 없다.

@Bean
public Map<String, Object> uiPropertiesMap(ConfigurableEnvironment environment) {
	Map<String, Object> uiPropertiesMap = new LinkedHashMap<String, Object>();
	for (org.springframework.core.env.PropertySource<?> source : environment.getPropertySources()) {
		if (source instanceof EnumerablePropertySource) {
			for (String name : ((EnumerablePropertySource<?>) source).getPropertyNames()) {
				String key = getSubKey(name, "ui.", "");
				if (key != null && !uiPropertiesMap.containsKey(key)) {
					uiPropertiesMap.put(key, source.getProperty(name));
				}
			}
		}
	}
	
	return uiPropertiesMap;
}

private static String getSubKey(String name, String rootPrefix, String keyPrefix) {
	if (name.startsWith(rootPrefix + keyPrefix)) {
		return name.substring((rootPrefix + keyPrefix).length());
	}
	return null;
}


반응형
profile

파란하늘의 지식창고

@Bluesky_

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