안드로이드 서포트 라이브러리와 구글 플레이 서비스의 덩치는 날이 갈수록 커져가고 있습니다.

때문에, 다른 라이브러리를 더 추가하다 보면 메소드 참조 수가 증가하여 멀티덱스를 사용해야만 앱을 정상적으로 빌드할 수 있습니다.

멀티덱스란?

안드로이드 앱을 구성하는 코드는 컴파일되어 덱스(dex) 파일로 만들어집니다. 그런데, 덱스 포맷의 제한으로 인해 하나의 덱스 파일에는 최대 65536 개의 메소드 참조만 저장할 수 있습니다.

일반적으로 메소드 참조 수는 사용하는 라이브러리의 수와 앱을 구성하는 코드의 수에 비례하여 증가합니다. 따라서 큰 규모의 앱을 작성하다 보면 앞의 메소드 제한을 훌쩍 뛰어넘게 됩니다.

이 경우, 빌드 시 다음 중 하나의 에러 메시지가 표시됩니다.

Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536
trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.

멀티덱스를 사용하면 덱스 파일을 여러 개로 나누어 이러한 문제를 피할 수 있습니다. 이와 관련된 좀 더 자세한 내용은 공식 페이지를 통해 확인할 수 있습니다.

그냥 멀티덱스를 쓰면 안 되나요?

멀티덱스를 사용하면 큰 어려움 없이 큰 규모의 앱을 빌드할 수 있습니다. 하지만, 빌드 과정에서 앱 내에서 사용하는 클래스를 여러 개의 덱스 파일로 나누어야 하며, 이를 위해 별도의 분석 작업이 추가되므로 빌드 속도가 느려집니다.

또한, 실제로 앱에서 사용하지 않는 클래스도 모두 포함하므로 앱 용량도 늘어나게 됩니다.

즉, 개발자와 사용자 모두에게 좋지 않은 영향을 줍니다.

클래스 다이어트: 꼭 필요한 클래스만 남기자

만약 앱에서 꼭 필요한 클래스만 남기고, 사용하지 않는 나머지 클래스는 빌드 과정에서 제거한다면 어떨까요? 라이브러리의 경우 해당 라이브러리의 일부 기능만 사용하는 경우도 많으므로 꽤나 많은 부분을 제거할 수 있습니다.

프로가드를 사용하면 기본적으로 사용하지 않는 클래스를 제거해 주므로, 최종 바이너리에 포함될 클래스들 간 메소드 참조 수를 하나의 덱스 파일에서 허용하는 수준 이하로 낮출 가능성이 높습니다.

그런데, 프로가드를 사용하면 이와 더불어 난독화도 수행하고, 클래스 내 파일 이름이나 라인 넘버 등의 정보도 모두 제거하므로 디버그가 어렵습니다.

디버그 빌드를 만들 때만 난독화를 하지 않으려면?

먼저, 디버그 빌드를 위한 프로가드 설정 파일을 새로 생성합니다. 여기에서는 proguard-debug.pro 라는 이름으로 생성합니다.

파일을 생성한 후, 다음과 같이 내용을 작성합니다. dontobfuscate 옵션은 난독화를 수행하지 않도록 하며, keepattributes 옵션을 사용하여 소스 파일 정보와 라인 넘버 정보를 유지하도록 했습니다.


# Begin: Debug ProGuard rules

-dontobfuscate
-keepattributes SoureFile,LineNumberTable

# End: Debug ProGuard rules

그 다음, 릴리즈 빌드에 프로가드 설정을 하는 것과 똑같이 디버그 빌드에서 프로가드를 수행하도록 설정합니다.

프로젝트에서 추가로 필요한 프로가드 설정이 있다면 함께 추가하며, 디버그 빌드에만 앞에서 작성한 proguard-debug.pro 설정을 더 추가해줍니다.

android {

  ...

  buildTypes {

    debug {
      // 프로가드 활성화
      minifyEnabled true

      // 릴리즈 빌드에서 사용하는 프로가드 설정을 동일하게 추가합니다.
      // 기본 프로가드 설정
      proguardFile getDefaultProguardFile('proguard-android.txt')
      // 프로젝트에서 필요한 프로가드 설정
      proguardFile 'proguard-rules.pro'

      // 디버그 빌드를 위한 프로가드 설정
      proguardFile 'proguard-debug.pro'

      ...
    }

    release {
      minifyEnabled true

      // 기본 프로가드 설정
      proguardFile getDefaultProguardFile('proguard-android.txt')
      // 프로젝트에서 필요한 프로가드 설정
      proguardFile 'proguard-rules.pro'

      ...
    }
  }
}

이것으로 모든 설정이 끝났습니다. 빌드를 수행하면 모든 빌드 타입에 프로가드가 적용되며, 디버그 빌드에만 난독화가 적용되지 않으므로 디버깅 등에도 문제 없이 사용할 수 있습니다.

적용 사례

프로가드 적용 전 총 메소드 참조 수가 8만여 개인 상태입니다. (두 번째 덱스 파일의 메소드 참조 수가 약 2만여개인 상태)

멀티덱스를 사용하였으므로, APK Analyzer로 확인해보면 dex 파일도 두 개로 분리되어 있는 것을 확인할 수 있습니다.

앞의 방법을 사용하여 최적화를 수행한 결과 메소드 참조 수가 5만여개로 줄어들었고, 덕분에 멀티덱스를 제거할 수 있게 되었습니다.

김태호 (Taeho Kim)

안드로이드와 오픈소스, 코틀린(Kotlin)에 관심이 많습니다. 전 한국 GDG 안드로이드 운영자 및 GDE 안드로이드로 활동했으며, 현재 구글에서 애드몹 기술 지원을 담당하고 있습니다.

kunny Androidhuman


Published