문제 상황
오늘은 안드로이드 프로젝트에서 kapt를 사용할 때 발생할 수 있는 문제점에 대해 다루려고 합니다.
바로 오류 메세지부터 보시죠.
java.lang.IllegalAccessError: superclass access check failed: class org.jetbrains.kotlin.kapt3.base.javac.KaptJavaCompiler (in unnamed module @0x48cd3d62) cannot access class com.sun.tools.javac.main.JavaCompiler (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.main to unnamed module @0x48cd3d62
안드로이드 프로젝트를 빌드하던 중에 위와 같은 에러 메세지를 확인하였는데요.
이해를 조금 더 돕기 위해 제 프로젝트의 build.gradle 파일들을 보여드리겠습니다.
먼저 app 모듈 수준의 build.gradle 파일입니다.
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
id 'androidx.navigation.safeargs.kotlin'
}
android {
namespace 'com.example.movieapp'
compileSdk 34
defaultConfig {
applicationId "com.example.movieapp"
minSdk 28
targetSdk 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
kapt {
correctErrorTypes = true
}
buildFeatures {
buildConfig true
compose true
}
composeOptions {
kotlinCompilerExtensionVersion '1.5.7'
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation(platform("org.jetbrains.kotlin:kotlin-bom:$kotlin_version"))
implementation "androidx.core:core-ktx:$core_version"
// Compose
implementation "androidx.compose.ui:ui:$compose_ui_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version"
implementation "androidx.compose.material:material:$compose_material_version"
implementation "androidx.activity:activity-compose:$activity_compose_version"
// LifeCycle
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// Coil
implementation "io.coil-kt:coil-compose:$coil_version"
// Navigation
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"
// Hilt
implementation "com.google.dagger:hilt-android:$hilt_version"
implementation "androidx.hilt:hilt-navigation-fragment:1.1.0"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
// Fragment
implementation "androidx.fragment:fragment-ktx:$fragment_version"
// Timber
implementation "com.jakewharton.timber:timber:$timber_version"
// Retrofit
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2'
implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit_version"
// Gson
implementation "com.google.code.gson:gson:$gson_version"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"
}
다음으로는 프로젝트 수준의 build.gradle 파일입니다.
// Top-level build files where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
activity_compose_version = '1.7.2'
compose_ui_version = '1.4.3'
kotlin_version = '1.9.0'
nav_version = '2.7.3'
coil_version = '2.3.0'
hilt_version = '2.48'
timber_version = '5.0.1'
lifecycle_version = '2.6.1'
retrofit_version = '2.9.0'
gson_version = '2.10.1'
compose_material_version = '1.4.3'
core_version = '1.10.1'
fragment_version = '1.6.1'
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.7.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
}
}
plugins {
id 'com.android.application' version '8.7.2' apply false
id 'com.android.library' version '8.7.2' apply false
id 'org.jetbrains.kotlin.android' version "1.9.0" apply false
}
가설
문제가 발생하면 저는 보통 chatGpt와 대화를 하는데요.
어제와 오늘 몇 시간 동안 이 문제를 해결하기 위해서 많은 가설들을 세우고 실행해보았습니다.
참고로 Gradle 캐시 문제는 기본값입니다.
InvalideCaches는 매번 문제를 맞닥드릴 때마다 가장 먼저 실행해보는 도구입니다.
이외에도 여러 가설들을 검토했는데요.
일단 가장 먼저 알게된 사실은 해당 오류는 Java 모듈 시스템과 관련된 접근 제약 문제에서 비롯된다는 것입니다.
Kapt가 JDK의 내부 클래스에 접근하려 할 때 모듈 시스템의 제한에 의해 거부된 상황이라는 것이죠.
다음은 제가 이 문제를 해결하기 위해 사용했던 방법들입니다.
첫 번째 방법 : JVM 옵션을 수정하여 모듈 접근 허용하기
build.gradle 파일에 kapt 옵션에 다음과 같은 매개변수를 추가해보았습니다.
kapt {
javacOptions {
option("-J--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED")
}
}
kapt 안에서 javacOptions가 Gradle 버전에 따라 인식 자체가 안되는 문제가 있어 아래와 같이 시도도 해보았습니다.
gradle.properties에 직접 설정을 해주는 방식인데요.
org.gradle.jvmargs=--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
이 방법들을 모두 다 적용해보아도 문제는 여전했습니다.
두 번째 방법 : JDK 버전 다운그레이드
이 방법은 JDK 17을 그대로 사용하고 싶었기에 고려 대상에서 제외했습니다.
세 번째 방법 : Gradle 데몬 재시작
이 방법도 무수히 많이 해보았습니다.
이 과정에서 아직 gradlew의 실행 권한을 제가 주지 않았었다는 사실도 알게 되었지만~
별 효과는 없었습니다.
네 번째 방법 : Kapt 대신 다른 도구 사용
ksp로 전환하는 방법이 있어 시도해보았지만 다른 라이브러리들과 호환성이 맞지 않아 실패했습니다.
그리고 다른 레퍼런스 프로젝트에서 Kapt를 너무나 멀쩡히 잘 활용하고 있는 것을 보았기에 일단은
Kapt로 문제를 해결해야겠다고 생각했습니다.
다섯 번째 방법 : Gradle 버전 업데이트
이 역시도 문제가 해결되지 않았습니다.
Gradle 버전과 AGP 버전을 모두 최신 버전으로 설정했는데도 문제는 여전했습니다.
문제 해결
Gpt와 계속 대화를 하는데 문제가 계속 빙빙 도는것 같아서
이번에는 stackOverFlow에서 저와 같은 케이스가 있나 찾아보았습니다.
그러던 중 이 글을 발견했어요.
java.lang.IllegalAccessError: superclass access check failed: class org.jetbrains.kotlin.kapt3.base.javac.KaptJavaCompiler?
I'm getting this strange AccessError after implementing Firebase Database dependencies. I had Firebase Authentication and Firebase Analytics enabled before loading Firebase Database. I'm using it as
stackoverflow.com
글에 보면 코틀린 버전을 1.9.21로 바꾸라는 조언이 있더라고요.
적용했더니 놀랍게도 kapt 오류가 더 이상 나오지 않았습니다.
빌드가 아주 클린하게 되었습니다.
왜?
이런 종류의 문제를 해결하고 나면 늘 마음속에 남는 한 단어는...왜...?
왜 해결된 건지 지금부터 알아보겠습니다.
그 이전에 안드로이드 공식 문서를 좀 읽어보도록 하겠습니다.
https://developer.android.com/build/tool-and-library-dependencies?hl=ko
도구 및 라이브러리 상호 종속 항목 | Android Studio | Android Developers
빌드에서 라이브러리와 도구는 어떤 관계가 있나요?
developer.android.com
공식 문서에 보면 라이브러리, 플러그인, 하위 프로젝트, Android SDK, Kotlin 컴파일러, Java 컴파일러,
Android Studio와 같은 개발 환경 등이 Gradle이 프로젝트를 빌드하는 데에 영향을 줄 수 있다고 설명하네요.
빌드 내 관계는 크게 3가지로 나누어져있다고 설명을 하는데요.
첫 번째는 소스 코드로 내가 관리할 수 잇는 코드와 리소스를 의미합니다.
두 번째는 라이브러리 종속 항목으로 빌드 시 프로젝트 및 하위 프로젝트에 포함되는 외부 라이브러리 또는 모듈을 의미합니다.
세 번째는 도구로 소스를 애플리케이션 또는 라이브러리로 변환하는 컴파일러, 플러그인, SDK를 의미합니다.
이 중에서 저희는 세 번째에 좀 집중해야겠죠.
다음 그림을 한 번 볼까요.
저희가 문제가 생겼던 부분이 그림 왼쪽 아래에 위치한 KGP였죠.
KGP의 버전을 바꾸니 문제가 해결된 것을 알 수 있었습니다.
그렇다면 KGP는 어떤 역할을 하는 것일까요?
결론부터 말씀드리자면 KGP는 Kapt의 동작 방식이나 설정에 영향을 주고요.
또 Kotlin Compiler의 버전에도 영향을 줍니다.
KGP에 따라 Kapt를 사용하는 방식이 달라지고 때로는 버그도 고쳐지기 때문에
KGP 버전을 새롭게 설정함에 따라 문제가 해결된 것이죠.
결론
KGP 버전에 따라 Kapt의 버그 문제가 해결될 수 있다.
'Computer' 카테고리의 다른 글
[CS] 프로세스와 스레드 (0) | 2025.03.26 |
---|---|
[Android] JVM vs ART (0) | 2025.03.22 |
[Kotlin] 클래스를 상속받을 때 주의할 점 (0) | 2024.12.09 |
[Jetpack Compose] Compose, 최적화를 위한 원칙 (0) | 2024.11.21 |
[Java] 내부 클래스, 캡슐화 원칙을 위한 설계 기법 (0) | 2024.11.20 |