그동안 API Request, Response body 컨벤션을 snake_case로 해왔다. 그럴때마다 아래와 같이 @JsonNaming 아노테이션을 붙였다.
@JsonNaming(value= PropertyNamingStrategies.SnakeCaseStrategy.class)
클래스가 점점 많아지는데 매번 아노테이션을 달지 않고 하는 방법이 있는지 궁금했다.
먼저 Jackson에서 제공하는 ObjectMapper에 주목했다. Jackson은 자바용 Json 라이브러리로 data-processing 툴이다. Jackson의 ObjectMapper는 Json을 Object 데이터로 역직렬화하고 Object를 Json 데이터로 직렬화하는 작업을 해준다. 그런데 ObjectMapper를 커스터마이징하면 Parsing 과정에 조건이나 기능을 추가할 수 있다.
@Data
@Builder
public class AccountResponse {
private String name;
private String email;
private LocalDateTime registeredAt;
}
위 예시에 대한 ObjectMapper 커스터마이징을 @Configuration 아노테이션으로 했다. 신경써야 할 부분은 snake_case와 (추가) LocalDate 타입 설정이다.
@Configuration
public class ObjectMapperConfig {
@Bean
public ObjectMapper objectMapper()
위와 같이 ObjectMapper를 반환하는 메소드를 구현할 것이다. 꼭 @Bean으로 등록해야 default ObjectMapper가 아닌 내가 커스터마이징한 ObjectMapper를 불러 사용할 수 있다. 그리고 아래와 같이 설정했다.
{
var objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.SnakeCaseStrategy());
return objectMapper;
}
}
objectMapper.registerModule(new Jdk8Module());
필자는 JDK11를 사용하고 있고 JDK8 이상의 클래스를 처리할 수 있도록 설정했다.
objectMapper.registerModule(new JavaTimeModule());
LocalDate 타입인 registeredAt를 위한 설정이다.
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Deserialization(역직렬화)할 때 Object와 매핑되지 않는 Json 데이터가 존재하는 경우 무시하는 설정이다.
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
반대로 Serialization(직렬화)할 때 필드가 없는 Object인 경우 무시하는 설정이다.
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
LocalDate 타입을 직렬화할 때 ISO 8601 형식으로 하는 설정이다.
objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.SnakeCaseStrategy());
마지막으로 역직렬화할 때 Json body 컨벤션을 snake_case로 설정한다.
Swagger를 사용하여 테스트 해본 결과이다.
registered_at으로 snake_case로 직렬화된 것을 확인할 수 있었다. 하지만 또 다른 문제가 생겼다.
Swagger의 Schemas를 보니 camelCase로 나와있다. 즉 Swagger에도 ObjectMapper를 설정해야 한다.
아래와 같이 설정하면 된다.
@Configuration
public class SwaggerConfig {
@Bean
public ModelResolver modelResolver(ObjectMapper objectMapper){
return new ModelResolver(objectMapper);
}
}
modelResolver 메소드의 호출인데 매개변수 objectMapper는 어디서 어떻게 주입되는지 궁금해서 찾아봤다. 위에서 작성한 ObjectMapperConfig의 objectMapper()에서 Bean 등록한 ObjectMapper가 자동으로 호출되어 주입된다.
잘 바뀌었다.