Composite 패턴을 통한 ObjectMapper 통합 관리
문제의 발생
- ObjectMapper의 NamingStrategies를 여러개 쓰이게 되는 상황이 발생했다.
- 여러 종류의 외부 API를 반영하는 서비스였기에 외부 API 마다의 json 네이밍 컨벤션이 달랐다.
- 특히, 하나의 NamingStrategies가 늘어날 때 마다 ObjectMapper에 대한 빈을 하나 더 등록하면서 주입 받을 때도 빈이 여러개라서 신경을 써야 하는 등의 불편함이 발생했다.
이 문제를 해결하는 방법을 찾던 중, 디자인 패턴인 Composite 패턴을 통해 해결하기로 했다.
Composite 패턴이란?
- Component 라 불리는 최상위 클래스와 Leaf, Composite 라 불리는 하위 클래스가 협력하는 패턴이다.
- Leaf는 Composite에서 사용될 클래스로 Composite에 여러 다른 구현체가 등록되어 사용된다.
- 클라이언트에서는 다양한 구현체를 한 번에 사용 가능하다.
뭔 소리지? 다이어그램을 살펴보자
구조는 알겠으니 이제 코드를 통해 알아보자
- 아래 코드에서 클래스의 각 역할은 다음과 같다.
- PropertyNamingStrategy == Component
- CompositePropertyNamingStrategy == Composite
- ComponentPropertyNamingStrategy의 생성자로 받는 PropertyNamingStrategy == Leaf
- objectMapper 메서드 == 클라이언트 (Component를 사용하는 주체)
CompositePropertyNamingStrategy
public class CompositePropertyNamingStrategy extends PropertyNamingStrategy {
private final PropertyNamingStrategy[] strategies;
public CompositePropertyNamingStrategy(final PropertyNamingStrategy... strategies) {
this.strategies = strategies;
}
@Override
public String nameForField(final MapperConfig<?> config, final AnnotatedField field, final String defaultName) {
String name = defaultName;
for (final PropertyNamingStrategy strategy : strategies) {
name = strategy.nameForField(config, field, name);
}
return name;
}
@Override
public String nameForGetterMethod(final MapperConfig<?> config, final AnnotatedMethod method, final String defaultName) {
String name = defaultName;
for (final PropertyNamingStrategy strategy : strategies) {
name = strategy.nameForGetterMethod(config, method, name);
}
return name;
}
@Override
public String nameForSetterMethod(final MapperConfig<?> config, final AnnotatedMethod method, final String defaultName) {
String name = defaultName;
for (final PropertyNamingStrategy strategy : strategies) {
name = strategy.nameForSetterMethod(config, method, name);
}
return name;
}
}
objectMapper 메서드
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
.setPropertyNamingStrategy(
new CompositePropertyNamingStrategy(
PropertyNamingStrategies.SNAKE_CASE,
PropertyNamingStrategies.LOWER_CAMEL_CASE));
}
결론
- 위와 같은 코드를 통해 ObjectMapper를 하나만 사용할 수 있게 되었다.
- 또한, 여러개의 PropertyNamingStrategy를 한 번에 사용할 수 있게 되었다.