Flutter 패키지 올렸더니 안드로이드 빌드가 깨졌다 — JVM 타깃 충돌 3연쇄 해결기
홈 화면 위젯 패키지를 최신 버전으로 올린 뒤 'Inconsistent JVM-target compatibility' 오류로 안드로이드 빌드가 멈췄습니다. app 모듈만 고쳐서는 해결되지 않았고, 플러그인 모듈까지 세 단계로 정리해야 했던 과정을 코드와 함께 정리했습니다.
안녕하세요. Jay입니다!
운영 중인 앱에 홈 화면 위젯 기능을 붙이려고 home_widget 패키지를 최신 버전으로 올렸는데요, iOS는 멀쩡히 빌드되는데 안드로이드가 이런 메시지를 뱉으며 멈췄습니다.
Inconsistent JVM-target compatibility detected for tasks
'compileDebugJavaWithJavac' (1.8) and 'compileDebugKotlin' (11).
Flutter로 앱을 만들다 패키지를 올릴 때 꽤 자주 만나는 오류입니다. 검색하면 "app 모듈의 자바 버전을 올려라"는 답이 제일 먼저 나오는데, 저는 그것만으로는 해결되지 않았고 세 단계를 거쳐야 통과했습니다. 같은 곳에서 막히신 분들을 위해 순서대로 정리합니다.
🧩 왜 나는 오류인가
안드로이드 빌드에는 자바 컴파일과 코틀린 컴파일이 함께 돌아가는데, 이 둘이 같은 JVM 바이트코드 버전을 목표로 해야 합니다. 하나는 1.8을 노리고 다른 하나는 11을 노리면 Gradle이 위 오류로 멈춥니다.
최근 패키지들이 JVM 11 이상을 요구하기 시작하면서, 프로젝트는 예전 그대로(1.8)인데 새로 추가한 패키지만 11로 컴파일되는 불일치가 생깁니다. 즉 내가 뭘 잘못한 게 아니라, 올린 패키지가 기준선을 끌어올린 것입니다.
1️⃣ 첫 번째 — app 모듈 타깃 올리기
가장 널리 알려진 조치입니다. android/app/build.gradle에서 자바와 코틀린을 함께 11로 맞춥니다. 둘 중 하나만 올리면 오류가 그대로 납니다.
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
}
여기까지 하고 다시 빌드했더니 오류 메시지에 찍히는 모듈 이름만 바뀌어서 또 실패했습니다. 이번엔 제 앱 코드가 아니라 패키지(플러그인) 모듈이 문제였습니다.
2️⃣ 두 번째 — 플러그인 모듈까지 한 번에 통일하기
Flutter 프로젝트에서 각 패키지는 별도의 안드로이드 라이브러리 모듈로 빌드됩니다. 그 모듈들은 각자의 설정을 갖고 있어서, app 모듈만 올려서는 안 맞습니다. 그래서 android/build.gradle(루트)에서 모든 서브 프로젝트의 타깃을 일괄로 맞춰줬습니다.
subprojects {
plugins.withId("com.android.library") {
def androidExt = extensions.getByName("android")
androidExt.compileOptions.sourceCompatibility = JavaVersion.VERSION_11
androidExt.compileOptions.targetCompatibility = JavaVersion.VERSION_11
}
tasks.withType(JavaCompile).configureEach {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions.jvmTarget = "11"
}
}
여기서 중요한 함정이 하나 있습니다. 인터넷 예제 중에는 이 설정을 afterEvaluate { ... } 블록으로 감싸는 코드가 많은데, Flutter 프로젝트 기본 템플릿에 들어 있는 아래 설정과 충돌합니다.
subprojects {
project.evaluationDependsOn(":app")
}
둘을 같이 쓰면 평가 순서가 꼬이면서 빌드가 실패합니다. 그래서 afterEvaluate 대신 plugins.withId와 configureEach 같은 지연(lazy) 훅을 써야 합니다. 위 코드가 그 방식입니다. 이걸 모르고 afterEvaluate로 계속 시도하다 시간을 꽤 썼습니다.
3️⃣ 세 번째 — 그래도 남는 모듈은 검증 완화
여기까지 하면 대부분 통과하는데, 저는 마지막에 하나가 더 걸렸습니다. 패키지에 따라 자기만 더 높은 타깃(17)을 요구하는 경우가 있어서 전부 11로 통일하는 게 애초에 불가능했거든요. 이럴 땐 android/gradle.properties에 검증 수준을 낮추는 옵션을 넣습니다.
kotlin.jvm.target.validation.mode=warning
기본값은 error라 불일치가 하나만 있어도 빌드를 멈추는데, warning으로 두면 경고만 남기고 진행합니다. 이건 근본 해결이 아니라 완충 장치라는 점은 분명히 알고 쓰는 게 좋습니다. 서로 다른 타깃이 섞이는 상황 자체를 허용하는 것이니까요. 다만 라이브러리들이 각자 다른 버전을 요구하는 과도기에는 현실적인 선택지이고, 실제로 이 상태로 릴리스 빌드(AAB)까지 정상 생성됐습니다.
✅ 정리 — 순서대로 확인하세요
| 순서 | 파일 | 조치 |
|---|---|---|
| 1 | android/app/build.gradle |
compileOptions + kotlinOptions 함께 11로 |
| 2 | android/build.gradle |
subprojects에 lazy 훅으로 일괄 통일 (afterEvaluate 금지) |
| 3 | android/gradle.properties |
타깃이 섞이면 validation.mode=warning |
수정 후에는 flutter clean 한 번 돌리고 다시 빌드하세요. Gradle이 이전 설정을 캐시하고 있어서, 고쳤는데도 같은 오류가 나오는 것처럼 보이는 경우가 있습니다.
💡 Jay의 한 줄 — "패키지 하나 올렸을 뿐인데 왜 이렇게 번지나"
이번 건은 기능 개발이 아니라 버전 승급의 여파로 하루를 썼습니다. 코드는 한 줄도 안 바뀌었는데 빌드 환경 세 곳을 고쳐야 했죠. 이런 종류의 작업은 끝나도 아무 기능이 늘지 않아서 허무한데, 그래도 기록해두면 다음 릴리스에서는 같은 자리에서 안 멈춥니다. 실제로 이 정리 이후 다음 릴리스 빌드는 그대로 통과했습니다.
패키지를 올릴 때는 릴리스 노트에서 최소 요구 JVM/Gradle/AGP 버전이 올라갔는지를 먼저 확인하는 습관을 들이면, 최소한 "왜 갑자기?"라는 당황은 줄일 수 있습니다.
결론
Inconsistent JVM-target compatibility 오류는 app 모듈만 고쳐서는 끝나지 않는 경우가 많습니다. ① app 모듈에서 자바와 코틀린을 같은 버전으로 올리고 ② 루트 Gradle에서 플러그인 모듈까지 lazy 훅으로 통일하고 ③ 그래도 섞이면 검증을 warning으로 완화 — 이 순서로 접근하시면 대부분 해결됩니다. afterEvaluate를 쓰지 않는 것만 꼭 기억하세요.
다음에도 유익한 포스팅으로 찾아오겠습니다. 감사합니다!