[안드로이드] release 해서 앱 실행하면 왜 crash 가 발생할까? (ProGuard leads to NullPointerException)
겪은 이슈
WebView 를 이용하여 하이브리드 App 을 개발하고, release 로 .apk 를 말아 사업팀에 전달하다 발생한 이슈였다.
발생한 에러는 아래와 같다.
자꾸 WebViewClient 쪽에서 url 이 Null 이 발생하여 NullPointerException 이 발생하는 Error 만 발생했다.
WebViewClient.class 83 줄에는 아래와 같은 코드가 있다.
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return super.shouldOverrideUrlLoading(view, request);
}
저 부분에서 왜 Null 이 발생했던 걸까?? Why?
이슈 해결
처음에 이슈가 난 이유를 몰라 한참을 헤맸다.
우선 첫 번째로 헤맸던 건,
android {
// ...
buildTypes {
debug {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.txt',
'proguard-rules.debug.txt'
debuggable true
}
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.txt'
}
}
}
build.gradle 파일 내, 소스에서 buildTypes 중에 release minifyEnabled 'false' 를 하게 되면,
release 시에 Crash 가 발생하지 않았다.
어?? 모지? Proguard 이슈 인가?
난독화하는 과정에서 발생한 이슈인데.. 어디서부터 잘못된걸까? 라는 생각을 하게 됐다.
그래도 이렇게 접근을 해서 해결할 수 있긴 했었다.
일단 내 소스는 WebViewClient 를 커스텀해서 사용하고 있었고,
shouldOverrideUrlLoading 함수를 오버라이딩 하여, 로그를 찍으면서 해당 웹뷰 화면 전환 간 Url 이 잘 찍히는지 확인하는 소스가 있었다. 해당 소스는 아래 Util 쪽, Logger Class 를 이용하여 Log tracking 을 했다.
private static void Logger(int priority, String msg) {
if (GiftshopInfo.LOGGER) {
StringBuilder msgBuilder = new StringBuilder();
msgBuilder.append("[FORSHOP_LOG]").append("[").append(Thread.currentThread().getStackTrace()[4].getMethodName())
.append("()").append("]").append(" ").append(msg)
.append(" (").append(Thread.currentThread().getStackTrace()[4].getFileName()).append(":")
.append(Thread.currentThread().getStackTrace()[4].getLineNumber()).append(")");
Log.println(priority, Thread.currentThread().getStackTrace()[4].getFileName().replace(".java", ""), msgBuilder.toString());
}
}
그러면서 생각했던 건,
어? 난독화를 하게 되면 Null Exception 이 발생하니깐, 난독화 이후에 Class 를 찾지 못하는 이슈일까?
이게 맞았다.
난독화를 하면, 소스 클래스가, a.b.c.d.e.class 이런식으로 소스파일 명들이 바뀌게 된다.
그러다보니 위 Logger Class 내에서 getFileName() 이런 함수를 사용하게 될 경우, 난독화 이전 원하는 Class 를 찾지 못해서 발생한 Error 였다.
오버라이딩해서 사용했던, Log 소스 한 줄을 주석하니, release 시에, Crash 가 발생되지 않았다.
참.. 난독화 관련하여 공부 안한 티가 났다.
난독화란?
코드 난독화는 프로그램 언어로 작성된 코드를, 치환 및 자리바꿈 등의 기법으로 소스코드를 알아보기 어렵게 만드는 기술이다.
그러면 왜 앱 소스를 난독화를 하게 될까?
앱 소스 코드를 분석하기 어렵게 만들기 위해 앱 소스 내에서 난독화 기술을 사용하게 된다. 그 중 Proguard 를 난 사용했다.
그러면 소스를 난독화하는 건 꼭 필요할까?
보안 수준을 높일 수 있기 때문에 난독화한다.
우리는 Google Play Store 를 통해서 Android 앱을 누구나 안드로이드 폰 기종으로 설치를 할 수 있다.
누구나 자유롭게 해당 앱을 다로드 받아, APK 파일을 추출하는 건 가능하다.
이 APK 파일을 디컴파일(Decompile) 을 하여, 소스코드를 보는 것도 가능하다.
이 말은 소스 코드를 뜯어서 앱 내 민감한 데이터를 복제하는데 이용하고 소스코드에 침입할 시에 사용될 수 있다는 말이다.
여기서 사용했던, Proguard 는 식별자 난독화(Renaming) 을 지원하고,
이는 소스코드를 식별자 변환, 클래스/메소드 이름을 특정 키워드로 대체하게 된다.
(ex. a,b,c, ac, k 등)