R8 규칙이나 버전 업데이트도 하지 않았고 아무것도 변경하지 않았는데 앱이 크래시가 발생했다.
이 글에서는 코드를 한 줄도 건드리지 않았음에도 발생한 크래시의 원인을 추적하고, Third-Party SDK의 숨겨진 의존성이 어떻게 앱을 파괴할 수 있는지 분석한다. 그리고 이 이슈를 Ktor 팀에 직접 보고한 과정도 공유한다.
Fatal Exception: java.lang.NoClassDefFoundError
Failed resolution of: Lio/ktor/client/plugins/UserAgent;
com.moloco.sdk.internal.http.a$a.a (SourceFile:1)
com.moloco.sdk.internal.http.a$a.invoke (SourceFile:1)
io.ktor.client.HttpClientKt.HttpClient (HttpClient.kt:648)
io.ktor.client.HttpClientJvmKt.HttpClient (HttpClientJvm.kt:25)
com.moloco.sdk.internal.http.a.a (SourceFile:1)
...
com.moloco.sdk.publisher.Moloco$initialize$1.invokeSuspend (SourceFile:6)
스택 트레이스를 자세히 보면 뭔가 이상하다:
com.moloco.sdk.internal.http - Moloco SDK 내부 코드io.ktor.client.HttpClientKt.HttpClient - Ktor HttpClient 초기화io.ktor.client.plugins.UserAgent - 찾을 수 없는 클래스내 앱 코드에서 UserAgent 플러그인을 사용하지만 코드를 변경한 적이 없다. Moloco SDK 내부에서 발생한 문제였다.
[내 앱의 Ktor 3.x]
+
[Moloco SDK의 내부 Ktor 사용]
↓
Gradle 의존성 해결
↓
하나의 Ktor 버전만 사용
↓
R8 코드 축소 (release 빌드)
↓
"UserAgent는 앱에서 직접 안 쓰니까 제거해도 되겠네?"
↓
Moloco SDK 초기화 시 ClassNotFoundException 발생
Ktor UserAgent 문서에 따르면, UserAgent 플러그인은 ktor-client-core 아티팩트에 포함되어 있다.
// UserAgent 플러그인 사용 예시
val client = HttpClient(CIO) {
install(UserAgent) {
agent = "Ktor client"
}
}
문제는 Moloco SDK가 내부적으로 이 플러그인을 사용하는데, R8 입장에서는 "앱 코드에서 직접 참조 안 하니까 필요 없겠네"라고 판단한 것이다.
SDK는 난독화된 AAR로 배포되니까 R8이 내부 동작을 제대로 분석하지 못한 것 같다.
마이그레이션 문서를 보면 Ktor 3.x에서 꽤 많은 변경이 있었다:
이런 변경들이 R8 최적화와 충돌할 여지를 만든 것 같다.
ProGuard 규칙도 이미 있었다:
# 이미 적용되어 있던 규칙
-keep class io.ktor.client.** { *; }
-keep class io.ktor.client.plugins.** { *; }
미리 대비할 수 있으면 좋겠지만 Third-Party SDK는 블랙박스인 것 같다.
SDK 제공사가 결정하는 것들:
개발자가 통제할 수 없는 것들:

이 문제를 발견한 후, 직접 Ktor 공식 레포지토리에 이슈를 생성했다:
https://github.com/ktorio/ktor/issues/5240
Moloco SDK의 문제이긴 하지만, 근본 원인은 Ktor와 R8의 상호작용에 있다고 봤다:
# gradle/libs.versions.toml
[versions]
# ktor = "3.x.x" # 문제 발생
ktor = "2.3.12" # 안정 버전으로 롤백
# SDK가 어떤 라이브러리를 사용하는지 확인
./gradlew :app:dependencies --configuration releaseRuntimeClasspath | grep -E "(ktor|moloco)"
프로젝트에 어떤 의존성이 들어오는지 정기적으로 확인해야 하는데 생각보다 개발하면 고려해야할 부분이 참 많은 것 같다.
SDK 버전을 업데이트할 때는:
통제할 수 없는 문제가 발생하면 생기는 무력감은 정말 맛이 없다... Third-Party SDK를 사용할 때는 SDK의 내부 의존성을 파악하고, Release 빌드로 철저히 테스트하고, 문제 발생 시 빠르게 롤백할 준비를 해둬야겠다.
Moloco 최신 버전 "4.3.1" 변경 후에 NoClassDefFoundError 미발생합니다 :)