[트러블슈팅] ADO + Java 21 에러: Maven ‘release version 21 not supported’ 완벽 해결

▶ TECH LOG — Troubleshooting


서론: 은행 차세대 ADO 파이프라인 컴파일 단계의 붕괴

은행 차세대 시스템의 Java 21 마이그레이션 도중 마주친 세 번째 벽입니다. 이전 단계에서 ADO(Azure DevOps) 자체 호스팅 에이전트의 JAVA_HOME을 JDK 21로 맞춰 스캐너 실행 크래시는 잡았습니다. 하지만 안심하고 파이프라인을 태운 순간, 이번에는 소스코드를 바이트코드로 변환하는 Maven 빌드(Compile) 단계에서 새빨간 에러가 터졌습니다. 관련해서 Java 21 + SonarQube 에러 해결법 글도 참고하면 도움이 됩니다.

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project core-banking:
Fatal error compiling: error: release version 21 not supported

이 에러로 인해 컴파일된 .class 파일 자체가 생성되지 않았고, 그 결과 SonarQube v2025.2 Data Center 서버로 정적 분석 데이터를 보낼 수 없어 전체 QA 배포 파이프라인이 전면 중단되었습니다.


원인 분석: source/target 대신 도입된 release 플래그의 반란

에러의 핵심은 ‘Maven Compiler Plugin’의 구버전(3.8.1)이 Java 21의 새로운 --release 플래그를 해석하지 못하는 현상입니다.

과거 Java 8 시절에는 크로스 컴파일을 위해 <source><target> 속성을 분리해서 사용했습니다. 하지만 이 방식은 JDK 내부 API 시그니처까지는 맞춰주지 못해 런타임에 NoSuchMethodError가 터지는 치명적인 단점이 있었습니다. 이를 해결하기 위해 Java 9부터는 타겟 버전의 표준 API까지 완벽하게 맞춰주는 --release 플래그가 도입되었고, 현재는 이 방식이 권장 표준입니다.

우리는 차세대 표준에 맞춰 pom.xml에 당당히 <release>21</release>를 선언했지만, 3.8.1 버전의 컴파일러 플러그인은 “21이라는 릴리스 버전은 내 사전에 없다”며 컴파일을 자체적으로 거부한 것입니다. 플러그인 자체가 Java 21 명세가 추가되기 전에 릴리스된 구형이었기 때문입니다.


실패한 시도들: 헛다리 짚었던 2가지 삽질기

단순한 버전 문제임에도 인프라 환경과 겹쳐 잘못된 판단으로 귀중한 시간을 허비했습니다.

  1. ❌ 빌드 타겟(Target) 다운그레이드 꼼수
    “컴파일러가 21을 못 읽으면, pom.xml<maven.compiler.release> 값을 17로 낮춰서 통과만 시켜보자”라고 접근했습니다. 하지만 소스 코드 내부에 이미 Java 21의 핵심 기능인 가상 스레드(Virtual Threads)와 레코드 패턴(Record Patterns) 문법이 적용되어 있었기 때문에, 당연히 컴파일 타임 구문 분석에서 수많은 문법 에러를 뿜어내며 실패했습니다. 언어 마이그레이션에 꼼수나 롤백은 통하지 않습니다.
  2. ❌ ADO 에이전트의 Maven 엔진(CLI) 강제 업데이트
    “에이전트에 깔린 메이븐 자체가 구버전이라 그런가?”라는 의심을 품고, 인프라 팀을 달달 볶아 폐쇄망 에이전트 OS에 설치된 Maven 자체를 최신 3.9.x로 올렸습니다. 터미널 mvn -v는 훌륭하게 최신 버전을 가리켰지만 ADO 파이프라인은 똑같이 터졌습니다. 원인은 ADO의 실행 엔진이 아니라, 프로젝트 pom.xml에 하드코딩된 컴파일러 플러그인의 버전이었기 때문입니다.

해결 방법: Compiler 플러그인 업그레이드 및 ADO 환경 동기화

해결책은 명확합니다. 프로젝트의 pom.xml을 열어 maven-compiler-plugin을 Java 21을 완벽히 지원하는 3.11.0 이상(2026년 기준 최신 안정화 버전)으로 고정해야 합니다. 그리고 성공적으로 컴파일된 결과물을 SonarQube 스캐너가 정확히 파싱할 수 있도록 소나 프로퍼티도 동기화합니다.

1. pom.xml 완벽 설정

<properties>
    <!– Maven Compiler Java 21 릴리스 강제 –>
    <maven.compiler.release>21</maven.compiler.release>
    <!– SonarQube v2025.2 분석 엔진 타겟 동기화 –>
    <sonar.java.source>21</sonar.java.source>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version> <!– 최소 3.11.0 이상으로 명시 –>
        </plugin>
    </plugins>
</build>

2. azure-pipelines.yml 컴파일 태스크 점검

ADO 파이프라인에서 Maven 태스크를 실행할 때, JDK 옵션이 명확히 21을 가리키도록 YAML 파일을 다듬어줍니다. 컴파일러 플러그인이 21을 지원하더라도, 정작 ADO 빌드 태스크가 구형 JDK를 주입하면 다시 에러가 발생합니다.

– task: Maven@4
  inputs:
    mavenPomFile: ‘pom.xml’
    goals: ‘clean verify sonar:sonar’
    javaHomeOption: ‘JDKVersion’
    jdkVersionOption: ‘1.21’ # ADO 런타임에 명확히 21 버전을 지시
    mavenOptions: ‘-Xmx3072m’

결론: 진정한 마이그레이션은 생태계 전체의 버전업이다

release version 21 not supported 에러는 깊게 파고들면 ‘플러그인의 지원 명세’라는 아주 기초적인 원인에서 출발합니다. 하지만 수십 개의 모듈과 수백 명의 개발자가 엮인 은행 차세대 프로젝트에서는 이렇게 사소한 버전 미스매치 하나가 ADO 파이프라인 전체를 멈춰 세우는 병목이 됩니다.

소스코드가 Java 21로 작성되었다면, 이를 빌드하는 maven-compiler-plugin과 품질을 검증하는 SonarQube(sonar.java.source), 그리고 이를 실행하는 ADO Agent JDK까지 모든 톱니바퀴가 완벽하게 동기화되어야 합니다. 이 삼박자가 맞았을 때 비로소 차세대 시스템의 안정적이고 빠른 배포 사이클을 누릴 수 있을 것입니다.

📌 Tech Log Review
다음 포스트에서는 Gradle 8.5+ 환경에서 발생하는 Java 21 + SonarQube 플러그인 의존성 충돌과 해결 과정을 심도 있게 다루겠습니다. 빌드 도구만 다를 뿐 겪게 되는 고통의 결은 비슷합니다.
자세한 내용은 Azure DevOps Maven Task 공식 문서에서 확인할 수 있습니다.