Play Feature Delivery 개요

Google Play의 앱 제공 모델은 Android App Bundle을 사용하여 각 사용자의 기기 설정에 최적화된 APK를 생성하고 제공하므로 사용자는 앱을 실행하는 데 필요한 코드와 리소스만 다운로드하면 됩니다.

Play Feature Delivery는 App Bundle의 고급 기능을 사용하여 앱의 특정 기능을 조건부로 전송하거나 주문형으로 다운로드할 수 있도록 합니다. 이를 위해 먼저 이러한 기본 앱의 기능을 기능 모듈로 분리해야 합니다.

기능 모듈 빌드 구성

Android 스튜디오를 사용해 새 기능 모듈을 만들 때 IDE는 다음과 같은 Gradle 플러그인을 모듈의 build.gradle 파일에 적용합니다.

// The following applies the dynamic-feature plugin to your feature module.
// The plugin includes the Gradle tasks and properties required to configure and build
// an app bundle that includes your feature module.

apply plugin: 'com.android.dynamic-feature'

표준 애플리케이션 플러그인에서 사용 가능한 많은 속성은 기능 모듈에서도 사용할 수 있습니다. 다음 섹션에서는 기능 모듈의 빌드 구성에 포함해야 할 속성 또는 포함해서는 안 될 속성에 관해 설명합니다.

기능 모듈 빌드 구성에 포함해서는 안 되는 것

각 기능 모듈은 기본 모듈에 종속되기 때문에 특정 구성도 상속합니다. 따라서 기능 모듈의 build.gradle 파일에서 다음을 생략해야 합니다.

  • 서명 구성: App Bundle은 기본 모듈에 지정된 서명 구성을 사용해 서명됩니다.
  • minifyEnabled 속성: 기본 모듈의 빌드 구성에서만 앱 프로젝트 전체의 코드 축소를 사용 설정할 수 있습니다. 따라서 기능 모듈에서 이 속성을 생략해야 합니다. 그러나 각 기능 모듈에 추가 ProGuard 규칙을 지정할 수 있습니다.
  • versionCodeversionName: App Bundle을 빌드할 때 Gradle은 기본 모듈이 제공하는 앱 버전 정보를 사용합니다. 기능 모듈의 build.gradle 파일에서 이러한 속성을 생략해야 합니다.

기본 모듈과의 관계 설정

Android 스튜디오는 기능 모듈을 생성할 때 아래와 같이 android.dynamicFeatures 속성을 기본 모듈의 build.gradle 파일에 추가하여 기능 모듈이 기본 모듈에 표시되게 합니다.

// In the base module’s build.gradle file.
android {
    ...
    // Specifies feature modules that have a dependency on
    // this base module.
    dynamicFeatures = [":dynamic_feature", ":dynamic_feature2"]
}

또한 Android 스튜디오는 아래와 같이 기본 모듈을 기능 모듈의 종속 항목으로 포함합니다.

// In the feature module’s build.gradle file:
...
dependencies {
    ...
    // Declares a dependency on the base module, ':app'.
    implementation project(':app')
}

추가 ProGuard 규칙 지정

기본 모듈의 빌드 구성만으로 앱 프로젝트의 코드 축소를 사용 설정할 수 있지만 아래와 같이 proguardFiles 속성을 사용해 각 기능 모듈에 맞춤 ProGuard 규칙을 제공할 수 있습니다.

android.buildTypes {
     release {
         // You must use the following property to specify additional ProGuard
         // rules for feature modules.
         proguardFiles 'proguard-rules-dynamic-features.pro'
     }
}

이러한 ProGuard 규칙은 빌드 타임에 다른 모듈(기본 모듈 포함)의 규칙과 병합됩니다. 따라서 각 기능 모듈에서 새로운 규칙 집합을 지정할 수 있지만 이 규칙은 앱 프로젝트의 모든 모듈에 적용됩니다.

앱 배포

기능 모듈을 지원하는 앱을 개발하는 동안 일반적인 방식과 마찬가지로 메뉴 바에서 Run > Run을 선택(또는 툴바에서 Run 을 클릭)하여 연결된 기기에 앱을 배포할 수 있습니다.

앱 프로젝트에 하나 이상의 기능 모듈이 포함된 경우 다음과 같이 기존 실행/디버그 구성을 수정하여 앱을 배포할 때 포함할 기능을 선택할 수 있습니다.

  1. 메뉴 바에서 Run > Edit Configurations를 선택합니다.
  2. Run/Debug Configurations 대화상자의 왼쪽 패널에서 원하는 Android App 구성을 선택합니다.
  3. General 탭의 Dynamic features to deploy에서 앱을 배포할 때 포함할 각 기능 모듈 옆의 체크박스를 선택합니다.
  4. OK를 클릭합니다.

기본적으로 Android 스튜디오는 App Bundle을 사용하여 앱을 배포하는 앱은 배포하지 않습니다. 대신 IDE가 APK 크기보다는 배포 속도에 최적화된 APK를 빌드하여 기기에 설치합니다. App Bundle에서 APK와 인스턴트 환경을 대신 빌드하고 배포하도록 Android 스튜디오를 구성하려면 실행/디버그 구성을 수정해야 합니다.

맞춤 전송을 위한 기능 모듈 사용

기능 모듈의 고유한 이점은 Android 5.0(API 수준 21) 이상을 실행하는 기기에 앱의 다양한 기능을 다운로드하는 방법 및 시기를 맞춤설정할 수 있다는 점입니다. 예를 들어 앱의 최초 다운로드 크기를 줄이기 위해 특정 기능은 주문형으로 필요에 따라 다운로드되도록 설정하거나 사진 촬영 또는 증강 현실 기능 지원과 같은 특정 기능을 지원하는 기기에만 다운로드되도록 설정할 수 있습니다.

앱을 App Bundle로 업로드하면 기본적으로 매우 최적화된 다운로드를 제공할 수 있지만 더 고급의 맞춤설정 가능한 기능 제공 옵션을 사용하려면 기능 모듈을 사용하여 앱 기능을 추가로 설정하고 모듈화해야 합니다. 즉, 기능 모듈은 각각 필요에 따라 다운로드되도록 설정할 수 있는 모듈식 기능을 생성하기 위한 기본 구성요소를 제공합니다.

사용자가 온라인 마켓플레이스에서 상품을 사고팔 수 있는 앱을 생각해 보세요. 앱의 다음 기능을 각각 별도의 기능 모듈로 모듈화할 수 있습니다.

  • 계정 로그인 및 생성
  • 마켓플레이스 탐색
  • 판매할 상품 배치
  • 결제 처리

아래 표에서는 기능 모듈이 지원하는 다양한 제공 옵션을 소개하고, 이러한 옵션을 사용하여 샘플 마켓플레이스 앱의 최초 다운로드 크기를 최적화할 수 있는 방법을 설명합니다.

제공 옵션 동작 샘플 사용 사례 시작하기
설치 시 제공 위에서 설명한 제공 옵션을 전혀 설정하지 않은 기능 모듈은 기본적으로 앱 설치 시 다운로드됩니다. 이 동작은 고급 제공 옵션을 점진적으로 수용할 수 있다는 의미이기 때문에 중요합니다. 예를 들어 Play Core 라이브러리를 사용하여 주문형 다운로드를 완전히 구현한 후에만 앱 기능 모듈화를 활용하고 주문형 전송을 사용 설정할 수 있습니다.

또한 앱에서 나중에 기능을 제거하도록 요청할 수 있습니다. 만약 앱 설치 시 특정 기능이 필요하며 그 후에는 필요하지 않다면 기기에서 해당 기능을 삭제하도록 요청함으로써 설치 크기를 줄일 수 있습니다.

마켓플레이스에서 상품을 사고파는 방법에 관한 대화형 가이드와 같은 특정 교육 활동이 앱에 있다면 기본적으로 앱 설치 시 이러한 기능을 포함할 수 있습니다.

하지만 앱의 설치 크기를 줄이기 위해 앱에서는 사용자가 교육을 마친 후 이 기능을 삭제하도록 요구할 수 있습니다.

고급 제공 옵션을 설정하지 않은 기능 모듈을 사용하여 앱을 모듈화합니다.

사용자에게 더 이상 필요하지 않은 특정 기능 모듈을 삭제함으로써 앱의 설치 크기를 줄이는 방법에 관해 알아보려면 설치된 모듈 관리를 참고하세요.

주문형 제공 앱이 필요에 따라 기능 모듈을 요청하고 다운로드할 수 있도록 허용합니다. 마켓플레이스 앱을 사용하는 사용자의 20%만 판매를 위해 상품을 게시한다면 대부분의 사용자를 위해 최초 다운로드 크기를 줄일 수 있는 좋은 전략은 사진을 찍고 상품 설명을 추가하며 상품을 판매 가능하도록 배치하는 것과 관련된 기능을 주문형 다운로드로 설정하는 것입니다. 즉, 사용자가 마켓플레이스에 판매할 상품을 배치하는 데 관심을 보일 때만 앱의 판매 기능에 관한 기능 모듈이 다운로드되도록 설정할 수 있습니다.

또한 사용자가 일정 기간이 지난 후 더 이상 상품을 판매하지 않는다면 앱이 이 기능을 제거하도록 요청하여 설치된 크기를 줄일 수 있습니다.

기능 모듈을 만들고 주문형 제공을 설정합니다. 그러면 앱이 Play Core 라이브러리를 사용하여 주문형 모듈을 다운로드하도록 요청할 수 있습니다.
조건부 제공 앱 설치 시 모듈화된 기능이 다운로드될지 여부를 결정하기 위해 하드웨어 기능, 언어, 최소 API 수준과 같은 특정 사용자 기기 요구사항을 지정하도록 허용합니다. 마켓플레이스 앱이 전 세계에 제공되는 경우 특정 지역에서만 사용되는 결제 수단을 지원해야 할 수 있습니다. 앱의 최초 다운로드 크기를 줄이려면 특정 유형의 결제 수단을 처리하는 별도의 기능 모듈을 만들어 등록된 언어에 따라 사용자 기기에 조건부로 설치되도록 할 수 있습니다. 기능 모듈을 만들고 조건부 제공을 설정합니다.
인스턴트 제공 Google Play 인스턴트를 사용하면 사용자가 기기에 앱을 설치하지 않고도 앱과 상호작용할 수 있습니다. 또는 Google Play 스토어의 '사용해 보기' 버튼이나 개발자가 만든 URL을 통해 앱을 사용해 볼 수 있습니다. 이런 형식으로 콘텐츠를 제공하면 더 쉽게 앱 참여도를 높일 수 있습니다.

인스턴트 제공 기능을 사용하면 Google Play 인스턴트를 활용하여 사용자가 앱을 설치하지 않고도 앱의 특정 기능을 즉시 사용해 보도록 할 수 있습니다.

게임의 처음 몇 레벨을 용량이 작은 기능 모듈에 포함한 게임을 생각해 보세요. 이 모듈을 인스턴트로 지원하도록 설정함으로써 사용자는 URL 링크나 '사용해 보기' 버튼을 통해 앱을 설치하지 않고도 게임을 즉시 해볼 수 있습니다. 기능 모듈을 만들고 인스턴트 제공을 설정합니다. 그러면 앱이 Play Core 라이브러리를 사용하여 주문형 모듈을 다운로드하도록 요청할 수 있습니다.

기능 모듈을 사용하여 앱 기능을 모듈화하는 것은 첫 단계에 불과합니다. Google Play 인스턴트를 지원하려면 앱의 기본 모듈 및 해당 인스턴트 지원 기능의 다운로드 크기가 엄격한 크기 제한을 충족해야 합니다. 자세한 내용은 앱 또는 게임 크기를 줄여 인스턴트 환경 사용 설정을 참고하세요.

리소스의 URI 빌드

URI를 사용하여 기능 모듈에 저장된 리소스에 액세스하려면 다음과 같이 Uri.Builder()를 사용하여 기능 모듈 리소스 URI를 생성하는 방법을 사용하세요.

Kotlin

val uri = Uri.Builder()
                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
                .authority(context.getPackageName()) // Look up the resources in the application with its splits loaded
                .appendPath(resources.getResourceTypeName(resId))
                .appendPath(String.format("%s:%s",
                  resources.getResourcePackageName(resId), // Look up the dynamic resource in the split namespace.
                  resources.getResourceEntryName(resId)
                  ))
                .build()

자바

String uri = Uri.Builder()
                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
                .authority(context.getPackageName()) // Look up the resources in the application with its splits loaded
                .appendPath(resources.getResourceTypeName(resId))
                .appendPath(String.format("%s:%s",
                  resources.getResourcePackageName(resId), // Look up the dynamic resource in the split namespace.
                  resources.getResourceEntryName(resId)
                  ))
                .build().toString();

리소스 경로의 각 부분은 런타임 시 구성되며 분할 APK가 로드된 후 올바른 네임스페이스가 생성되었는지 확인합니다.

URI가 생성되는 방식의 예로 다음과 같은 이름의 앱 및 기능 모듈이 있다고 가정해 보겠습니다.

  • 앱 패키지 이름: com.example.my_app_package
  • 기능의 리소스 패키지 이름: com.example.my_app_package.my_dynamic_feature

위 코드 스니펫의 resId가 기능 모듈의 'my_video'라는 원시 파일 리소스를 참조한다면 위의 Uri.Builder() 코드는 다음을 출력합니다.

android.resource://com.example.my_app_package/raw/com.example.my_app_package.my_dynamic_feature:my_video

그러면 앱에서 이 URI를 사용하여 기능 모듈의 리소스에 액세스할 수 있습니다.

URI의 경로를 검증하기 위해 다음과 같이 APK Analyzer를 사용하여 기능 모듈 APK를 검사하고 패키지 이름을 확인할 수 있습니다.

컴파일된 리소스 파일의 콘텐츠를 검사하는 APK Analyzer의 스크린샷

그림 2. APK Analyzer를 사용하여 컴파일된 리소스 파일의 패키지 이름을 검사합니다.

기능 모듈 고려사항

기능 모듈을 사용하면 빌드 속도와 엔지니어링 속도를 개선하고 앱 기능 제공 범위를 광범위하게 맞춤설정하여 앱 크기를 줄일 수 있습니다. 하지만 기능 모듈을 사용할 때 유념해야 할 몇 가지 제약 조건과 극단적 사례가 있습니다.

  • 50개 이상의 기능 모듈을 조건부 제공 또는 주문형 제공을 통해 단일 기기에 설치하면 성능 문제가 발생할 수 있습니다. 삭제 가능으로 구성되지 않은 설치 시 모듈은 기본 모듈에 자동으로 포함되며 각 기기에서 하나의 기능 모듈로만 간주됩니다.
  • 설치 시 제공을 위해 삭제 가능으로 구성하는 모듈 수를 10개 이하로 제한하세요. 제한하지 않으면 앱의 다운로드 및 설치 시간이 늘어날 수 있습니다.
  • Android 5.0(API 수준 21) 이상을 실행하는 기기에서만 주문형 기능 다운로드 및 설치를 지원합니다. 이전 버전의 Android에서 기능을 사용할 수 있도록 하려면 기능 모듈을 만들 때 융합을 사용 설정해야 합니다.
  • SplitCompat을 사용 설정하여 다운로드된 기능 모듈 중에서 주문형으로 제공된 기능 모듈에 앱이 액세스할 수 있도록 합니다.
  • 기능 모듈은 android:exportedtrue로 설정된 매니페스트에서 활동을 지정하면 안 됩니다. 다른 앱이 활동을 실행하려고 할 때 기기가 기능 모듈을 다운로드했다는 보장이 없기 때문입니다. 또한 앱은 코드 및 리소스에 액세스하기 전에 기능이 다운로드되었는지 확인해야 합니다. 자세한 내용은 설치된 모듈 관리를 참고하세요.
  • Play Feature Delivery를 사용하려면 App Bundle을 사용하여 앱을 게시해야 하므로 App Bundle의 알려진 문제에 관해 알고 있어야 합니다.

기능 모듈 매니페스트 참조

Android 스튜디오를 사용하여 새로운 기능 모듈을 만들 때 IDE에는 모듈이 기능 모듈처럼 작동하는 데 필요한 대부분의 매니페스트 속성이 포함됩니다. 또한 일부 속성은 컴파일 시 빌드 시스템에 의해 삽입되므로 직접 지정하거나 수정하지 않아도 됩니다. 다음 표에서는 기능 모듈에 중요한 매니페스트 속성을 설명합니다.

속성 설명
<manifest
...
일반적인 <manifest> 블록입니다.
xmlns:dist="http://schemas.android.com/apk/distribution" 새로운 dist: XML 네임스페이스를 지정하며 아래에서 자세히 설명합니다.
split="split_name" Android 스튜디오에서 App Bundle을 빌드할 때 이 속성을 포함합니다. 따라서 이 속성을 직접 포함하거나 수정하면 안 됩니다.

Play Core 라이브러리를 사용하여 주문형 모듈을 요청할 때 앱이 지정하는 모듈 이름을 정의합니다.

Gradle에서 이 속성의 값을 결정하는 방법

기본적으로 Android 스튜디오를 사용하여 기능 모듈을 만들 때 IDE는 모듈 이름으로 지정한 항목을 사용하여 Gradle 설정 파일에서 모듈을 Gradle 하위 프로젝트로 식별합니다.

App Bundle을 빌드할 때 Gradle은 하위 프로젝트 경로의 마지막 요소를 사용하여 모듈의 매니페스트에 이 매니페스트 속성을 삽입합니다. 예를 들어 MyAppProject/features/ 디렉터리에 새로운 기능 모듈을 생성하고 모듈 이름을 'dynamic_feature1'이라고 지정했다면 IDE는 settings.gradle 파일에 ':features:dynamic_feature1'을 하위 프로젝트로 추가합니다. App Bundle을 빌드할 때 Gradle은 모듈의 매니페스트에 <manifest split="dynamic_feature1">을 삽입합니다.

android:isFeatureSplit="true | false"> Android 스튜디오에서 App Bundle을 빌드할 때 이 속성을 포함시킵니다. 따라서 이 속성을 직접 포함하거나 수정하면 안 됩니다.

이 모듈이 기능 모듈임을 지정합니다. 기본 모듈 및 구성 APK의 매니페스트는 이 속성을 생략하거나 false로 설정합니다.

<dist:module 이 새로운 XML 요소는 모듈이 패키징되는 방식 및 APK로 배포되는 방식을 결정하는 속성을 정의합니다.
dist:instant="true | false" 모듈이 Google Play 인스턴트를 통해 인스턴트 환경으로 사용 가능한지 여부를 지정합니다.

앱에 하나 이상의 인스턴트 지원 기능 모듈이 포함되어 있다면 기본 모듈도 인스턴트를 지원하도록 설정해야 합니다. Android 스튜디오 3.5 이상을 사용 중이라면 인스턴트 지원 기능 모듈을 생성할 때 IDE에서 이 작업을 해줍니다.

XML 요소를 true로 설정하는 동시에 <dist:on-demand/>도 설정할 수 없습니다. 그러나 여전히 Play Core 라이브러리를 사용하여 인스턴트 환경으로 인스턴트 지원 기능 모듈의 주문형 다운로드를 요청할 수 있습니다. 사용자가 앱을 다운로드하고 설치할 때 기기는 기본적으로 기본 APK와 함께 앱의 인스턴트 지원 기능 모듈을 다운로드하여 설치합니다.

dist:title="@string/feature_name" 사용자에게 표시될 모듈 제목을 지정합니다. 예를 들어 기기에서 다운로드 확인을 요청할 때 이 제목을 표시합니다.

기본 모듈의 module_root/src/source_set/res/values/strings.xml 파일에 이 제목의 문자열 리소스를 포함해야 합니다.

<dist:fusing dist:include="true | false" />
</dist:module>
Android 4.4(API 수준 20) 이하를 실행 중인 기기를 타겟팅하는 멀티 APK에 모듈을 포함할지 여부를 지정합니다.

또한 bundletool을 사용하여 App Bundle에서 APK를 생성할 때 이 속성을 true로 설정하는 기능 모듈만 범용 APK에 포함됩니다. 범용 APK란 앱이 지원하는 모든 기기 설정에 관한 코드와 리소스가 포함된 모놀리식 APK입니다.

<dist:delivery> 아래와 같이 모듈 제공을 맞춤설정하는 옵션을 캡슐화합니다. 각 기능 모듈은 이러한 맞춤 제공 옵션 유형 한 가지만 설정해야 합니다.
<dist:install-time> 모듈이 설치 시에 사용 가능해야 함을 지정합니다. 이는 다른 맞춤 제공 옵션 유형을 지정하지 않는 기능 모듈의 기본 동작입니다.

설치 시 다운로드에 관한 자세한 내용은 설치 시 제공 구성을 참고하세요.

또한 이 노드는 기기 기능, 사용자 국가, 최소 API 수준과 같은 특정 요구사항에 부합하는 기기로 모듈을 제한하는 조건을 지정할 수도 있습니다. 자세한 내용은 조건부 전송 구성을 참조하세요.

<dist:removable dist:value="true | false" />

설정을 해제하거나 false로 설정하면 bundletool이 번들에서 분할 APK를 생성할 때 기본 모듈에 설치 시간 모듈을 통합합니다. 통합으로 인해 분할 APK 수가 줄어들기 때문에 이 설정으로 앱 성능이 향상될 수 있습니다.

removabletrue로 설정된 경우: 설치 시간 모듈이 기본 모듈에 통합되지 않습니다. 나중에 모듈을 제거하려면 true로 설정합니다. 그러나 삭제될 수 있는 모듈을 너무 많이 구성하면 앱의 설치 시간이 늘어날 수 있습니다.

기본값은 false입니다. 기능 모듈에 융합을 사용 중지하려는 경우에만 매니페스트에서 이 값을 설정해야 합니다.

참고: Android Gradle 플러그인 4.2를 사용하거나 명령줄에서 bundletool v1.0을 사용하는 경우에만 이 기능을 사용할 수 있습니다.

</dist:install-time>  
<dist:on-demand/> 모듈이 주문형 다운로드로 제공되어야 함을 지정합니다. 즉, 설치 시에는 모듈을 사용할 수 없지만 앱에서 나중에 다운로드하도록 요청할 수 있습니다.

주문형 다운로드에 관한 자세한 내용은 주문형 제공 구성을 참고하세요.

</dist:delivery>
<application
android:hasCode="true | false">
...
</application>
기능 모듈이 DEX 파일을 생성하지 않는다면, 즉 나중에 DEX 파일 형식으로 컴파일되는 코드가 기능 모듈에 포함되어 있지 않다면 다음을 실행해야 합니다. 그러지 않으면 런타임 오류가 발생할 수 있습니다.
  1. 기능 모듈의 매니페스트에서 android:hasCode"false"로 설정합니다.
  2. 기본 모듈의 매니페스트에 다음을 추가합니다.
    
    <application
      android:hasCode="true"
      tools:replace="android:hasCode">
      ...
    </application>
    

추가 리소스

기능 모듈 사용에 관해 자세히 알아보려면 다음 리소스를 참고하세요.

블로그 게시물

동영상