IDE는 정상인데 빌드만 Jackson 패키지 불일치와 KAPT 에러 대응 실패하는 이유

궁금하면 500원·2026년 4월 27일

미생의 개발 이야기

목록 보기
81/81

Jackson 타입 불일치 Spring Boot 4 마이그레이션에서의 KAPT 컴파일 에러 해결하기

공공 API 연동 프로젝트인 IdolGlow를 개발하며 Spring Boot 버전을 업그레이드하는 과정에서 직면한 가장 까다로운 이슈 중 하나는 Jackson 라이브러리의 버전 혼용 문제였습니다.

IDE에서는 정상적으로 보이던 코드가 빌드 시점에 KAPT 에러를 뱉어내며 중단되는 현상, 그리고 이를 해결하기 위한 실전 대응 전략을 기록합니다.


1. Jackson 2와 3의 충돌 문제 상황

Spring Boot 4 업그레이드 후의 이상한 에러

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.jacksontools.jackson이라는 두 개의 유사한 패키지가 공존하며 서로 다른 타입을 요구하고 있었습니다.


2. 어노테이션과 구현의 패키지 불일치

Spring Boot 4에서 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 패키지에 기반을 두고 있습니다.

  1. @JsonDeserialize 어노테이션은 com.fasterxml.jackson.databind.JsonDeserializer 타입을 상속받은 클래스를 요구합니다.
  2. 개발자가 tools.jackson.databind.JsonDeserializer를 상속받아 구현체를 만듭니다.
  3. KAPT는 컴파일 타임에 어노테이션의 제약 조건을 검사하다가, "구현체가 기대하는 부모 타입(com.fasterxml)이 아니다"라고 판단하여 에러를 발생시킵니다.

3. Tour 역직렬화 KAPT 에러

문제 발생 지점: TourFestivalApiClient.kt

Tour 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 { ... }
}

어노테이션 계약에 맞춰 import 교체

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를 호출할 수 있도록 설계되어 있으므로 컴파일 에러만 해결하면 런타임은 안전합니다.


4. 모듈 간 ObjectMapper 타입 정렬

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 인스턴스를 일관된 타입으로 공유하게 되었습니다.


5. 프로젝트 전체 Jackson 타입 전략

Spring Boot 4 마이그레이션 시 발생할 수 있는 이 혼란을 방지하기 위해 IdolGlow에 적용한 최종 전략은 다음과 같습니다.

최종 일관성 구조

  1. 전역 ObjectMapper: tools.jackson.databind.ObjectMapper 타입을 사용합니다.
    이는 최신 프레임워크의 빈 정의와 일치시키기 위함입니다.

  2. 커스텀 Deserializer/Serializer: @JsonDeserialize 등 어노테이션과의 계약을 위해 com.fasterxml.jackson 패키지 클래스들을 상속받아 구현합니다.

  3. Import 관리: 동일한 클래스명JsonParser, JsonToken 등 이 두 패키지에 모두 존재하므로, 자동 완성 시 반드시 패키지 경로를 확인합니다.

결론

기술의 세대교체 시기에는 이처럼 동일한 역할을 수행하는 서로 다른 패키지의 클래스들이 충돌을 일으킨다는것을 배웠습니다.

특히 컴파일 타임에 엄격한 규칙을 적용하는 Kotlin 환경에서는 이러한 패키지 정렬이 시스템 안정성을 배우게되었는데

이번 수정을 통해 IdolGlow 프로젝트는 빌드 안정성을 확보함과 동시에 최신 기술 스택으로의 전환을 성공적으로 마무리할 수 있었습니다.

profile
그냥 코딩할래요 재미있어요

0개의 댓글