문제 상황
Android 프로젝트를 빌드할 때 다음과 같은 에러가 발생했습니다:
Caused by: java.lang.RuntimeException: Duplicate class org.jetbrains.annotations.Nls
found in modules
- annotations-23.0.0.jar -> annotations-23.0.0 (org.jetbrains:annotations:23.0.0)
- kotlin-gradle-plugin-2.1.0-gradle85.jar -> kotlin-gradle-plugin-2.1.0-gradle85
(org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.0)
Duplicate class org.jetbrains.annotations.Nls$Capitalization found in modules
- annotations-23.0.0.jar -> annotations-23.0.0 (org.jetbrains:annotations:23.0.0)
- kotlin-gradle-plugin-2.1.0-gradle85.jar -> kotlin-gradle-plugin-2.1.0-gradle85
(org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.0)
에러 분석
이 에러는 org.jetbrains.annotations 패키지의 클래스들이 두 개의 서로 다른 라이브러리에
중복으로 포함되어 발생하는 의존성 충돌(Dependency Conflict) 문제입니다.
- org.jetbrains:annotations:23.0.0
- org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.0 (내부에 동일한 클래스 포함)
문제 원인 찾기
1. 의존성 트리 확인
먼저 어디서 충돌이 발생하는지 확인하기 위해 의존성 트리를 검색했습니다:
./gradlew :app:dependencies --configuration debugRuntimeClasspath | grep
"kotlin-gradle-plugin"
2. 전체 프로젝트 검색
모든 build.gradle.kts 파일에서 kotlin-gradle-plugin을 참조하는 곳을 찾았습니다:
# 또는 IDE에서 전체 프로젝트 검색
검색 결과:
- build-logic/convention/build.gradle.kts - 정상 (빌드 로직에서 사용)
- gradle/libs.versions.toml - 정상 (버전 카탈로그 정의)
- domain/build.gradle.kts - 문제 발견!
근본 원인
domain 모듈에서 사용하는 build.gradle.kts 파일에서 다음과 같이 잘못된 의존성이 추가되어 있었습니다:
dependencies {
implementation(projects.core.model)
implementation(projects.core.util)
implementation(libs.kotlin.gradlePlugin) // 문제의 원인! (앱 모듈에 이미 정의되어있음)
implementation(libs.timber)
implementation(libs.javax.inject)
}
왜 문제가 되는가?
kotlin-gradle-plugin은 빌드 스크립트 전용 라이브러리입니다.
- 올바른 사용처: buildSrc 또는 build-logic 같은 빌드 스크립트 모듈에서 compileOnly로
사용
- 잘못된 사용: 앱이나 라이브러리 모듈에서 implementation으로 사용
kotlin-gradle-plugin은 내부적으로 org.jetbrains.annotations 클래스들을 포함하고 있어,
런타임 의존성으로 추가하면 다른 라이브러리와 충돌이 발생합니다.
해결 방법
domain/build.gradle.kts에서 해당 의존성을 제거했습니다:
dependencies {
implementation(projects.core.model)
implementation(projects.core.util)
// implementation(libs.kotlin.gradlePlugin) // 제거!
implementation(libs.timber)
implementation(libs.javax.inject)
}
시도했던 다른 방법들 (실패)
문제 해결 과정에서 다음과 같은 방법들을 시도했으나 효과가 없었습니다:
1. Packaging 옵션에서 pickFirst 사용 (클로드가 알려줌)
// 효과 없음 (심지어 deprecated 된 기능)
android {
packaging {
resources {
pickFirst("org/jetbrains/annotations/Nls.class")
pickFirst("org/jetbrains/annotations/Nls\$Capitalization.class")
}
}
}
→ 이 방법은 패키징 단계의 충돌을 해결하지만, 의존성 해결 단계에서 이미 에러가 발생하므로
소용없습니다.
2. gradle.properties 설정 변경
# 효과 없음 (deprecated)
android.enableDexingArtifactTransform.desugaring=false
3. ResolutionStrategy로 버전 강제
// 효과 없음
configurations.all {
resolutionStrategy {
force("org.jetbrains:annotations:23.0.0")
}
}
→ 버전 충돌이 아니라 같은 클래스가 두 개의 다른 artifact에 존재하는 문제이므로 해결되지
않습니다.
4. Exclude로 의존성 제외
// 또 다른 문제 발생
configurations.all {
exclude(group = "org.jetbrains", module = "annotations")
}
→ annotations를 완전히 제외하면 Kotlin 컴파일러가 필요한 클래스를 찾지 못해 다른 에러가
발생합니다:
Caused by: java.lang.ClassNotFoundException: org.jetbrains.annotations.NotNull
올바른 kotlin-gradle-plugin 사용법
kotlin-gradle-plugin은 다음 위치에서만 사용해야 합니다:
build-logic/convention/build.gradle.kts
plugins {
`kotlin-dsl`
}
dependencies {
compileOnly(libs.android.gradlePlugin)
compileOnly(libs.kotlin.gradlePlugin) // <--- compileOnly로 사용
}
gradle/libs.versions.toml
[libraries]
kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin",
version.ref = "kotlin" }
이렇게 정의하고, 빌드 로직에서만 compileOnly로 사용하는 것이 올바른 방법입니다.
결론
- 증상: Duplicate class org.jetbrains.annotations 에러
- 원인: 앱/라이브러리 모듈에서 kotlin-gradle-plugin을 런타임 의존성으로 추가
- 해결: 해당 의존성을 제거
- 교훈: Gradle 플러그인 관련 라이브러리는 빌드 스크립트 모듈에서만 사용해야 함
디버깅 팁
비슷한 문제가 발생했을 때 다음 순서로 확인해보세요:
1. 에러 메시지 확인: 어떤 클래스가 어떤 두 라이브러리에서 중복되는지 확인
2. 의존성 트리 검색: ./gradlew :module:dependencies --configuration runtimeClasspath
3. 전체 프로젝트 검색: 의심되는 라이브러리를 모든 build.gradle.kts에서 검색
4. 잘못된 위치에 추가된 의존성 제거
특히 -plugin, -compiler, -gradle 같은 이름이 들어간 라이브러리는 일반 앱 모듈에 추가하면
안 되는 경우가 많으니 주의하세요!
'Computer > Android&iOS' 카테고리의 다른 글
| Android WebViewAssetLoader: 안전하고 효율적인 로컬 리소스 로딩 (0) | 2025.10.22 |
|---|---|
| KMP (CMP) 프로젝트 init (0) | 2025.10.17 |
| Android studio 단축키 모음집 (0) | 2025.10.10 |
| 구글은 왜 isTablet() API를 만들지 않았을까 (0) | 2025.09.29 |
| remember와 rememberSaveable (0) | 2025.09.26 |