ABOUT ME

-

  • Android) Debug 모드에서는 작동하는데 Release 모드에서 Crash 발생할때 (Debug, Release 차이점)
    Android 2021. 5. 23. 14:48


    배포한 앱이 긴 심사기간을 거쳐 어제 출시가 되었습니다.

    디버그 모드에서 테스트도 여러 번 해봤기 때문에 별 걱정 없이 기쁜 마음으로 앱을 설치하고 켜는 순간

    엥? 키자마자 앱이 죽어버렸습니다..

     

    즉, 디버그 모드에서는 작동하지만, 릴리즈 모드에서는 앱이 죽는다..

     

    어제 하루종일 삽질한 내용들을 정리해보겠습니다.

     

    디버그 모드

    • 실행파일에 디버깅 정보를 삽입하여 언제든지 디버깅을 할 수 있도록 하며 Debug 서브 폴더에 실행파일을 만들어줌.
    • 디버깅 정보가 들어가 있기 때문에 실행파일 상태를 확인할 수 있다.
    • 디버그 빌드와 릴리즈 빌드에서 서로 실행 결과가 다른 경우 -> 특히 디버그 빌드에서는 괜찮은데 릴리즈 빌드에서만 오류가 발생하여 앱이 죽는 경우가 있는데, 이런 경우는 대부분 메모리가 깨진 경우 발생. (두 모드에서 동적으로 메모리를 할당하면 힙 영역에 요청한 크기만큼 메모리를 할당받게 되는데 그 초기값이 다르다.)

    릴리즈 모드

    • 초기화 하지 않는다.
    • 같은 문자열 상수라도 서로 다른 공간에 할당.
    • 디버깅 정보를 삽입하지 않고 코드를 최적화하여 실행 파일 크기를 최대한 줄여준다.
    • 속도나 크기면에서 월등히 유리(메모리 점유율 낮아지고 실행도 빨라짐)
    • 더 이상 현재버전에서 내결함성이나 문제점들을 발견할 수 없었을 때 빌드하여 주는 모드.

    디버그 모드 vs 릴리즈 모드

    • 디버깅 정보를 실행코드 안에 넣냐 안 넣냐가 차이점입니다. 
    • 디버거 모드로 컴파일하게 되면 실행상태에서 추적할 수 있는 정보가 실행파일 안에 들어가게 되므로 용량이 커지고, 릴리즈 모드의 경우 디버깅 정보 없이 순수한 소스코드 자체의 기능만 컴파일되어 실행파일로 만들어집니다.
    • 즉 디버그 모드는 에러난 파일과 로깅 확인 가능. 릴리즈 모드는 기본적으로 불가.

     

    더 깊숙이 알아보자면

    • 디버그 모드랑 릴리즈 모드는 bin 내용에 차이가 있다.
    • 릴리즈 모드는 단순히 execution binary라고 하면, 디버그 모드는 디버깅 가능한 정보를 포함한 execution binary가 된다.
    • 결국 디버깅 정보를 볼 수 있는 dll들이 필요하고 ms dev의 MFC42D.DLL, MFCN42D.DLL, MFCO42D.DLL, MSVCRTD.DLL 같은 것들이 필요. (Debug 모드 : mfc42*d.dll, Release 모드 : mfc42.dll) (파일 크기는 디버그 모드가 릴리즈 모드의 3~4배 정도)
    • 릴리즈 모드에서는 앱이 죽는데, 디버그 모드에서는 잘 작동하는 경우는 디버그 모드는 추가 정보가 있기 때문에 boundary(메모리 영역침범)에 대해 조금 더 버티는 운이 좋은 경우가 있다. 이런 경우는 boundary를 유발하는 장애가 반드시 있으니, 샅샅이 버그를 찾는 수밖에.
    • 만약 릴리즈 모드의 에러를 고치지 못해 디버그 모드로 배포해버린다면 -> 디버그 모드는 디버깅을 위한 dll들이 필요한데, 디버그에 필요한 정보들을 실행 시에 계속 체크하니까 느려지고, 디버그용 dll도 함께 배포가 돼버린다. 즉 릴리즈로 배포하자.

     

    buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
            debug {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }

    기본적으로 위와 같이 buildTypes에 릴리즈와 디버그 모드가 구분되어 있습니다.

    릴리즈 모드에서 디버깅을 해보고 싶은 경우는 debuggable true를 넣어주시면 됩니다.

     

    디버그 모드와 릴리즈 모드의 개념은 위에서 알아봤고, 저는 아직 에러를 해결하지 못해 'proguard-android-optimize.txt'를 의심하게 됩니다. Proguard를 적용하지 않았지만, 혹시나 최적화 옵션에 의해 죽는 게 아닌가 의심이 되었습니다.

     

    예를 들어 아래의 코드는 작동은 잘하겠지만, 

    for(i=0; i<10; i++) {
        j=3;
        printf(“%d”, i);
    }

    최적화 옵션이 적용된다면 아래와 같이 바뀝니다. j가 for문 내부에 있을 필요가 없기 때문에. 

    j=3;
    for(i=0; i<10; i++) {
        printf(“%d”, i);
    }

    그래서 Proguard 설정 파일인 'proguard-android.txt', 'proguard-android-optimize.txt'를 비교해 봤습니다.

    둘의 차이는 최적화 사용 유무에 따라 사용하지 않을 경우 전자의 파일, 사용할 경우 후자의 파일을 사용하라고 되어 있습니다.

     

    android platform SDK 해당 링크를 통해 두 파일의 내용을 확인할 수 있습니다.

     

    해당 파일의 위치를 확인하고 싶다면

    • File > Project Structure
    • 왼쪽 창의 SDK Location에서 Android SDK location의 경로를 따라가시면 됩니다.
    • 그다음 tools 폴더 -> proguard 폴더로 가시면 아래와 같이 해당 파일들을 확인 가능합니다.

     

    그리고 디버그 모드로 테스트해보고 에러가 없어 배포하시는 분들이 계실 겁니다.(저처럼..)

    하지만 릴리즈 모드에서 예기치 못한 에러가 발생할 수 있으니 릴리즈 모드로도 테스트 해보고 배포하시는 것을 추천드립니다.

     

    릴리즈 모드로 테스트 하기

    • 배포 시 필요한 keystore 파일을 생성하기 위해 Build -> Generate Singed Bundle/APK로 갑니다.
    • APK로 간 다음 key Password를 입력하고 적절한 위치에 release모드로 생성합니다.

     

    • 그다음 방금 생성한 keystore 파일을 app 폴더 안에 집어넣습니다.

    • 그리고 app/ build.gradle 안에 방금 만든 keystore의 정보를 넣어줍니다.
     signingConfigs {
            release {
                storeFile file('test.jks')
                storePassword "password"
                keyAlias "alias"
                keyPassword "password"
            }
        }
        
         buildTypes {
            release {
                signingConfig signingConfigs.release
            }
    • 이제 안드로이드의 왼쪽 밑을 보면 포스트잇 같은 모양이 있습니다. 이것을 클릭하고 Build Variants를 눌러줍니다.

     

    • 기본적으로 Active Build Variant가 debug로 되어 있을 텐데 release로 바꿔줍니다.
    • 그리고 run을 하면 릴리즈 모드로 테스트할 수 있습니다.

     

     

    자 그러면 저는 에러를 해결했을까요?

     

    제가 만든 앱 같은 경우 디바이스 아이디가 필요했는데, 아래와 같이 디버그 모드일 때 디바이스 아이디를 받아오는 Interceptor를 이용하고 있었습니다.

    if (BuildConfig.DEBUG) {
                val loggingInterceptor = HttpLoggingInterceptor()
                loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
                builder.addInterceptor(loggingInterceptor)
                builder.addInterceptor(AuthorizationInterceptor(PreferenceManager(context)))
            }

    그래서 릴리즈 모드에서 당연히 앱이 죽는 것입니다.

    해당 코드를 밖으로 빼주면서 해결하였습니다.


    결론 

    급할수록 돌아가라..

    반응형

    댓글

Designed by Me.