Flutter로 안드로이드 빌드를 돌리다가 Unsupported class file major version 65가 뜨거나 "Your project's Gradle version is incompatible with the Java version that Flutter is using for Gradle" 경고로 멈출 수 있습니다. 이 증상의 원인은 프로젝트 코드가 아니라 Flutter가 고른 JDK와 Gradle 하한입니다. JAVA_HOME을 17로 맞춰 두었는데도 같은 오류가 반복되는 경우가 많습니다. Flutter는 JAVA_HOME보다 Android Studio에 딸린 JDK를 먼저 씁니다. 이 글은 Flutter 도구의 실제 JDK 선택 순서와 Gradle 하한을 저장소 소스에서 확인합니다. 지금 쓰는 Java를 특정한 뒤 최소 수정으로 끝내는 순서도 함께 정리합니다.
오류 문구 세 가지는 서로 다른 지점을 가리킵니다
같은 "Java가 안 맞는다"는 증상이지만 문구마다 확인할 곳이 다릅니다.
- Unsupported class file major version 65 — Groovy와 Gradle이 Java 21로 컴파일된 클래스 파일을 읽지 못한 상태입니다. 빌드 스크립트를 해석하는 단계에서 멈추므로 프로젝트 코드는 아직 컴파일되지도 않았습니다.
- Your project's Gradle version is incompatible with the Java version that Flutter is using for Gradle — Flutter CLI가 빌드를 시작하기 전에 호환표를 보고 미리 내는 경고입니다. Gradle이 실제로 실패하기 전에 알려 준다는 점이 다릅니다.
- Android Gradle plugin requires Java 17 to run. You are currently using Java 11 — 안드로이드 공식 문서에 실린 AGP의 JDK 하한 오류입니다. AGP 8.x는 JDK 17을 요구합니다.
세 문구 중 첫 번째가 나왔다면 Java가 Gradle보다 앞서 있습니다. 세 번째가 나왔다면 Java가 AGP보다 뒤처진 상태입니다. 방향이 반대이므로 처방도 반대입니다.
Unsupported class file major version 65는 Java 21이라는 뜻입니다
숫자 65는 Java 버전이 아니라 클래스 파일 형식 번호입니다. Java Virtual Machine 명세는 이 번호의 범위를 45부터 69까지로 규정합니다. Java SE 25를 지원하는 JVM이 그 범위를 모두 처리한다는 설명도 함께 실려 있습니다. 45가 Java 1.1이므로 이후 번호는 Java 버전에 44를 더한 값입니다.
- Java 8 → 52
- Java 11 → 55
- Java 17 → 61
- Java 21 → 65
- Java 25 → 69
그래서 major version 65는 Java 21로 만든 클래스 파일이라는 뜻입니다. 61은 Java 17, 69는 Java 25입니다. 오류에 찍힌 숫자에서 44를 빼면 문제를 일으킨 Java 버전이 바로 나옵니다. 65가 나왔다면 Android Studio가 최근 버전에서 기본으로 들고 오는 JDK 21이 원인일 가능성이 큽니다.
Flutter는 어떤 Java를 고르나요
여기서 대부분이 한 번 미끄러집니다. Flutter 저장소의 packages/flutter_tools/lib/src/android/java.dart에는 JDK를 찾는 함수가 있습니다. 실제 확인 순서는 다음과 같습니다.
- flutter config --jdk-dir로 지정한 경로
- Android Studio에 딸린 JDK
- JAVA_HOME 환경 변수
- PATH에서 찾은 java
두 번째가 세 번째보다 앞이라는 점이 핵심입니다. JAVA_HOME을 17로 맞춰 두어도 Android Studio의 번들 JDK가 21이면 Flutter는 21을 씁니다. 터미널에서 java -version을 쳐서 17이 나온다고 해도 그 값이 빌드에 쓰인다는 보장이 없습니다.
같은 파일 위쪽의 주석은 이 순서를 "Android Studio 번들, JAVA_HOME, PATH" 세 단계로만 적어 둡니다. 가장 앞에 있는 jdk-dir 항목이 주석에는 빠져 있습니다. 문서와 주석만 보고 판단하면 순서를 잘못 잡게 되는 지점입니다.
지금 Flutter가 쓰는 Java와 Gradle을 확인합니다
추측을 걷어내려면 두 값을 먼저 봅니다.
flutter doctor -v
# [✓] Android toolchain ...
# • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
# • Java version OpenJDK Runtime Environment (build 21.0.x)
Java binary at 줄에 찍히는 경로가 Flutter가 실제로 쓰는 JDK입니다. JAVA_HOME과 다르다면 그 차이가 원인입니다.
프로젝트의 Gradle 버전은 wrapper 설정 파일에 들어 있습니다.
grep distributionUrl android/gradle/wrapper/gradle-wrapper.properties
# distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
Flutter CLI는 이 두 값을 자체 호환표와 비교해 경고를 냅니다. 호환 여부만 빠르게 보려면 다음 명령을 씁니다.
flutter analyze --suggestions
Java 21에 필요한 Gradle은 8.4인가 8.5인가
두 공식 출처의 표가 갈립니다. 먼저 Flutter 저장소의 호환표입니다. gradle_utils.dart의 상수에 Java별 최소 Gradle이 들어 있습니다.
| Java | Flutter 소스의 최소 Gradle |
|---|---|
| 17 | 7.3 |
| 19 | 7.6 |
| 20 | 8.1 |
| 21 | 8.4 |
| 22 | 8.7 |
| 23 | 8.10 |
| 24 | 8.14 |
| 25 | 9.1.0 |
Gradle 공식 호환표는 열을 둘로 나눕니다. Java 21의 경우 툴체인 지원은 8.4, Gradle 자체를 그 Java로 실행하는 지원은 8.5부터입니다. Java 22는 8.7과 8.8, Java 25는 양쪽 모두 9.1.0입니다.
Flutter 소스의 8.4는 Gradle 표의 툴체인 열과 같은 값입니다. 빌드 JVM을 Java 21로 두는 조건은 8.5입니다. Java 21을 쓰면서 Gradle 8.4에 머무르면 툴체인으로는 맞아도 Gradle 실행 자체가 다시 막힐 수 있습니다. Java 21로 빌드한다면 Gradle 8.5 이상을 기준으로 잡는 편이 안전합니다.
Flutter가 아는 상한도 함께 봐 둡니다. 같은 파일에 최대 Gradle 9.5.0, 최대 AGP 9.4, 권장 최소 Java 17이 상수로 들어 있습니다. 이 범위를 넘는 조합은 Flutter가 검증한 적이 없다는 뜻입니다. 오류가 나도 도구가 안내해 주지 않습니다.
Java 25까지 올릴 때 걸리는 지점
Gradle 9.1.0부터 Java 25 실행이 지원되고 Flutter 소스의 표도 같은 값을 적습니다. 그런데 Flutter 저장소 이슈 187223은 Java 25로 Flutter 안드로이드 빌드를 돌리면 네이티브 접근 오류와 클래스 버전 오류로 여전히 실패한다고 기록합니다. 이유로 든 것이 AGP입니다. AGP는 Java 17과 일부 21까지만 검증되어 있어 Gradle 쪽이 준비되어도 AGP에서 막힌다는 설명입니다.
Flutter 소스의 AGP 호환표도 이 상태와 어긋나지 않습니다. AGP 8.0 이상 구간에 최소 Java 17만 적혀 있고 상한이 없습니다. 상한이 없다는 것은 검증을 마쳤다는 뜻이 아닙니다. 그 위 조합을 도구가 판단하지 않는다는 의미입니다. Java 25는 지금 Flutter 안드로이드 빌드의 현실적인 선택지가 아닙니다.
공식 마이그레이션 가이드의 7.3~7.6.1 처방을 그대로 따르면 안 되는 이유
Flutter 공식 마이그레이션 문서에는 두 부분이 따로 있습니다. 앞부분은 클래스 버전 61(Java 17) 사례를 다루면서 Gradle을 7.3부터 7.6.1 사이로 올리라고 안내합니다. 이 값은 Android Studio Flamingo가 번들 Java를 11에서 17로 바꾼 시점의 처방입니다.
뒷부분은 클래스 버전 65(Java 21)를 따로 다룹니다. 여기서 안내하는 값은 Gradle 7.3~7.6.1이 아닙니다. flutter analyze --suggestions로 호환 여부를 보라고 안내합니다. flutter doctor로 Java 버전을 확인하고 flutter config --jdk-dir로 쓸 Java를 지정하는 절차도 함께 제시합니다.
앞부분만 읽고 7.6.1로 맞추면 Java 21 환경에서는 같은 오류가 그대로 남습니다. 7.6.1은 Java 21을 실행하지 못합니다. 같은 페이지 안에서 처방이 두 갈래로 갈립니다. 오류에 찍힌 클래스 버전 번호를 먼저 확인하고 해당 절을 골라야 합니다.
최소 수정 1: Flutter가 쓸 JDK를 고정합니다
Android Studio를 내리거나 JDK를 지우는 방식은 다른 프로젝트에 영향을 줍니다. Flutter가 참조하는 JDK만 바꾸는 편이 되돌리기 쉽습니다.
flutter config --jdk-dir=/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home
flutter doctor -v
설정값은 사용자 홈의 설정 파일에 저장됩니다. macOS와 리눅스에서는 XDG_CONFIG_HOME이 설정되어 있지 않다면 ~/.config/flutter/settings이고, 예전 경로인 ~/.flutter_settings 파일이 남아 있으면 그쪽을 먼저 읽습니다. Windows에서는 %APPDATA%\.flutter_settings입니다. 현재 값을 확인하려면 flutter config --list를 씁니다.
되돌릴 때는 설정 파일에서 jdk-dir 항목을 지웁니다. 그러면 선택 순서가 다시 Android Studio 번들 JDK부터 시작합니다.
프로젝트가 Java 21을 요구한다면 반대 방향으로 풀어야 합니다. JDK를 17로 낮추는 대신 Gradle을 8.5 이상으로 올리고 AGP 하한을 함께 확인합니다. AGP와 Gradle의 짝은 반대 방향으로도 깨집니다. 한쪽만 올리면 다른 오류가 납니다. 이 조합은 Android Studio 업데이트 후 Gradle 오류에서 따로 정리했습니다.
최소 수정 2: Gradle과 AGP를 함께 올립니다
Java 21을 유지하기로 했다면 순서를 지켜야 합니다.
cd android
./gradlew wrapper --gradle-version=8.5
wrapper를 올린 뒤에는 AGP 하한을 확인합니다. 안드로이드 공식 문서는 AGP 8.x가 JDK 17을 요구한다고 적고, 그보다 낮은 JDK로 빌드하면 플러그인 적용 단계에서 멈춘다고 설명합니다. Gradle을 8.5로 올렸는데 프로젝트 AGP가 8.0 미만이면 그 지점에서 다시 실패합니다.
Flutter 플러그인을 함께 쓰는 프로젝트라면 AGP 9 계열에서 적용 단계가 따로 깨집니다. Flutter AGP 9 빌드 실패 해결에 그 증상과 확인 순서를 정리해 두었습니다.
흔히 시도하지만 효과가 없는 조치
- JAVA_HOME만 바꿉니다. Android Studio 번들 JDK가 우선이므로 순서상 반영되지 않습니다.
- flutter clean을 반복합니다. 캐시를 지워도 JDK 선택 경로는 바뀌지 않습니다.
- gradle-wrapper.properties만 손봅니다. Java 쪽이 21이면 Gradle 7.x대에서는 같은 오류가 반복됩니다.
- Android Studio의 Gradle JDK 설정만 바꿉니다. 그 설정은 IDE 빌드에만 적용됩니다. Flutter CLI는 번들 JDK 경로를 직접 읽습니다.
- 클래스 버전 숫자를 보지 않고 마이그레이션 문서 앞부분의 7.3~7.6.1을 그대로 적용합니다.
적용 전후 확인 목록
- flutter doctor -v의 Java binary at 경로를 기록합니다.
- 오류에 찍힌 클래스 버전 숫자에서 44를 빼 Java 버전을 확인합니다.
- android/gradle/wrapper/gradle-wrapper.properties의 distributionUrl을 확인합니다.
- Java 21이면 Gradle 8.5 이상인지, AGP가 8.x 이상인지 확인합니다.
- flutter config --jdk-dir로 JDK를 고정한 뒤 같은 경로가 doctor에 찍히는지 확인합니다.
- flutter analyze --suggestions로 AGP, Java, Gradle 조합 경고가 사라졌는지 확인합니다.
- 되돌릴 때는 설정 파일에서 jdk-dir 항목을 지우고 doctor로 원래 경로가 돌아왔는지 확인합니다.
참고 자료
- Flutter java.dart — JDK 선택 순서(jdk-dir, Android Studio 번들, JAVA_HOME, PATH)
- Flutter gradle_utils.dart — Java별 최소 Gradle과 최대 Gradle, 최대 AGP 상수
- Flutter base/config.dart — 설정 파일 경로 규칙
- Gradle 호환표 — Java 21의 툴체인 지원 8.4, 실행 지원 8.5
- Flutter Android Java Gradle 마이그레이션 가이드 — 클래스 버전 61과 65 처방
- Flutter 이슈 187223 — Java 25 빌드 실패와 AGP 검증 범위
- 안드로이드 Java 버전 문서 — AGP 8.x의 JDK 17 요구
- Java Virtual Machine 명세 4장 — 클래스 파일 major version 범위 45~69