Fragment를 한 줄도 쓰지 않는 Compose 앱인데 Play Console이 androidx.fragment:fragment 1.1.0을 오래된 SDK로 지목한다면, 범인은 구글 자신의 라이브러리입니다. 광고·Firebase·인앱 리뷰가 공통으로 거치는 play-services-basement가 2019년판 fragment 1.1.0을 끌고 옵니다. 최신 basement로 올려도 그대로라서 앱에서 버전을 승격하는 것이 유일한 방법입니다. 앱 18개를 전수로 확인해 보니 12개가 이 상태였는데, 콘솔 경고는 3개에만 떠 있었습니다.
안드로이드 앱 여러 개를 운영하고 있습니다. 어느 날 한 앱의 Play Console에 SDK 문제로 이 라이브러리가 떴습니다.
androidx.fragment:fragment 1.1.0
당황스러웠습니다. 그 앱은 순수 Compose라 Fragment 클래스를 한 번도 import 한 적이 없고, build.gradle에도 fragment라는 글자가 없었습니다. 차단은 아니고 권고 수준의 경고였지만, 내가 선언하지 않은 것을 고치라니 어디서부터 봐야 할지 몰랐습니다.
누가 끌고 오는지 — dependencyInsight
Gradle에 물어보면 바로 나옵니다.
./gradlew :app:dependencyInsight \
--configuration releaseRuntimeClasspath \
--dependency androidx.fragment:fragment
경로를 따라 올라가면 끝에 이것이 있습니다(출력을 줄여 옮겼습니다).
androidx.fragment:fragment:1.1.0
\--- com.google.android.gms:play-services-basement:18.x
+--- com.google.android.gms:play-services-ads-...
+--- com.google.android.gms:play-services-measurement-... (firebase-analytics)
\--- com.google.android.play:review ...
play-services-basement는 구글 플레이 서비스 계열의 바닥 라이브러리라 애드몹, Firebase Analytics, 인앱 리뷰가 전부 이걸 거칩니다. 광고나 분석을 넣은 앱이면 거의 자동으로 생긴다고 보면 됩니다.
라이브러리를 최신으로 올리면 되지 않나 — 안 된다
제일 먼저 시도한 게 이것이었습니다. 그런데 Google Maven의 POM을 직접 열어 보면 답이 나옵니다.
| play-services-basement | 의존하는 androidx.fragment |
|---|---|
| 18.4.0 | 1.1.0 |
| 18.11.0 (확인 시점 최신) | 1.1.0 |
curl -s https://dl.google.com/android/maven2/com/google/android/gms/play-services-basement/18.11.0/play-services-basement-18.11.0.pom
최신판도 fragment 1.1.0을 그대로 선언하고 있습니다. 기다려도, SDK를 올려도 안 사라집니다. 앱 쪽에서 더 높은 버전을 요구해 Gradle의 충돌 해소가 높은 쪽을 고르게 하는 수밖에 없습니다.
해결 — 앱에서 버전을 승격한다
방법 A: constraints로 바닥값만 올린다 (권장)
# gradle/libs.versions.toml
[versions]
fragment = "1.8.9"
[libraries]
androidx-fragment = { group = "androidx.fragment", name = "fragment", version.ref = "fragment" }
// app/build.gradle.kts
dependencies {
constraints {
implementation(libs.androidx.fragment) {
because("play-services-basement 가 끌어오는 fragment 1.1.0 을 Play 가 경고한다")
}
}
...
}
constraints는 의존성을 추가하지 않습니다. 누군가 fragment를 끌고 올 때만 "최소 이 버전"을 걸어 줍니다. 안 쓰는 Fragment를 내가 선언해 들고 있는 모양이 안 되고, 나중에 basement가 알아서 최신을 끌고 오게 되면 이 줄은 아무 일도 안 합니다.
방법 B: implementation으로 직접 선언한다
// Fragment 버전 강제 업그레이드 (play-services-basement 가 1.1.0 을 끌고 온다)
implementation(libs.androidx.fragment)
더 단순하고 확실합니다. 다른 앱들은 이렇게 고쳤습니다. 다만 아래 두 가지를 조심하세요.
fragment-ktx를 선언하면 activity-ktx·collection-ktx·lifecycle 계열 ktx·savedstate-ktx 등 여섯 개가 딸려 들어왔습니다. Kotlin/Compose 앱은 그것들을 이미 갖고 있어서 차이가 없지만, Java 앱에는 전부 새 짐입니다.② 최신 버전이 Kotlin 버전을 끌어올릴 수 있습니다.
fragment 1.9.0은
kotlin-stdlib 2.1.20을 요구해서, Kotlin 2.0.21인 앱에서는 못 썼습니다. 1.8.9는 stdlib 1.8.22·activity 1.8.1을 요구해 기존 그래프보다 전부 낮아 아무것도 안 밀었습니다. 올리기 전에 dependencyInsight로 함께 올라가는 것을 확인하세요.확인 — 소스가 아니라 빌드 결과물로
의존성 트리는 "그렇게 될 것"이지 실제로 들어간 것이 아닙니다. Play가 읽는 것은 결과물이니 결과물에서 확인합니다.
# APK 안에 들어간 버전 파일 — 한 줄이면 끝난다
unzip -p app-release.apk META-INF/androidx.fragment_fragment.version
1.8.9
AAB로 올린다면 번들 안의 의존성 메타데이터(BUNDLE-METADATA/com.android.tools.build.libraries/dependencies.pb)가 Play가 SDK 색인에 쓰는 자료입니다. 바이너리라 눈으로 읽기는 어렵고, 같은 빌드로 만든 APK에서 버전 파일을 보는 편이 빠릅니다.
경고가 뜬 앱만 아픈 게 아니다
이 글을 쓰게 된 진짜 이유입니다. 경고는 최근에 업로드한 앱에만 뜹니다. 저는 처음에 경고가 뜬 앱 셋만 고쳤는데, 나중에 18개 앱을 전부 APK로 확인해 보니 12개가 여전히 1.1.0이었습니다.
더 뼈아팠던 건 따로 있었습니다. 이미 앱 네 개를 주석까지 달아 고쳐 두었는데, 그 뒤에 새로 만든 앱 다섯 개에 그 한 줄이 옮겨지지 않았습니다. 새 앱을 만들 때 기존 앱의 build.gradle을 참고하긴 했지만, "이 줄은 왜 있지?"로 읽히는 줄은 빠지기 쉽습니다. 특히 이 줄은 앱의 기능과 아무 관계가 없어 보이는 줄이라 더 그렇습니다.
각 앱의 릴리스 APK에서
META-INF/androidx.fragment_fragment.version을 뽑아 목록으로 보면 됩니다. 콘솔 알림을 기다리면 업로드한 순서대로 하나씩 알게 됩니다.정리
- Fragment를 안 써도 광고·Firebase·인앱 리뷰가 있으면
play-services-basement를 통해 fragment 1.1.0이 들어옵니다. - 최신 basement(18.11.0)도 1.1.0을 선언합니다. SDK를 올려서는 안 사라집니다.
- 앱에서
constraints(권장) 또는implementation으로 버전을 승격합니다. - Java 앱은
fragment(non-ktx)를, 버전은 Kotlin 요구 수준을 확인하고 고릅니다. - 확인은 APK의
META-INF/androidx.fragment_fragment.version으로 합니다. - 경고는 최근 업로드한 앱에만 뜹니다. 여러 앱이면 전부 확인하고, 새 앱 템플릿에도 넣으세요.
자주 묻는 것
이 경고를 무시하면 앱이 내려가나요?
제가 받은 것은 차단이 아닌 권고 수준이었고, 업로드와 출시는 그대로 됐습니다. 다만 Play의 SDK 색인 경고는 SDK 제공자가 등급을 올리면 나중에 업로드를 막는 쪽으로 바뀔 수 있습니다. 한 줄로 끝나는 일이라 미뤄 둘 이유가 없습니다.
fragment를 올리면 광고나 Firebase가 깨지지 않나요?
AndroidX는 같은 메이저 버전(1.x) 안에서 하위 호환을 지키는 것이 원칙이라, 1.1.0에서 1.8.x로 올리는 것은 그 범위 안입니다. 저는 광고·인앱 리뷰·Firebase Analytics가 든 앱들에 적용해 배포했고 빌드와 실행에 문제가 없었습니다. 그래도 불안하면 광고가 뜨는 화면과 동의(UMP) 팝업을 실기기에서 한 번씩 띄워 보세요.
constraints를 넣었는데 버전이 안 바뀌어요.
constraints는 같은 설정(configuration)에 걸려야 합니다. implementation 블록 안에 넣었는지, 다른 곳에서 strictly나 force로 낮은 버전을 박아 두지 않았는지 dependencyInsight로 확인하세요. 출력에 "By constraint"라는 사유가 붙어 있으면 제대로 걸린 것입니다.
