kotlin 을 사용하면, kotlin 용의 jackson library를 설치해야한다.
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
spring initializer 로 스프링 프로젝트를 구성하다보면, 자동으로 해당 라이브러리가 설치되어있기 때문에 별 생각없이 쓰게 된다.
개인적으로 토이 프로젝트를 하던 도중, 해당 라이브러리가 설치가 안되어있어서 json 변환관련 에러가 나서 도대체 이것이 뭔지 찾아보게 되었다.
왜 jackson-module-kotlin 없이는 변환이 안될까?
기본적으로 jackson 자체는 코틀린을 위한 라이브러리가 아니다.
코틀린의 data class 와 같은 것들은 기본 jackson library 가 이해하지 못하기 때문에, 변환을 할 수 없는 것이다.
코틀린의 jacksonObjectMapper() 는 무엇인가?
그리고 코드에서 사용할때는 보통 아래의 objectMapper를 쓰게 되는데,
*jacksonObjectMapper*() 의 실체는 무엇인가?
jsonMapper 라고 하는 ObjectMapper 의 구현체에 kotlinModule 을 추가한 것이다. ObjectMapper 중 json 에 특화된 구현체라고 보면된다.
fun jacksonObjectMapper(): ObjectMapper
= jsonMapper { addModule(kotlinModule()) }
어떻게 자동으로 등록되는가?
그런데 일반적으로 따로 설정을 하지 않아도, 해당 라이브러리만 설치하면 kotlin 에 맞게 json 변환이 이루어지는데 어떻게 가능한 것일까?
jackson 의 module class
jackson 라이브러리의 자동구성 대해 설명하기 전에, jackson 의 일부 개념에 대해서 짚고 넘어가보자.
ObjectMapper는 registerModule 을 통해서, 기능을 확장할 수 있다.
이 registerModule에 jackson 의 module 클래스 타입을 받게 된다.
자주 사용되는 아래의 모듈은 module 클래스를 확장하는 하위 타입들이다.
- Jdk8Module
- JavaTimeModule
- KotlinModule
val objectMapper = ObjectMapper().apply {
registerModule(Jdk8Module())
registerModule(JavaTimeModule())
registerModule(kotlinModule)
}
auto configuration
spring boot 에는 auto configuration 이라는게 있다.
→ 라이브러리를 설치하게 되면 자동으로 관련된 Bean 들을 등록해주게 되는데, 이를 auto configuration 이라고 한다.
Jackson 의 경우 JacksonAutoConfiguration 클래스를 통해서 자동구성이 된다.
해당 클래스의 내부(inner) 클래스에 JacksonObjectMapperConfiguration 클래스가 있는데, ObjectMapper 를 빈 메서드로 등록하는 코드이다.
// JacksonAutoConfiguration.class
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
static class JacksonObjectMapperConfiguration {
@Bean
@Primary
@ConditionalOnMissingBean
ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
return builder.createXmlMapper(false).build();
}
}
Jackson2ObjectMapperBuilder 를 빌드하게 되면,
ObjectMapper 의 static method 인 findModules를 호출하게 된다. 그러면 클래스 로더를 통해, jackson 의 Module 하위 클래스들을 모두 등록하게 된다.
// ObjectMapper.class
public static List<Module> findModules(ClassLoader classLoader)
{
ArrayList<Module> modules = new ArrayList<Module>();
ServiceLoader<Module> loader = secureGetServiceLoader(Module.class, classLoader);
for (Module module : loader) {
modules.add(module);
}
return modules;
}
'스프링부트' 카테고리의 다른 글
나는 테스트에서 @Transactional 붙이지 않겠다.. (feat. 동시성 삽질기) (0) | 2024.05.02 |
---|---|
jackson for kotlin part3. 삽질기 (feat. jacksonObjectMapper) (0) | 2024.03.10 |
jackson for kotlin part2. 커스터마이징 (0) | 2024.03.10 |
영속성 컨텍스트와 트랜잭션의 관계 (1) | 2024.02.18 |
@RequestBody, @ResponseBody 의 동작 원리 (1) | 2023.10.21 |