들어가며
Flutter 앱을 릴리즈할 때 안드로이드 빌드 최적화는 필수적입니다. 하지만 최적화 과정에서 예상치 못한 런타임 에러를 마주치기도 합니다. 이번 글에서는 안드로이드 빌드 최적화 설정을 자세히 살펴보고, 실제 발생한 에러 케이스와 해결 과정을 공유하고자 합니다.
안드로이드 빌드 최적화 설정
minifyEnabled의 동작 원리
minifyEnabled
는 R8 컴파일러(이전의 ProGuard)를 사용하여 다음과 같은 최적화를 수행합니다:
- 코드 축소(Code Shrinking)
- 사용되지 않는 클래스, 필드, 메서드 제거
- 라이브러리에서 사용하지 않는 부분 제거
- 데드 코드 제거
- 난독화(Obfuscation)
- 클래스, 메서드, 필드명을 의미 없는 짧은 이름으로 변경
- 리버스 엔지니어링 방지
- 앱 크기 축소 효과
- 최적화(Optimization)
- 메서드 인라이닝
- 중복 코드 제거
- 상수 전파(Constant propagation)
shrinkResources의 역할과 중요성
shrinkResources
는 리소스 최적화를 담당하며 다음과 같은 작업을 수행합니다:
- 리소스 정리
- 사용하지 않는 레이아웃 파일 제거
- 참조되지 않는 이미지 리소스 제거
- 미사용 문자열 리소스 제거
- 리소스 최적화
- 리소스 파일 압축
- 중복 리소스 제거
- 리소스 병합
android {
buildTypes {
release {
minifyEnabled true // 코드 최적화 활성화
shrinkResources true // 리소스 최적화 활성화
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
최적화로 인한 런타임 에러 케이스 스터디
발생한 문제 상황
Flutter Local Notifications 플러그인을 사용하여 로컬 알림을 구현했는데, 디버그 빌드에서는 정상 작동하지만 릴리즈 빌드에서 다음과 같은 에러가 발생했습니다:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: cohttp://m.apposter.bring, PID: 28133
java.lang.RuntimeException: Unable to start receiver com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver: java.lang.RuntimeException: Missing type parameter.
at android.app.ActivityThread.handleReceiver(ActivityThread.java:4905)
...
Caused by: java.lang.RuntimeException: Missing type parameter.
at cohttp://m.google.gson.reflect.a.getSuperclassTypeParameter(Unknown Source:26)
문제 분석
- 스택 트레이스 분석
- GSON 라이브러리의 리플렉션 관련 에러
- 타입 파라미터 정보가 최적화 과정에서 손실됨
- Local Notifications 플러그인의 리시버 클래스에서 문제 발생
- 원인 파악
- minifyEnabled로 인한 GSON 클래스 정보 손실
- 제네릭 타입 정보가 난독화 과정에서 제거됨
- 플러그인의 직렬화/역직렬화 과정 실패
단계별 해결 과정
1. proguard-rules.pro
기본 ProGuard 규칙 추가
# Flutter Local Notifications Plugin
-keep class com.dexterous.** { *; }
➜ 에러 지속 발생
2. GSON 관련 규칙 추가
# Flutter Local Notifications Plugin
-keep class com.dexterous.** { *; }
# GSON 기본 설정
-keepattributes Signature
-keepattributes *Annotation*
-dontwarn sun.misc.**
-keep class com.google.gson.** { *; }
# GSON 직렬화 관련 클래스 보존
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
- 빌드 캐시 정리 및 재빌드
flutter clean cd android ./gradlew clean cd .. flutter build apk --release
- 검증 및 테스트
- 알림 예약 기능 테스트
- 알림 수신 확인
- 백그라운드 동작 확인
최적화 설정 사용 시 주의사항
1. 라이브러리 호환성 확인
- 사용하는 라이브러리의 ProGuard 설정 문서 확인
- 라이브러리 GitHub 이슈 트래커 참고
- 테스트 빌드로 충분한 검증
2. 단계적 최적화 적용
android {
buildTypes {
release {
// 1단계: 코드 축소만 적용
minifyEnabled true
shrinkResources false
// 2단계: 리소스 축소까지 적용
minifyEnabled true
shrinkResources true
// 3단계: 추가 최적화 규칙 적용
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
3. 디버깅 설정
# 에러 추적을 위한 소스 파일명 보존
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile
성능 최적화 결과
최적화 설정 | APK 크기 | 장단점 |
---|---|---|
미적용 | 24MB | 안정적이나 큰 용량 |
minifyEnabled만 적용 | 18MB | 중간 크기, 높은 안정성 |
모두 적용 | 12MB | 최소 크기, 추가 설정 필요 |
결론
안드로이드 빌드 최적화는 앱의 크기를 줄이고 성능을 향상시키는 중요한 과정입니다. 하지만 최적화 과정에서 발생할 수 있는 문제들을 이해하고, 적절한 ProGuard 규칙을 작성하는 것이 중요합니다. 특히 서드파티 라이브러리를 사용할 때는 더욱 신중한 접근이 필요합니다.