[Android] Debug에서 발생하지 않던 에러가 Release에서 발생하는 경우

Choi Sang Rok·2022년 8월 30일
1
post-custom-banner

Nexters 21기의 첫 프로젝트인 "클라이머를 위한 출석 시스템 홀디" 를 배포하면서 생긴 Issue를 해결하기 위한 과정을 담았습니다.


Issue💡

Debug에서 정상적으로 작동하던 홀디 앱을, 출시를 위해 Release Variants로 설정하여도 잘 작동되는지 확인해 보고자 하였습니다.

그런데 앱 로그인, 온보딩이 끝나고 홈 화면으로 들어가는 시점에서 갑자기 앱이 종료되는 현상이 발생하였습니다. 에러는 다음과 같았습니다.

2022-08-30 15:13:33.779 28548-28548/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: team.nexters.semonemo, PID: 28548
    java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter <this>
        at team.nexters.data.moim.mapper.MoimMapperKt.toDomain(Unknown Source:2)
...


Nullable의 문제❓

먼저, 로그에 Error가 찍혀있으니 주어진 에러를 해결하는 것을 초점에 두었습니다.

Data단에서 받은 Response를 Domain Model로 변환하는Mapper에서 위와 같은 에러가 발생하였습니다. 그렇기 때문에 에러의 이유를 다음 2 가지 중 하나일 거라고 생각했습니다.

  1. 서버에서 Nullable한 값을 보내기 때문에 클라이언트 측에서도 모델의 프로퍼티를 Nullable로 지정해야 한다.
  2. 데이터 직렬화는 잘 되었으나, 역직렬화에 문제가 발생했다.

위 두가지 에러를 해결해 보았으나, 근본적으로 해결되지는 않았습니다.



Debug와 Release의 차이에서 오는 것인가❓

결국 다시 처음으로 돌아왔습니다.
이번에는 Debug에서 발생하는 에러가 Release에서만 발생한다 에 초점을 두었고 buildTypes 에서 코드를 최적화 하는 코드를 살펴보았습니다.

각각의 역할은, 다음과 같습니다

  • debuggable : 디버깅 여부
  • minifyEnabled : 사용하지 않는 코드 제거 및 코드 난독화
  • shrinkResources : 사용하지 않는 리소스 제거

StackOverFlow에서 비슷한 맥락의 오류를 발견했고, minifyEnabled , shrinkResources 를 적용하는 부분에서 에러가 발생했을 가능성을 염두하여 최적화 옵션 적용을 제거했습니다.



해결❓


최적화 옵션을 적용하지 않음으로서 당장은 해결할 수 있었습니다.
minifyEnabled은 난독화 여부를 결정하는데, 난독화란 클래스 명이나 함수 명들을 a,b 등의 문자로 치환시켜 기존 코드의 보안성을 강화하는 것을 말합니다.
그러나, 단순히 최적화 옵션 적용이 안된다는 이유만으로 최적화 옵션을 버릴 수는 없었습니다.



진짜 해결❗

MinifyEnabled를 통해 클래스 파일이 난독화가 되면, 클래스나 파일 명이 a,b,c 등이 되어 Reflection을 사용할 수가 없었습니다.
문제가 되는 코드에서 딱히 Reflection을 사용하지 않았는데, 알고보니 Retrofit 에서 Generic Parameter에 대해 Reflection을 사용하고 있다는 것을 알게 되었습니다.

Reflection을 사용하는 클래스 등, 난독화가 되면 안되는 것들은 Proguard에 정의함으로써 방지할 수 있게 됩니다.
따라서 아래와 같이 Proguard Rule을 설정함으로써, MinifyEnabled를 적용했을 때 문제 없이 작동될 수 있었습니다.

# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod

# Retrofit does reflection on method and parameter annotations.
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations

# Keep annotation default values (e.g., retrofit2.http.Field.encoded).
-keepattributes AnnotationDefault

...

Retrofit, Glide 등 외부 라이브러리는 공식 문서에 가면 Proguard Rule이 기재되어 있습니다.

profile
android_developer
post-custom-banner

2개의 댓글

comment-user-thumbnail
2022년 8월 30일

해결되어서 다행입니당 :)

답글 달기
comment-user-thumbnail
2022년 10월 26일

많이 배워갑니다!

답글 달기