[Android] ERROR: Missing classes detected while running R8. Please add the missing classes or apply additional keep rules that are generated in missing_rules.txt.

Minji Jeong·2025년 7월 26일
0

Troubleshooting

목록 보기
24/24
post-thumbnail

프로젝트의 AGP 버전을 8 이상으로 업그레이드 한 이후부터, 앱 최적화를 위해 minifyEnabledshrinkResources 를 true 로 설정한 후 Release mode 에서 빌드 시 발생하는 이슈입니다. Debug mode 에서는 발생하지 않습니다.

Root cause

R8은 Google의 앱 최적화 도구인 Proguard에서 개선된 버전으로, 사용하지 않는 코드와 리소스를 삭제하고 런타임 성능을 최적화하도록 코드를 다시 작성하는 등 앱을 간소화합니다. 이 기능을 사용하려면 app/build.gradle에서 minifyEnabledshrinkResources 을 true로 설정하고 빌드하면 되는데, 활성화 시 빌드 시간이 늘어나고 코드 수정으로 디버깅이 더 어려워 질 수 있기 때문에 릴리즈 빌드 시에만 활성화하는 것이 권장됩니다.

특히 AGP 8.0.0 버전부터는 R8이 기본적으로 full mode로 동작하며, 더 공격적으로 최적화를 합니다. 특정 클래스나 메서드가 사용되지 않거나 특정 플랫폼에만 존재할 때 R8이 경고를 생성하며 빌드 에러가 발생하는데, AGP 업그레이드 이전에는 이러한 경고가 단순 warning으로 처리되었기 때문에 빌드에 문제가 없었습니다. 하지만 업그레이드 후 에러로 바뀌어서 빌드 실패의 원인이 되었습니다.

minifyEnabledshrinkResources 를 다시 비활성화하면 빌드는 되지만, 앞서 말했듯 릴리즈 빌드 시 최적화 옵션을 활성화하는 것이 권장되기 때문에 다른 방법을 찾아보았습니다.

Trouble Shooting

proguard-rules.pro 에 keep rules 추가하기

proguard-rules.pro는 R8로 코드 최적화 시 참고하는 설정 파일입니다. 에러 메세지에서 가장 첫번째로 나오는 해결책이 이 파일에 missing_rules.txt에 생성된 keep rules를 추가하라는 것인데, 이 방식을 적용하면 대부분의 경우 해결됩니다.

-keep class ...
-dontwarn ...

-keep : 특정 클래스나 멤버가 최적화 중 제거되거나 난독화되지 않도록 보존

-dontwarn : 특정 클래스나 패키지를 찾을 수 없을 때 경고 무시, 즉 코드가 참조하고는 있지만 실제 앱 코드에 포함되지 않아도 되는 경우 사용 가능

하지만 저의 경우 위 방법을 적용하면 apk 설치까지는 되어도, 앱 실행시 크래시가 발생하여 앱이 바로 종료되는 문제가 있었습니다. 클래스가 실제로 필요한데도 누락되어 있다면 dontwarn으로 경고를 무시해도 소용이 없기 때문입니다.

릴리즈 모드로 빌드하여 로그 확인

앱 크래시의 원인을 확인하기 위해 릴리즈 모드로 빌드를 시도했더니, 다음과 같은 에러가 로그에 표시되었습니다.

flutter run --release
E/AndroidRuntime(19459): java.lang.InternalError: 
cannot create instance of org.bouncycastle.jcajce.provider.digest.GOST3411$Mappings : 
java.lang.InstantiationException: 
java.lang.Class<org.bouncycastle.jcajce.provider.digest.GOST3411$Mappings> 
has no zero argument constructor
E/AndroidRuntime(19459): 
at org.bouncycastle.jce.provider.BouncyCastleProvider.o(Unknown Source:66)
E/AndroidRuntime(19459): 
at org.bouncycastle.jce.provider.BouncyCastleProvider.n(Unknown Source:6)

bouncycastle이라는 암호화 관련 라이브러리에서 뭔가 문제가 있는 것 같았습니다. 이 문제를 GPT에 검색해보니, 아래와 같이 친절하게 알려주었습니다.

  • BouncyCastle이 내부적으로 GOST3411$Mappings라는 클래스를 리플렉션으로 생성하려고 함
  • 하지만 이 클래스에 기본 생성자 (MyClass())가 없거나, Proguard가 제거하거나 비공개로 바꿔서 접근 불가가 된 것
  • 따라서 인스턴스 생성이 불가해지면서 런타임 시 에러 발생

리플렉션(Reflection)

  • 구체적인 클래스 타입을 알지 못하더라도 그 클래스의 메서드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API
  • 컴파일 타임이 아닌 런타임에 동적으로 특정 클래스의 정보를 추출할 수 있는 프로그래밍 기법
val clazz = Class.forName("com.example.Person")
val person = clazz.newInstance()

따라서 이 문제를 해결하기위해 proguard-rules.pro에 다음과 같은 규칙들을 추가해주었더니, 앱 실행 시 더이상 문제가 발생하지 않았습니다.

# Keep BouncyCastle provider classes
-keep class org.bouncycastle.jcajce.provider.digest.** { *; }
-keep class org.bouncycastle.jcajce.provider.**.Mappings { *; }
-keep class org.bouncycastle.** { *; }
-dontwarn org.bouncycastle.**

Conclusion

사실 이 문제를 해결하기 위해 dependencies에 정의된 라이브러리들의 버전을 업데이트 해보거나, aar 파일을 다운로드받아서 추가해보는 등 많은 시도를 해봤습니다(해결되지는 않았지만요 ㅎㅎ 너무 복잡하게 생각했던 것 같습니다). 결론적으로 제가 생각했을 때 이 문제를 해결하기 위한 가장 좋은 방법은 missing_rules.txt에 나온 규칙들을 proguard-rules.pro에 추가하고 나서, 런타임에 크래시가 나면 릴리즈 모드로 직접 빌드해서 원인을 찾아보는 것 같습니다.

References

🤖 GPT

https://developer.android.com/topic/performance/app-optimization/enable-app-optimization?hl=ko

https://developer.android.com/build/releases/past-releases/agp-8-0-0-release-notes?hl=ko

https://jige.tistory.com/91

https://jeongkyun-it.tistory.com/225

profile
Software Engineer

0개의 댓글