문제 정의

검증 환경

본 절은 원문 사례·문서에 등장한 버전/도구를 정리한 것이다. 별도 실험실 재현이 명시되지 않은 항목은 일반화하지 않는다.

항목본문·원문에서 확인된 범위
런타임/도구Spring Boot 3.1.4
런타임/도구JDK 21
런타임/도구jdk21
문서 정리일2026-08-30
  • OS/CI 세부 값은 프로젝트마다 다르므로, 적용 전 로컬에서 동일 오류 메시지를 재확인한다.

Spring Boot 프로젝트를 JDK 21로 업그레이드한 직후, 컴파일 단계에서 다음과 같은 치명적 오류(Fatal error)가 발생합니다.

1
2
Fatal error compiling: java.lang.NoSuchFieldError:
Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field 'com.sun.tools.javac.tree.JCTree qualid'
  • 발생 단계: 빌드/컴파일 단계 (mvn compile 또는 IDE 빌드)
  • 관련 도구: javac (JDK 21), Lombok, Spring Boot
  • 표면 증상: 코드 자체는 문제없어 보이는데, 컴파일러가 내부 클래스를 해석하다 갑자기 NoSuchFieldError를 던지며 실패합니다.

에러 메시지에 등장하는 com.sun.tools.javac.tree.JCTree$JCImportqualid는 javac 내부의 추상 구문 트리(AST) 구현에 쓰이는 클래스와 필드입니다. 개발자가 직접 만지는 코드가 아니라, 컴파일러 플러그인 계층에서 참조하는 내부 API입니다.

증상 fingerprint

항목
에러 타입java.lang.NoSuchFieldError (Fatal error compiling)
정확한 메시지Class ...JCTree$JCImport does not have member field '...JCTree qualid'
발생 단계컴파일 (빌드)
트리거JDK 21로의 업그레이드 직후
전형적인 잘못된 추측“내 코드/의존성이 무언가 잘못됐다” — 실제로는 javac 내부 구조와 Lombok의 버전 불일치
빠른 판단 기준JDK 21 + 상대적으로 오래된 Lombok(1.18.30 미만) 조합이면 거의 확실히 이 원인

원인 탐구

NoSuchFieldError는 컴파일 타임(Runtime)에 특정 클래스/필드가 바이트코드로 존재할 것으로 기대했는데, 실제로는 그 필드가 사라지거나 이름이 바뀌었을 때 발생합니다.

JDK 21에서는 javac 내부의 JCTree$JCImport 클래스 구조가 변경되었습니다. 구체적으로는 import 문을 표현하는 AST 노드에서 qualid라는 필드가 사라지거나 이름이 바뀌었습니다. 문제는 다음 두 가지가 함께 벌어진다는 점입니다.

  1. javac가 JDK 21에서 바뀌었고
  2. Lombok 같은 컴파일러 플러그인은 컴파일 시점에 이 javac 내부 구조를 직접 들여다보며 코드를 주입합니다.

Lombok은 어노테이션 처리 단계에서 javac의 AST를 읽고 수정하는 방식으로 동작합니다. 오래된 Lombok 버전은 JDK 21 이전의 JCImport.qualid 필드 구조를 하드코딩해 기대하고 있습니다. JDK 21로 바뀐 구조에서는 그 필드가 없으므로 NoSuchFieldError가 터집니다.

즉, 이 오류는 “패키지가 없다"거나 “코드가 틀렸다"는 문제가 아니라, javac 내부 구조 변화를 Lombok 버전이 따라가지 못한 버전 불일치 문제입니다.


근본 원인 분석

근본 원인은 확인되었으며, Lombok의 버전 호환성 한계입니다.

  • JDK 21과 호환되는 **최소 Lombok 버전은 1.18.30**입니다. (출처: Lombok 저장소의 이슈 및 커뮤니티 검증)
  • Spring Boot에서 Lombok 버전을 직접 명시하지 않고 Spring Boot BOM(의존성 관리)에 맡긴다면, **최소 Spring Boot 버전은 3.1.4**가 됩니다. Spring Boot 3.1.4부터 BOM이 관리하는 Lombok 버전이 1.18.30 이상이기 때문입니다.

흔한 오해 바로잡기

많은 개발자가 이 오류를 보고 “내가 이제 막 업그레이드했는데 왜 JDK가 문제를 일으키지?“라고 생각합니다. 하지만 JDK 21 자체는 정상이며, 문제는 이 JDK 21을 지원하는 버전이 되기 전의 Lombok을 쓰고 있는 것입니다. JDK가 아니라 의존성(플러그인) 버전이 뒤처진 상황입니다.

원본 출처에는 포함되지 않은 일반적인 점검 기준으로, pom.xml에서 Lombok을 직접 버전 지정했는지(예: <lombok.version>) 여부가 원인 분기점이 됩니다.


코드 해결책

원인별 분기표 (버전 호환성 판단)

현재 상황필요한 조치해결 방향
Lombok 버전을 프로젝트에서 직접 관리함Lombok을 1.18.30 이상으로 올린다버전 명시 후 재빌드
Spring Boot BOM의 Lombok 버전을 그대로 씀Spring Boot를 3.1.4 이상으로 올린다BOM이 호환 Lombok을 관리
Spring Boot BOM을 쓰지만 최신으로 올릴 수 없음Lombok 버전만 BOM과 다르게 재정의 (비권장)임시 해결책이지만 호환성 위험

임시 해결책 vs 권장 해결책

이 문제는 버전 업그레이드가 근본 해결책이며, “설정만 바꿔 없애는” 임시방편이 존재하지 않습니다. 다만 업그레이드 접근 방식에 따라 두 갈래로 나뉩니다.

① Spring Boot BOM을 그대로 사용하는 경우 (권장)

Spring Boot 버전만 올리면 Lombok 버전까지 함께 관리되므로 가장 안전합니다.

1
2
3
4
5
6
7
<!-- pom.xml 또는 build.gradle에서 Spring Boot 버전을 3.1.4 이상으로 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.1.4</version>
    <relativePath/>
</parent>

② Lombok 버전을 직접 지정하는 경우

Lombok 의존성을 명시하고 있다면 1.18.30 이상으로 올립니다.

1
2
3
4
5
6
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>

Gradle의 경우:

1
2
3
4
dependencies {
    compileOnly 'org.projectlombok:lombok:1.18.30'
    annotationProcessor 'org.projectlombok:lombok:1.18.30'
}

잘못된 해결책 (피해야 할 방법)

Spring Boot 공식 문서는 BOM이 관리하는 써드파티 의존성 버전을 임의로 덮어쓰는 것에 대해 경고합니다.

각 Spring Boot 릴리즈는 특정 써드파티 의존성 집합에 맞춰 설계되고 테스트되었습니다. 버전을 덮어쓰면 호환성 문제가 발생할 수 있습니다. (Spring Boot 문서)

  • 비권장: Spring Boot가 3.1.4 미만인 상태에서 Lombok만 1.18.30으로 강제 지정하는 것
    • 물론 Lombok 버전을 프로젝트에서 직접 <lombok.version>으로 강제하면 동작할 수는 있지만, Spring Boot가 테스트한 조합 밖이므로 다른 미묘한 호환성 문제가 나타날 수 있습니다.
  • 비권장: 오류를 무시하거나 -proc:none 등으로 어노테이션 처리를 꺼버리는 우회
    • Lombok이 생성하는 코드(getter/setter/빌더 등)가 사라져 오히려 다른 컴파일 오류가 쏟아집니다.

향후 예방 조치

JDK 메이저 버전 업그레이드는 빌드 도구와 어노테이션 프로세서(특히 Lombok)의 호환성 체크를 먼저 하고 진행해야 합니다.

업그레이드 전 확인 사항

  • Lombok 최신 버전 확인: JDK 버전별로 어떤 Lombok 버전이 필요한지 릴리스 노트에서 미리 확인합니다.
  • Spring Boot BOM 버전 확인: BOM이 관리하는 Lombok 버전이 현재 JDK를 지원하는지 확인합니다.
  • 의존성 버전 덮어쓰기 지양: Spring Boot BOM 버전을 임의로 바꾸지 않고, Spring Boot 전체 업그레이드를 우선 고려합니다.

검증 명령 (해결 후 확인)

업그레이드가 반영됐는지와 실제로 컴파일되는지 아래 명령으로 확인합니다.

1
2
3
4
5
# Lombok 버전 확인 (실제 해석되는 의존성 버전)
mvn dependency:tree -Dincludes=org.projectlombok

# 빌드가 정상 완료되는지 확인 (컴파일 단계)
mvn clean compile
1
2
3
# Gradle 프로젝트라면
./gradlew dependencies --configuration annotationProcessor
./gradlew clean compileJava

mvn clean compile (또는 ./gradlew clean compileJava)이 **빌드 성공(BUILD SUCCESS)**으로 끝나면 오류가 해결된 것입니다. 의존성 트리에서 Lombok이 1.18.30 이상으로 표시되는지도 함께 확인하는 것이 좋습니다.

실전 적용 체크

  • 로컬: JDK 버전(java -version)과 Lombok 버전을 함께 기록해 두면 회귀 추적이 쉽습니다.
  • CI: CI가 사용하는 JDK 이미지의 버전과 pom.xml/build.gradle의 Lombok 버전을 일치시킵니다. 로컬만 다른 JDK로 빌드하다가 CI에서 이 오류가 나는 경우가 흔합니다.
  • 프로덕션 빌드: 배포 파이프라인의 JDK와 Lombok 버전을 버전 핀(pin)으로 고정해 재현성을 확보합니다.

DevTrace verdict

이 문제의 핵심은 “JDK 21이 잘못됐다"가 아니라, JDK 21에서 바뀐 javac 내부 AST 구조를 후속 버전(Lombok 1.18.30 이상)이 따라잡기 전의 오래된 Lombok을 쓰고 있다는 버전 불일치다.


출처: StackOverflow — Compilation error after upgrading to JDK 21: NoSuchFieldError: JCImport does not have member field JCTree qualid (질문: Honza Zidek, 채택 답변 기준)

DevTrace 결론

핵심은 JDK 21에서 바뀐 javac 내부 구조를 오래된 Lombok이 따라가지 못한 버전 불일치이며, Lombok 1.18.30 이상 또는 이를 포함하는 Spring Boot 3.1.4 이상으로 버전을 맞추면 해결된다.

원문 출처는 문제 발견의 단서이며, 위 판단과 점검 항목은 DevTrace의 독자 분석이다.