devtools
Android ProGuard R8 완벽 가이드 — 코드 난독화와 빌드 최적화 설정
Android R8 컴파일러로 코드 난독화, 불필요한 코드 제거, APK 크기 축소를 설정하는 방법과 리플렉션·직렬화 라이브러리에서 발생하는 keep 규칙 작성법을 정리하였다.
- ·R8은 Android Gradle Plugin 3.4.0부터 기본 컴파일러로 ProGuard를 대체했다
- ·minifyEnabled = true 하나로 코드 축소, 난독화, 최적화가 모두 활성화된다
- ·shrinkResources = true는 사용하지 않는 리소스 파일(이미지, 레이아웃)도 제거한다
- ·-keep 규칙 없이 Gson, Retrofit 모델 클래스를 사용하면 필드 이름이 난독화되어 JSON 파싱이 실패한다
출시 빌드를 처음 활성화했을 때 앱이 실행은 되는데 네트워크 응답이 null로 오는 문제가 생겼다. 디버그 빌드에서는 정상이었기 때문에 한동안 원인을 찾지 못했고, 결국 R8이 Gson 모델 클래스의 필드 이름을 난독화해서 JSON 역직렬화가 실패한 것이었다. 이후 release 빌드를 더 자주 직접 테스트하고 keep 규칙을 데이터 클래스마다 확인하는 습관이 생겼다.
R8 기본 설정과 minifyEnabled 활성화
Android R8 minifyEnabled와 shrinkResources로 APK 크기 줄이기
release 빌드 타입에서 minifyEnabled = true와 shrinkResources = true를 설정하는 것이 R8 활성화의 시작이다. minifyEnabled는 사용하지 않는 코드 제거(tree shaking), 클래스·메서드·필드 이름 난독화, 바이트코드 최적화를 한 번에 활성화한다. shrinkResources는 코드에서 참조하지 않는 리소스 파일을 제거하며 반드시 minifyEnabled와 함께 사용해야 효과가 있다. proguardFiles에 getDefaultProguardFile('proguard-android-optimize.txt')를 포함하면 기본 최적화 규칙이 적용된다. proguard-rules.pro에는 프로젝트 고유의 keep 규칙을 추가한다. 처음 minifyEnabled를 켜면 대부분의 프로젝트에서 크래시나 기능 이상이 발생하므로 별도 릴리스 프로파일로 자주 테스트하고 문제가 생길 때마다 keep 규칙을 추가하는 방식으로 점진적으로 설정하는 것이 현실적이다.
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}Android R8 keep 규칙 작성 — Gson, Retrofit 모델 클래스 난독화 방지
R8이 클래스나 멤버를 제거하거나 이름을 바꾸지 않도록 지시하는 것이 -keep 규칙이다. Gson을 사용하는 경우 모델 클래스의 필드 이름이 JSON 키와 일치해야 파싱이 성공한다. -keepclassmembers class * { @com.google.gson.annotations.SerializedName <fields>; }처럼 어노테이션 기반 keep 규칙을 쓰거나 해당 패키지 전체를 keep하는 방법이 있다. Retrofit 인터페이스 메서드, Parcelable 구현체, enum 클래스 이름 등 리플렉션이나 직렬화가 개입하는 모든 위치를 점검해야 한다. 라이브러리 AAR에는 자체 consumer-rules.pro가 포함되어 있어 별도 규칙이 필요 없는 경우도 있지만, 직접 작성한 모델 클래스는 예외다. -printmapping mapping.txt로 난독화 맵을 출력해두면 크래시 스택 트레이스를 retrace 도구로 복원할 수 있어 운영 중 디버깅에 필수다.
R8 빌드 분석과 트러블슈팅
Android R8 적용 후 크래시 디버깅 — mapping 파일과 retrace 활용
minifyEnabled 적용 후 출시 빌드에서 크래시가 발생하면 스택 트레이스가 난독화된 클래스명으로 출력된다. Google Play Console의 Android Vitals에는 retrace가 자동 적용되지만, 직접 디버깅할 때는 빌드 시 생성된 build/outputs/mapping/release/mapping.txt와 함께 retrace 도구를 실행해 원래 클래스명을 복원한다. 테스트 단계에서는 Firebase Crashlytics에 mapping 파일을 업로드해두면 크래시 리포트가 자동으로 역난독화되어 편리하다. 특정 기능만 크래시가 나면 우선 해당 클래스에 -keep 규칙을 추가하고 좁혀가는 방식이 원인 추적에 효율적이다. R8 적용 직후 QA 단계에서 릴리스 빌드 전용 테스트 사이클을 한 번 더 돌리는 것이 운영 크래시를 예방하는 가장 확실한 방법이다.
APK 크기 분석과 최적화 효과 측정
Android R8 적용 후 APK Analyzer로 코드 축소 효과 측정하기
Android Studio의 Build > Analyze APK 기능으로 R8 전후 APK 크기와 내부 구성을 비교할 수 있다. classes.dex 항목에서 전체 메서드 수와 클래스 수를 확인하고, resources.arsc와 res/ 폴더에서 리소스 크기 감소를 수치로 확인한다. 일반적으로 minifyEnabled + shrinkResources 적용 후 APK 크기가 30~50% 감소하는 경우가 많다. 라이브러리 의존성이 많을수록 효과가 크며 사용하지 않는 라이브러리 코드가 대규모로 제거된다. 멀티 ABI 빌드 대신 Android App Bundle(AAB)로 배포하면 기기별로 필요한 ABI와 화면 밀도의 리소스만 설치되어 설치 용량이 추가로 줄어드는 효과와 합산하면 실질적인 사용자 경험이 크게 개선된다.
자주 묻는 질문
Kotlin data class는 keep 규칙 없이 Gson으로 직렬화할 수 있나요?+
R8이 활성화된 릴리스 빌드에서는 필드 이름이 난독화돼 파싱이 실패합니다. @SerializedName을 각 필드에 붙이거나 해당 클래스를 keep 규칙으로 보호해야 합니다. Kotlin Serialization의 @Serializable은 컴파일 타임에 처리되어 R8에서도 안전합니다.
ProGuard와 R8의 성능 차이는 얼마나 되나요?+
R8은 D8 덱스 컴파일러와 통합되어 빌드 시간이 ProGuard보다 빠릅니다. 코드 최적화 수준도 더 높아 결과 APK 크기가 ProGuard보다 소폭 더 작은 경향이 있습니다.
debuggable 빌드에서도 minify를 테스트하려면 어떻게 하나요?+
별도 staging 빌드 타입을 만들어 initWith(buildTypes.release)로 릴리스 설정을 복사하고 debuggable true를 추가하면 됩니다. 이 빌드로 R8 크래시를 개발 중에 잡을 수 있습니다.