
공공 API 연동 프로젝트인 IdolGlow를 개발하며 Spring Boot 버전을 업그레이드하는 과정에서 직면한 가장 까다로운 이슈 중 하나는 Jackson 라이브러리의 버전 혼용 문제였습니다.
IDE에서는 정상적으로 보이던 코드가 빌드 시점에 KAPT 에러를 뱉어내며 중단되는 현상, 그리고 이를 해결하기 위한 실전 대응 전략을 기록합니다.
IdolGlow 프로젝트를 Spring Boot 3.x에서 4.x로 업그레이드하면서, 빌드 단계에서 다음과 같은 치명적인 Kotlin Annotation Processing Tool 에러가 발생했습니다.
[ERROR] KAPT compilation error in TourFestivalApiClient.kt
Class `tools.jackson.databind.JsonDeserializer`
cannot be converted to Class `? extends com.fasterxml.jackson.databind.JsonDeserializer`
@JsonDeserialize(using = LenientFestivalItemsDeserializer::class)
^
Type mismatch at parameter 'using'
발견된 특이 현상:
IDE의 침묵: IntelliJ 등 IDE 편집기에서는 에러 표시되지 않아 코드가 정상인 것처럼 착각하게 만듭니다.
빌드 실패: ./gradlew build 실행 시 KAPT 단계에서 타입 불일치를 이유로 컴파일을 거부합니다.
패키지 분리: com.fasterxml.jackson과 tools.jackson이라는 두 개의 유사한 패키지가 공존하며 서로 다른 타입을 요구하고 있었습니다.
Spring Boot 4부터는 기존의 Jackson 2(fasterxml) 외에도 차세대 Jackson 3(tools.jackson)를 선택적으로 사용할 수 있게 되었습니다.
| 항목 | Jackson 2 (fasterxml) | Jackson 3 (tools.jackson) |
|---|---|---|
| 패키지 경로 | com.fasterxml.jackson.* | tools.jackson.* |
| 관리 주체 | FasterXML (오픈소스) | Google / Jackson 프로젝트 |
| Spring Boot 4 | 기본 권장 사항 | 선택 가능 |
| 하위 호환성 | 광범위한 생태계 지원 | 성능 개선 및 경량화 집중 |
Spring Boot 4 프로젝트에서 spring.json.mapper: jackson2 실제로는 Jackson 3인 tools.jackson을 지칭 설정을 활성화하면, 컨테이너에 등록되는 ObjectMapper 빈은 tools.jackson.databind.ObjectMapper 타입이 됩니다.
문제는 어노테이션(@JsonDeserialize)에 있습니다.
현재 유통되는 대부분의 Jackson 어노테이션 라이브러리는 여전히 com.fasterxml 패키지에 기반을 두고 있습니다.
@JsonDeserialize 어노테이션은 com.fasterxml.jackson.databind.JsonDeserializer 타입을 상속받은 클래스를 요구합니다.tools.jackson.databind.JsonDeserializer를 상속받아 구현체를 만듭니다.com.fasterxml)이 아니다"라고 판단하여 에러를 발생시킵니다.TourFestivalApiClient.ktTour API 응답 중 items 필드가 불규칙하게 빈 문자열 들어오는 문제를 해결하기 위해 작성된 커스텀 역직렬화기에서 에러가 터졌습니다.
수정 전 코드:
// 어노테이션은 com.fasterxml 소속
@JsonDeserialize(using = LenientFestivalItemsDeserializer::class)
val items: FestivalItems
// 구현체는 tools.jackson 소속을 상속함
class LenientFestivalItemsDeserializer : tools.jackson.databind.JsonDeserializer<FestivalItems>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): FestivalItems { ... }
}
KAPT는 런타임 호환성보다 컴파일 타임의 타입 계약을 중요하게 여깁니다.
따라서 어노테이션이 요구하는 com.fasterxml 패키지로 모든 관련 클래스 JsonParser, Deserializer 를 통일해야 합니다.
수정 후
// 모든 Jackson 관련 import를 fasterxml로 강제 통일
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.JsonToken
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
class LenientFestivalItemsDeserializer : com.fasterxml.jackson.databind.JsonDeserializer<FestivalItems>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): FestivalItems {
// 내부 로직은 동일하지만, 부모 클래스와 파라미터 타입이 계약을 충족함
}
}
핵심: ObjectMapper 빈 자체가 tools.jackson이더라도, Spring과 Jackson의 내부 브릿지가 런타임에 com.fasterxml 기반의 Deserializer를 호출할 수 있도록 설계되어 있으므로 컴파일 에러만 해결하면 런타임은 안전합니다.
TourApiClient.kt 문제 발생 지점IdolGlow는 멀티 모듈 구조로, eventinfo 모듈과 productpackage 모듈이 각각 Tour API를 호출합니다.
그런데 두 모듈의 주입 받는 ObjectMapper 타입이 달라 Dependency Injection 충돌 위험이 있었습니다.
eventinfo: tools.jackson.databind.ObjectMapper 사용 중.productpackage: com.fasterxml.jackson.databind.ObjectMapper를 import하여 사용 중.프로젝트 전역에서 생성된 빈은 tools.jackson 타입인데, 특정 모듈에서 com.fasterxml 타입을 주입받으려 하니 타입 불일치 경고나 주입 실패가 발생할 수 있는 상황이었습니다.
수정 전
import com.fasterxml.jackson.databind.ObjectMapper
@Component
class TourApiClient(private val objectMapper: ObjectMapper) // com.fasterxml 타입 기대
수정 후
import tools.jackson.databind.ObjectMapper // 프로젝트 전역 빈 타입으로 정렬
@Component
class TourApiClient(private val objectMapper: ObjectMapper) // tools.jackson 타입 주입 성공
이 수정을 통해 프로젝트의 모든 모듈이 동일한 ObjectMapper 인스턴스를 일관된 타입으로 공유하게 되었습니다.
Spring Boot 4 마이그레이션 시 발생할 수 있는 이 혼란을 방지하기 위해 IdolGlow에 적용한 최종 전략은 다음과 같습니다.
전역 ObjectMapper: tools.jackson.databind.ObjectMapper 타입을 사용합니다.
이는 최신 프레임워크의 빈 정의와 일치시키기 위함입니다.
커스텀 Deserializer/Serializer: @JsonDeserialize 등 어노테이션과의 계약을 위해 com.fasterxml.jackson 패키지 클래스들을 상속받아 구현합니다.
Import 관리: 동일한 클래스명JsonParser, JsonToken 등 이 두 패키지에 모두 존재하므로, 자동 완성 시 반드시 패키지 경로를 확인합니다.
기술의 세대교체 시기에는 이처럼 동일한 역할을 수행하는 서로 다른 패키지의 클래스들이 충돌을 일으킨다는것을 배웠습니다.
특히 컴파일 타임에 엄격한 규칙을 적용하는 Kotlin 환경에서는 이러한 패키지 정렬이 시스템 안정성을 배우게되었는데
이번 수정을 통해 IdolGlow 프로젝트는 빌드 안정성을 확보함과 동시에 최신 기술 스택으로의 전환을 성공적으로 마무리할 수 있었습니다.