
Play Console 'DEX 최적화 39%'의 범인: 쓰지도 않는 Kakao SDK keep 규칙
R8 keep 규칙 하나가 앱과 라이브러리의 public 멤버를 전부 붙잡고 있었고, 그 파일을 지우자 r8.json 기준 39%가 85%로 올랐습니다.
Play Console은 2026-09-25에도 한글 띄어쓰기 앱의 최신 번들을 "중급형(최적화 36%, 난독화 39%, 축소 39%)"으로 표시했습니다. 닷새 전에 R8 최적화를 막던 -dontoptimize를 지운 뒤였습니다. 남은 원인은 앱이 쓰지도 않는 Kakao SDK용 proguard 파일이었습니다. 그날 그 파일을 지우자 번들 안 r8.json의 축소 안 된 비율이 61.47%에서 15.18%로 내려갔고(100에서 뺀 환산값 39%→85%), AAB는 18.4MB에서 11.6MB가 됐습니다.
숫자는 SugarMount 앱 저장소(sugarspeller)의 커밋 a597223(2026-09-20)과 e05f8e3(2026-09-25), CHANGELOG 1.6.020·1.6.023 항목, 배포 감시 문서, 그리고 이 글을 쓰며 로컬 릴리스 AAB에서 다시 읽은 r8.json입니다. 기준일은 2026년 10월 20일입니다.
목차 (7)
Play Console의 퍼센트는 r8.json 비율을 뒤집은 값입니다
이 퍼센트는 번들에 든 r8.json의 통계와 숫자가 맞아떨어집니다. Android Gradle Plugin(AGP)은 R8을 돌린 릴리스 AAB 안에 BUNDLE-METADATA/com.android.tools/r8.json을 넣습니다. 그 파일의 stats에는 축소·난독화·최적화를 받지 않은 코드 비율이 들어 있습니다.
unzip -p app-release.aab BUNDLE-METADATA/com.android.tools/r8.json
"stats":{"noObfuscationPercentage":61.48,
"noOptimizationPercentage":64.27,
"noShrinkingPercentage":61.47}
100에서 빼면 Play 화면이 나옵니다. 61.47은 축소 39%, 64.27은 최적화 36%였습니다. 이 대응은 저장소의 전후 표에서 맞춰 본 것이고, Google 문서에서 계산식을 확인하지는 않았습니다. 그래도 이 대응으로 Play에 올리기 전에 로컬 빌드만으로 지표를 미리 볼 수 있습니다.
빌드 환경은 AGP 9.4.0, R8 전체 모드(full mode), 리소스 축소 사용, 기본 proguard-android-optimize.txt였습니다. 저장소 기록에는 Play가 이 지표를 2027년 2월까지 해소하라고 요구한다고 적혀 있습니다. Play 화면의 원문은 이 글에서 다시 확인하지 못했습니다.
-dontoptimize를 지우자 최적화 0%가 36%가 됐습니다
2026-09-20 커밋 a597223은 proguard-common.pro에서 -dontoptimize 한 줄을 지웠습니다. 이 줄은 Gson 리플렉션 파싱이 깨질까 봐 일부러 넣어 둔 것이었고, Play Console 최적화 지표를 0%로 묶고 있었습니다. 같은 환경에서 수정 전 HEAD(1.6.019)를 기준선으로 빌드해 비교했습니다.
최적화 안 된 비율은 100%에서 64.25%로 내려갔고, 압축 전 DEX는 16.31MB에서 14.84MB가 됐습니다. 같은 릴리스에서 인사카드 편집기와 이미지 에셋(약 11MB)을 함께 지웠기 때문에 AAB 29.95MB → 18.33MB는 최적화 덕분이 아닙니다. 최적화가 줄인 것은 DEX 1.5MB 안쪽입니다.
난독화 안 된 비율이 54.16%에서 61.46%로 올랐습니다. 그 커밋은 이것을 "앱 코드가 줄어 SDK keep 규칙 쪽 비중이 커진 것"이라고 설명하고 넘어갔습니다. 1.6.020은 2026-09-22에 배포됐습니다. 사흘 뒤 Play Console에 뜬 숫자가 이 글 첫머리의 36/39/39%입니다.
병합된 configuration.txt에 class * 규칙이 있었습니다
R8이 실제로 읽은 규칙은 app/build/outputs/mapping/release/configuration.txt에 한 파일로 모여 있습니다. 기본 규칙, 앱의 .pro 파일 6개, 라이브러리 AAR이 가져온 consumer 규칙까지 전부입니다. 2026-10-09 릴리스 빌드의 병합 결과는 1,369줄로, 처음부터 끝까지 눈으로 읽을 만한 분량입니다. 2026-09-25에 이 파일을 읽다가 아래 블록을 찾았습니다.
# proguard-kakao.pro (2020-12-16 첫 커밋부터 있던 파일, 7줄)
-keep class com.kakao.** { *; }
-keepattributes Signature
-keepclassmembers class * {
public static <fields>;
public *;
}
-dontwarn android.support.v4.**,org.slf4j.**,com.google.android.gms.**
셋째 규칙의 class *에는 조건이 없어서, 앱과 모든 라이브러리의 모든 클래스가 걸립니다. 그 클래스들의 public 필드와 메서드는 지워지지도, 이름이 바뀌지도, 인라이닝되지도 않습니다. 그런데 Kakao SDK는 의존성 목록에도 코드에도 없었습니다. 루트 빌드 스크립트의 Kakao 저장소 줄마저 주석 처리돼 있었습니다.
커밋 e05f8e3은 이 파일과 빌드 참조 두 곳(release, debug)을 지웠습니다. 한 번도 빌드에 연결된 적 없는 proguard-retrofit2.pro(29줄)도 같이 지웠습니다.
Android 앱이라면 class *에 거는 무조건 keep 규칙은 넣지 마십시오. 라이브러리에 필요한 규칙은 그 AAR의 consumer 규칙으로 이미 들어옵니다.
두 번의 수정 전후 r8.json과 크기
두 쌍의 측정은 서로 다른 날 다른 커밋을 빌드한 값입니다. 쌍 안에서만 비교하십시오. 09-20의 64.25%와 09-25의 64.27%처럼 같은 설정에서도 소수점 아래가 조금 다릅니다.
| 항목 | 1.6.019 기준선 | -dontoptimize 제거 (09-20) | kakao 파일 제거 전 (09-25) | 제거 후 (09-25) |
|---|---|---|---|---|
| 최적화 안 된 비율 | 100% | 64.25% | 64.27% | 15.68% |
| 난독화 안 된 비율 | 54.16% | 61.46% | 61.48% | 15.14% |
| 축소 안 된 비율 | 기록 없음 | 기록 없음 | 61.47% | 15.18% |
| Play 방식 환산 (최적화/난독화/축소) | 최적화 0% (Play 화면) | 36/39/- (r8.json 환산) | 36/39/39% (Play 화면) | 84/85/85 (r8.json 환산, Play 미확인) |
| DEX | 16.31MB (압축 전) | 14.84MB | 15.6MB (합계) | 8.2MB |
| AAB | 29.95MB | 18.33MB (에셋 제거분 포함) | 18.4MB | 11.6MB |

DEX 감소량으로 보면 규칙 하나를 지운 쪽이 -dontoptimize 제거의 다섯 배였습니다. 09-20에는 DEX가 1.5MB 줄었고, 이번에는 7.4MB(47%)가 줄었습니다. AAB도 6.8MB 작아졌습니다.

배포 전에는 디버그 키로 서명한 릴리스 APK를 API 34 에뮬레이터에 올렸습니다. 네이버 검사("안갔다→안 갔다, 좋치→좋지", 틀린 수 2가 서버 값과 일치), 검사 뒤 퀴즈 말풍선, 퀴즈 화면, Remote Config 콘텐츠 삽입이 모두 동작했습니다. Gson이 읽는 JSON 키 5종이 릴리스 dex 안에 문자열 그대로 남아 있는 것도 확인했습니다. (헤드리스 에뮬레이터는 screencap이 검게 나와서 uiautomator dump로 화면 글자를 읽었습니다.)
배포 뒤 감시 문서의 판정도 남아 있습니다. 2026-09-22부터 10-03까지 1.6.020, kakao 규칙을 뺀 첫 배포인 1.6.024, 1.6.025 모두 크래시 0건이었고, 판정은 "회귀 없음"이었습니다.
AI가 놓친 단서는 오른 난독화 비율이었습니다
두 커밋 모두 AI 코딩 에이전트(Claude) 세션이 작성했습니다. 09-20 세션은 커밋 메시지에 "원인이 이 한 줄이다"라고 적었습니다. 난독화 안 된 비율이 7%p 넘게 오른 것도 표에 정확히 적었습니다. 다만 그 이유를 "keep 규칙 변화 없음, 비중이 커졌을 뿐"이라는 추정으로 메웠고, 어느 규칙이 코드를 붙잡는지는 열어 보지 않았습니다.
09-25 오후 운영자가 Play Console 화면 캡처를 붙이며, 최적화가 안 된 것처럼 나온다면서 심층 분석을 요청했습니다. 그제야 에이전트가 병합 설정을 열었고, 2020년 첫 커밋부터 있던 7줄짜리 파일이 나왔습니다. 추정 대신 configuration.txt를 먼저 열었다면 닷새 일찍 끝났을 겁니다.
이 일 뒤로 저장소의 에이전트 지침에 두 줄이 생겼습니다. "class *에 거는 keep 규칙은 넣지 않는다"와 "병합 결과는 configuration.txt로 본다"입니다. 릴리스 빌드에서만 나는 R8 문제를 출시 전에 거르는 절차는 AI가 만든 Android 앱, 출시 전에 무엇을 검사하나에 따로 정리했습니다.
남은 일
- Play Console 화면 재확인. 저장소에는 r8.json에서 환산한 85%만 있고, 배포 뒤 Play 화면이 실제로 바뀌었다는 기록은 없습니다. 다음 배포 때 캡처를 남길 겁니다.
- 비율이 다시 오르는 이유. 1.6.025 빌드에서 새 Firebase 의존성이 들어오며 약 80%로 5%p 내려갔습니다. 2026-10-09에 로컬에 남은 릴리스 AAB의 r8.json은 최적화 안 된 비율 20.23%, DEX 9.30MB(9,302,488바이트)입니다. 어느 consumer 규칙이 늘었는지 configuration.txt로 다시 볼 차례입니다.
자주 묻는 질문
Play Console의 DEX 코드 최적화 경고가 있으면 업로드가 막히나요?
2026년 9월 기준으로는 막히지 않았습니다. 최적화 0%와 36%인 번들 모두 정상 배포됐습니다. 저장소 기록에는 Play가 2027년 2월까지 해소를 요구한다고 적혀 있으니 미루지 않는 편이 낫습니다.
Play에 올리기 전에 최적화 비율을 볼 수 있나요?
볼 수 있습니다. 릴리스 AAB에서 BUNDLE-METADATA/com.android.tools/r8.json을 꺼내 stats의 세 값을 100에서 빼면 됩니다. 이 앱에서는 61.47이 Play 화면의 축소 39%와 맞았습니다. 이 대응은 저장소 기록으로 맞춰 본 추정이고, 15.18(85%)이 배포 뒤 Play 화면에 그대로 나왔는지는 아직 기록이 없습니다.
어떤 keep 규칙이 지표를 붙잡는지 어떻게 찾나요?
병합된 configuration.txt에서 class *나 **처럼 범위가 넓은데 조건이 없는 -keep, -keepclassmembers를 찾습니다. 이 앱에서는 쓰지 않는 Kakao SDK용 파일의 -keepclassmembers class * { public *; } 한 블록이었습니다.
이 글은 SugarMount가 직접 측정한 값으로 쓰고, 초안 작성에 AI 코딩 에이전트를 썼습니다.
다른 AX 노트
- Android assets를 미리 gzip한 실측: APK 이득은 0, .pack.gz는 .pack이 되어 FileNotFoundException
- SKT 판매 삼성 폰에서 market://을 열면 스토어 선택 창이 뜬다: Play 패키지부터 지정하기
- addOnCompleteListener(this)가 onStop에서 지운 리스너 때문에 스플래시에 멈춘 버그
- "6.7" < "6.11"이 거짓이라 콘텐츠 갱신이 멈췄다: 6.2~6.9 기기만 막힌 Remote Config 버전 비교
- 화살표 퍼즐 1,100판을 만든 역구성 생성기와 G2 게이트
- 대개편 직후 DAU는 올랐고, 자연 유입 코호트 D1은 15.2%에서 11.2%로 내려갔다