본문 바로가기

안드로이드/트러블 슈팅

[안드로이드 트러블 슈팅] 웹뷰 JavascriptInterface 난독화 주의할 점


안드로이드 앱에서 웹 콘텐츠와의 상호작용은 흔히 웹뷰(Webview)를 통해 이루어집니다. 특히 앱과 웹 서버 간의 통신을 위해 JavascriptInterface를 추가하는 경우가 많습니다. 이번 포스팅에서는 JavascriptInterface를 사용할 때 난독화 때문에 발생한 트러블 슈팅 경험에 대해 공유하고자 합니다.

1. 문제 상황

개발 중인 앱에서는 Firebase Cloud Messaging(FCM) 토큰을 서버에 전송해 푸시 알림을 받을 필요가 있었습니다. 이를 위해 웹뷰를 통해 로그인 시, 안드로이드에서 정의한 로그인 Data Transfer Object(DTO)를 Gson 라이브러리를 이용하여 JSON 문자열로 변환하고, Web Bridge 방식을 통해 데이터를 웹 서버로 전달하는 방식을 사용했습니다. debug 빌드에서는 이 과정이 문제없이 작동했지만, release 빌드에서는 난독화 과정으로 인해 DTO의 프로퍼티들이 a, b, c와 같이 의미 없는 형태로 변환되면서 문제가 발생했습니다.

웹뷰와 브릿지 통신을 하기 위한 DTO
웹뷰와 브릿지 통신을 하기 위한 DTO

2. 문제 원인

release 빌드에서 난독화 옵션(isMinifyEnabled)을 활성화하면, proguard 설정이 필요하며 JavascriptInterface에도 적용되어야 하는데 적용하지 않았었습니다. 그리고 난독화 과정에서 중요한 클래스나 메서드, 변수의 이름이 변경되지 않도록 관련 DTO에 @Keep 어노테이션을 붙여야 하는데 붙이지 않은 것을 확인했습니다. 그래서 release 빌드 시, 문제가 발생했다고 가정한 후 하나씩 기능을 적용했습니다.

3. 해결 방법

안드로이드에서 난독화는 보안을 강화하는 동시에 원치 않은 버그를 발생시킬 수 있는데, 특정 클래스나 함수의 난독화를 제외시키기 위해서 Proguard 파일을 추가해 주었고, 통신을 위한 DTO 클래스에도 @Keep 어노테이션을 추가했습니다.

3-1. JavaInterface 관련 proguard 파일 생성

JavascriptInterface가 포함된 메서드가 난독화되지 않도록 ProGuard 설정 파일에 예외 규칙을 추가했습니다. 이렇게 하면 release 빌드에서도 웹뷰가 JavaScript를 통해 안드로이드 메서드를 호출할 수 있습니다.

-keepattributes JavascriptInterface
-keepattributes *Annotation*

-keepclassmembers class * {
    @android.webkit.JavascriptInterface <methods>;
}

-keepclassmembers class com.company.app.jsBridge.BaseJavascriptInterface {
    public *;
}

-keep public class com.company.app.jsBridge.BaseJavascriptInterface

3-2. gson 관련 proguard 파일 생성

Gson을 사용하여 JSON 문자열을 자바 객체로 변환하거나 그 반대로 변환할 때, 난독화로 인해 필드 이름이 변경되면 안 됩니다. 이를 방지하기 위해 Gson 사용에 필요한 ProGuard 규칙을 별도로 설정했습니다.

##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature


# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }

##---------------End: proguard configuration for Gson  ----------

3-3. @Keep 어노테이션 추가

클래스나 메서드를 난독화 과정에서 제외하기 위해 @Keep 어노테이션을 사용했습니다. 이 어노테이션은 ProGuard 또는 R8이 해당 코드를 유지하도록 지시합니다.

import androidx.annotation.Keep

@Keep
data class AppStaffDevice (
    val appVersion: String,
    val brand: String,
    val fcmToken: String,
    val manufacturer: String,
    val model: String,
    val osVersion: String,
)


한 가지 주의할 점은 @Keep 어노테이션이 Jetpack 라이브러리뿐만 아니라 다른 라이브러리에도 존재한다는 점입니다. 확인을 하지 않고 import 자동완성 기능을 사용하면 아래와 같이 원치 않은 @Keep 어노테이션이 추가될 수 있으니깐 주의해야 합니다.

import com.google.errorprone.annotations.Keep

// 어노테이션의 이름이 동일해서 얼핏보면 문제를 찾기 힘들 수 있음
@Keep
data class AppStaffDevice (
    val appVersion: String,
    val brand: String,
    val fcmToken: String,
    val manufacturer: String,
    val model: String,
    val osVersion: String,
)

4. 결론

난독화는 앱의 보안을 강화하고 리버스 엔지니어링을 방지하는 중요한 과정입니다. 하지만 난독화 설정을 잘못하면 예기치 않은 오류가 발생할 수 있습니다. 특히 웹뷰와 JavascriptInterface를 사용하는 경우, ProGuard 설정과 어노테이션의 올바른 사용이 필수적입니다. 이번 이슈를 통해 난독화 설정의 중요성과 관련된 주의 사항을 다시 한번 인식하게 되었고, 이 경험이 다른 개발자분들께도 도움이 되기를 바랍니다.