본문 바로가기
■Development■/《Android》

[Android] Proguard 완벽 가이드 : 소스코드 난독화를 위한 모든 것

by 은스타 2019. 9. 22.
반응형

안드로이드 ProGuard 완벽 가이드: 소스코드 난독화를 위한 모든 것

안녕하세요! 오늘은 안드로이드 앱 개발자라면 꼭 알아야 할 ProGuard에 대해 자세히 알아보겠습니다. 앱의 보안을 강화하고 크기를 최적화하는 데 필수적인 ProGuard의 모든 것, 특히 중요 파일 목록과 그 내용을 쉽게 이해할 수 있도록 설명해 드리겠습니다.


목차

  1. ProGuard란 무엇인가?
  2. ProGuard의 주요 기능
  3. ProGuard 설정 방법
  4. 중요 ProGuard 파일 목록과 설명
  5. proguard-rules.pro 파일 작성 가이드
  6. 라이브러리별 ProGuard 규칙
  7. ProGuard 사용 시 주의사항
  8. 문제 해결 가이드
  9. R8과 ProGuard의 차이점
  10. 자주 묻는 질문

 

#1. ProGuard란 무엇인가?

ProGuard는 안드로이드 앱의 자바 바이트코드를 최적화하고 난독화하는 도구입니다. 구글이 안드로이드 스튜디오에 기본으로 통합한 이 도구는 앱의 보안을 강화하고 용량을 줄이는 데 큰 역할을 합니다.

출처 : https://www.guardsquare.com/manual/home

ProGuard가 필요한 이유

  1. 리버스 엔지니어링 방지: 앱의 코드를 분석하기 어렵게 만들어 지적 재산을 보호합니다.
  2. 앱 크기 감소: 사용하지 않는 코드를 제거하고 클래스, 필드, 메서드의 이름을 짧게 바꿔 APK 크기를 줄입니다.
  3. 성능 최적화: 불필요한 코드를 제거하고 최적화하여 앱 성능이 향상됩니다.
  4. DEX 65K 메소드 제한 문제 해결: 안드로이드의 DEX 파일 형식 제한을 해결하는 데 도움을 줍니다.

 

#2. ProGuard의 주요 기능

ProGuard는 크게 네 가지 주요 기능을 제공합니다:

1. 축소(Shrinking)

  • 사용하지 않는 클래스, 필드, 메서드를 감지하고 제거합니다.
  • 코드베이스를 정리하여 APK 크기를 줄입니다.

2. 최적화(Optimization)

  • 바이트코드를 분석하고 최적화합니다.
  • 사용되지 않는 코드 블록 제거, 메서드 인라이닝, 상수 계산 등을 수행합니다.

3. 난독화(Obfuscation)

  • 클래스, 필드, 메서드의 이름을 의미 없는 짧은 이름으로 바꿉니다.
  • 디컴파일된 코드를 이해하기 어렵게 만들어 리버스 엔지니어링을 방지합니다.

4. 사전 검증(Preverification)

  • 자바 바이트코드를 JVM이나 안드로이드 런타임에 로드하기 전에 미리 검증합니다.
  • 런타임 오버헤드를 줄입니다.

 

#3. ProGuard 설정 방법

안드로이드 스튜디오에서 ProGuard를 설정하는 방법을 단계별로 알아보겠습니다.

build.gradle 파일 설정

앱 수준의 build.gradle 파일에서 다음과 같이 ProGuard를 활성화할 수 있습니다:

android {
    buildTypes {
        release {
            minifyEnabled true           // 코드 축소, 난독화, 최적화 활성화
            shrinkResources true         // 사용하지 않는 리소스 제거
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }

        debug {
            // 디버그 빌드에서도 활성화하려면 (선택사항)
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

주요 설정 옵션

  • minifyEnabled: 코드 축소, 최적화, 난독화를 활성화합니다.
  • shrinkResources: 사용하지 않는 리소스를 제거합니다 (minifyEnabled가 true일 때만 작동).
  • proguardFiles: ProGuard 규칙이 담긴 파일을 지정합니다.

 

#4. 중요 ProGuard 파일 목록과 설명

안드로이드 ProGuard 설정에서 알아야 할 중요한 파일들을 살펴보겠습니다.

1. proguard-android.txt

  • 위치: <ANDROID_SDK>/tools/proguard/proguard-android.txt
  • 설명: 안드로이드 SDK에서 제공하는 기본 ProGuard 설정 파일입니다.
  • 내용: 안드로이드 프레임워크 관련 기본 keep 규칙이 포함되어 있습니다.
  • 사용: getDefaultProguardFile('proguard-android.txt')로 참조합니다.

2. proguard-android-optimize.txt

  • 위치: <ANDROID_SDK>/tools/proguard/proguard-android-optimize.txt
  • 설명: 코드 최적화가 추가된 향상된 버전의 기본 설정 파일입니다.
  • 내용: proguard-android.txt의 모든 규칙 + 추가 최적화 옵션이 포함됩니다.
  • 사용: getDefaultProguardFile('proguard-android-optimize.txt')로 참조합니다.
  • 특징: 기본 파일보다 더 적극적인 최적화를 수행합니다.

3. proguard-rules.pro

  • 위치: 앱 모듈의 루트 디렉토리 (app/proguard-rules.pro)
  • 설명: 앱 특정 ProGuard 규칙을 정의하는 사용자 정의 파일입니다.
  • 내용: 라이브러리 관련 규칙, 커스텀 클래스 유지 규칙 등을 정의합니다.
  • 편집 방법: 안드로이드 스튜디오에서 직접 편집할 수 있습니다.

4. proguard-project.txt (이전 버전)

  • 위치: 프로젝트 루트 디렉토리
  • 설명: 이전 안드로이드 빌드 시스템에서 사용하던 파일입니다.
  • 현재 상태: 최신 안드로이드 스튜디오에서는 사용되지 않습니다.

5. mapping.txt

  • 위치: app/build/outputs/mapping/release/
  • 설명: 난독화 과정에서 생성된 이름 매핑 정보가 포함된 파일입니다.
  • 중요성: 난독화된 스택 트레이스를 원래 이름으로 복원하는 데 필수적입니다.
  • 관리 방법: 각 릴리스 버전마다 따로 보관해야 합니다.

6. seeds.txt

  • 위치: app/build/outputs/mapping/release/
  • 설명: 난독화되지 않고 유지된 클래스와 멤버의 목록입니다.
  • 용도: 어떤 클래스와 멤버가 entry point로서 보존되었는지 확인할 수 있습니다.

7. usage.txt

  • 위치: app/build/outputs/mapping/release/
  • 설명: 앱에서 사용된 코드와, 미사용으로 제거된 코드의 목록입니다.
  • 용도: 어떤 코드가 제거되었는지 확인하고 문제를 진단할 때 유용합니다.

8. dump.txt

  • 위치: app/build/outputs/mapping/release/
  • 설명: 앱의 내부 구조에 대한 상세 정보를 포함합니다.
  • 내용: 모든 클래스 파일의 내부 구조를 텍스트로 표현합니다.
  • 용도: 고급 디버깅과 문제 해결에 사용됩니다.

 

#5. proguard-rules.pro 파일 작성 가이드

proguard-rules.pro 파일은 앱 특성에 맞게 사용자가 직접 작성하는 파일입니다. 다음은 이 파일에 포함될 수 있는 주요 규칙들입니다.

기본 템플릿

# 기본 ProGuard 규칙
-keepattributes Signature
-keepattributes *Annotation*
-keepattributes SourceFile,LineNumberTable
-keep public class * extends java.lang.Exception

# 앱 모델 클래스 (예: JSON 파싱용 POJO)
-keep class com.example.app.model.** { *; }

# Android 컴포넌트
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.view.View

# 네이티브 메소드
-keepclasseswithmembernames class * {
    native <methods>;
}

# Enum 클래스 보존
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# Parcelable 구현체
-keepclassmembers class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator CREATOR;
}

# R 클래스의 정적 필드
-keepclassmembers class **.R$* {
    public static <fields>;
}

주요 ProGuard 지시어

  • -keep: 지정된 클래스와 멤버를 그대로 유지합니다.
  • -keepclassmembers: 지정된 클래스의 멤버만 유지합니다.
  • -keepclasseswithmembers: 지정된 멤버를 포함하는 클래스를 유지합니다.
  • -keepnames: 지정된 클래스와 멤버의 이름을 유지합니다(축소는 적용).
  • -keepclassmembernames: 지정된 클래스 멤버의 이름을 유지합니다.
  • -dontwarn: 지정된 클래스에 대한 경고를 무시합니다.
  • -assumenosideeffects: 지정된 메소드 호출을 제거합니다(로그 제거 등에 유용).

주요 와일드카드와 필터

  • *: 모든 패키지 이름, 클래스 이름 또는 메소드 이름과 일치
  • **: 모든 패키지와 하위 패키지와 일치
  • ***: 모든 타입(매개변수 타입이나 반환 타입)과 일치
  • !: 부정(예: !android.**는 android 패키지를 제외)
  • <init>: 생성자
  • <fields>: 모든 필드
  • <methods>: 모든 메소드

 

#6. 라이브러리별 ProGuard 규칙

많은 안드로이드 라이브러리는 특정 ProGuard 규칙이 필요합니다. 다음은 자주 사용되는 라이브러리의 ProGuard 규칙 예시입니다.

Retrofit 2

# Retrofit
-keepattributes Signature
-keepattributes Exceptions
-keep class retrofit2.** { *; }
-keepclasseswithmembers class * {
    @retrofit2.http.* <methods>;
}
-dontwarn retrofit2.**

Gson

# Gson
-keepattributes Signature
-keepattributes *Annotation*
-dontwarn sun.misc.**
-keep class com.google.gson.** { *; }
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

OkHttp 3

# OkHttp
-keepattributes Signature
-keepattributes *Annotation*
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
-dontwarn okio.**

Glide

# Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}
-dontwarn com.bumptech.glide.load.resource.bitmap.VideoDecoder

Firebase

# Firebase
-keep class com.google.firebase.** { *; }
-keep class com.firebase.** { *; }
-keep class org.apache.** { *; }
-keepnames class com.fasterxml.jackson.** { *; }
-keepnames class javax.servlet.** { *; }
-keepnames class org.ietf.jgss.** { *; }
-dontwarn org.apache.**
-dontwarn org.w3c.dom.**

Kotlin Serialization

# Kotlin Serialization
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.AnnotationsKt
-keep,includedescriptorclasses class com.yourcompany.yourpackage.**$$serializer { *; }
-keepclassmembers class com.yourcompany.yourpackage.** {
    *** Companion;
}
-keepclasseswithmembers class com.yourcompany.yourpackage.** {
    kotlinx.serialization.KSerializer serializer(...);
}

 

#7. ProGuard 사용 시 주의사항

ProGuard를 사용할 때 알아두어야 할 중요한 주의사항들입니다.

1. 리플렉션 사용 시 주의

자바 리플렉션을 사용하는 코드는 ProGuard로 인해 문제가 발생할 수 있습니다. 리플렉션을 통해 접근하는 클래스와 멤버는 명시적으로 유지해야 합니다.

-keep class com.example.app.ReflectedClass { *; }

2. 시리얼라이제이션/디시리얼라이제이션

JSON 파싱 등에 사용되는 모델 클래스는 보통 유지해야 합니다.

-keep class com.example.app.model.** { *; }

3. JavaScript 인터페이스

WebView에서 자바스크립트 인터페이스로 사용되는 클래스는 유지해야 합니다.

-keepclassmembers class com.example.app.JavaScriptInterface {
    <methods>;
}

4. 네이티브 메소드

JNI를 통해 호출되는 네이티브 메소드는 이름이 변경되면 안 됩니다.

-keepclasseswithmembernames class * {
    native <methods>;
}

5. 커스텀 뷰

XML 레이아웃에서 참조하는 커스텀 뷰 클래스는 유지해야 합니다.

-keep public class com.example.app.CustomView { *; }

6. 라이브러리 누락 경고

사용하지 않는 라이브러리 클래스에 대한 경고는 -dontwarn으로 무시할 수 있습니다.

-dontwarn okio.**
-dontwarn retrofit2.**

 

#8. 문제 해결 가이드

ProGuard를 적용한 후 발생할 수 있는 일반적인 문제와 해결 방법을 알아보겠습니다.

1. ClassNotFoundException / NoClassDefFoundError

원인: ProGuard가 필요한 클래스를 제거했거나 난독화했습니다.

해결 방법:

-keep class com.example.app.MissingClass { *; }

2. IllegalArgumentException: No enum constant

원인: Enum 클래스가 올바르게 유지되지 않았습니다.

해결 방법:

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

3. 크래시 로그 해독하기

난독화된 앱에서 크래시가 발생하면 스택 트레이스도 난독화됩니다. mapping.txt 파일을 사용하여 원래 클래스 및 메소드 이름으로 복원할 수 있습니다.

Firebase Crashlytics 설정:

android {
    buildTypes {
        release {
            firebaseCrashlytics {
                mappingFileUploadEnabled true
            }
        }
    }
}

Android Studio Retrace 도구 사용:

  1. mapping.txt 파일을 준비합니다.
  2. Logcat에서 난독화된 스택 트레이스를 복사합니다.
  3. Android Studio의 "Analyze > Analyze Stack Trace" 메뉴를 사용합니다.
  4. mapping.txt 파일과 스택 트레이스를 붙여넣고 분석합니다.

4. 디버그 빌드에서는 동작하지만 릴리스 빌드에서 문제가 발생할 때

릴리스 빌드에서 ProGuard 문제를 디버깅하려면:

  1. 릴리스 빌드를 로컬에서 실행하여 문제를 재현합니다.
  2. 로그를 확인하여 어떤 클래스나 메소드가 문제인지 파악합니다.
  3. usage.txt 파일을 확인하여 제거된 코드를 확인합니다.
  4. 필요한 클래스를 유지하는 규칙을 추가합니다.

 

#9. R8과 ProGuard의 차이점

안드로이드 스튜디오 3.4부터는 기본적으로 ProGuard 대신 R8 컴파일러가 사용됩니다.

R8이란?

R8은 구글이 개발한 코드 축소 및 난독화 도구로, ProGuard를 대체하기 위해 설계되었습니다. ProGuard보다 빠르고 효율적이며, 더 공격적인 최적화를 제공합니다.

주요 차이점

  1. 속도: R8이 ProGuard보다 빠르게 처리합니다.
  2. 코드 크기: R8은 일반적으로 더 작은 APK를 생성합니다.
  3. 호환성: R8은 ProGuard 규칙과 호환됩니다.
  4. 최적화: R8은 더 적극적인 최적화 기법을 사용합니다.
  5. 통합: R8은 D8 DEX 컴파일러와 함께 작동하도록 설계되었습니다.

R8 사용 설정/해제

gradle.properties 파일에서 R8 사용을 제어할 수 있습니다:

# R8 사용 (기본값)
android.enableR8=true

# ProGuard 사용 (R8 비활성화)
android.enableR8=false

 

#10. 자주 묻는 질문

Q: ProGuard를 적용하면 앱 성능이 향상되나요?

A: 예, ProGuard는 코드를 최적화하고 불필요한 코드를 제거하여 앱의 실행 속도를 향상시킬 수 있습니다. 또한 APK 크기가 작아져 설치 시간과 메모리 사용량이 줄어듭니다.

Q: ProGuard는 앱을 100% 안전하게 보호할 수 있나요?

A: 아니요, ProGuard는 리버스 엔지니어링을 어렵게 만들지만 완전히 막지는 못합니다. 숙련된 분석가는 여전히 난독화된 코드를 분석할 수 있습니다. 중요한 알고리즘이나 키는 서버 측에 두거나 추가 보안 계층을 사용하는 것이 좋습니다.

Q: 모든 빌드에 ProGuard를 적용해야 하나요?

A: 아니요, 일반적으로 릴리스 빌드에만 적용합니다. 디버그 빌드에 적용하면 디버깅이 어려워질 수 있습니다. 하지만 릴리스 전에 ProGuard가 적용된 빌드를 테스트하는 것은 중요합니다.

Q: mapping.txt 파일을 왜 보관해야 하나요?

A: mapping.txt 파일은 난독화된 클래스 이름을 원래 이름으로 매핑해주는 정보를 담고 있습니다. 사용자로부터 받은 크래시 리포트를 분석하려면 해당 버전의 mapping.txt 파일이 필요합니다.

Q: ProGuard 규칙을 어디서 찾을 수 있나요?

A: 많은 인기 라이브러리는 공식 문서나 GitHub 저장소에 ProGuard 규칙을 제공합니다. 또한 proguard-rules-examples 같은 저장소에서 일반적인 라이브러리의 규칙을 찾을 수 있습니다.


결론

ProGuard는 안드로이드 앱의 보안과 성능을 향상시키는 강력한 도구입니다. 적절한 설정과 규칙을 통해 앱을 효과적으로 보호하고 최적화할 수 있습니다.

이 가이드에서는 ProGuard의 기본 개념부터 중요 파일, 설정 방법, 문제 해결까지 모든 내용을 다뤘습니다. 이제 여러분의 안드로이드 앱에 ProGuard를 적용하여 더 안전하고 효율적인 앱을 만들어 보세요!

긴 글 읽어주셔서 감사합니다.

끝.

반응형