이 녀석들은 내가 android developer 사이트 가이드 문서 중 가장 좋아하지 않는 부분이다.
번역된 용어가 너무나 헷갈기 때문에..ㅜㅜ
따라서 이 블로거의 혼란을 야기하므로 용어는 영어로 사용하고자 합니다.
빌드 변형 = build variant
빌드 유형 = build types
제품 버전 = product flavors
버전 차원 = flavor dimension
소스 세트 = source sets
종속 = dependencies
서명 = signing
API 수준 = API level
이걸 왜 써?
- "하나의 프로젝트에서 다양한 버전의 앱을 만드는 방법"과 "dependencies 항목 및 signing 구성을 올바르게 관리"할 수 있으니까요.
1. "하나의 프로젝트에서 다양한 버전의 앱을 만드는 방법"
build variant(빌드 변형)
- 각각의 'build variant'마다 빌드할 수 있는 특정 버전의 앱을 나타낸다.
- 임의로 예를 들면,
- ex1) 제한된 '무료 버전'의 앱을 빌드하고, 더 많은 기능의 '유료 버전'의 앱을 빌드
- ex2) API 레벨, 각종 디바이스별 스펙 등 '서로 다른 여러 단말을 타겟팅하여 여러 버전의 앱을 빌드하는 경우'
-> 갤럭시 탭에 맞춰 앱을 개발하는데 갤럭시 탭S는 API 33이고, 갤러시 탭A는 API 29인 경우
- Gradle이 특정 규칙 세트를 사용하여 'build types과 product flavors에 구성된 설정', 'main/ 소스 세트에 포함된 구성, 코드, 리소스'를 조합'한 결과이다.
- 자체 기능과 리소스로 각 build variants을 맞춤설정하려면 -> "source sets"를 만들고 관리해야 한다.
- 개발자가 직접 build variants구성하는 것은 아니라 build variants을 형성하는 build types과 product flavors을 구성한다.
그렇다면, build variants 어떻게 확인해?
1) build variants를 구성 or 변경하면 스튜디오 상단 알림바에서 Sync Now가 나오고 이를 클릭
2) 이후 Gradle이 build types과 product flavors 에 따라 자동으로 빌드 변형을 생성하고 <product-flavor><Build-Type>에 따라 build variants의 이름을 지정합니다.
그렇다면, 어떻게 원하는 build variants으로 앱 실행해?
ㄴ> 'Build > Select Build Variant'로 이동하여 메뉴에서 build variants을 선택합니다.
- [build variants의 명명 형식]
'product flavors명+Build types명' 또는 '(flavor dimension1에 속한 product flavors명)+ ... + (flavor dimensionN에 속한product flavors명) + (Build types명)' |
=> 이제 'product flavors명 + Build types명'로 조합한 이름으로 빌드가능한 build variants 버전이 생긴다.
- [build variants별 APK 명명 디폴트 형식] (별도 지정 가능)
app-(flavor dimension1에 속한 product flavor명)-(flavor dimension1명)-(build type명).apk |
- [build variants의 개수 ]
(flavor dimension1에 속한 product flavors갯수) * ... * (flavor dimensionN에 속한product flavors갯수) + (Build types갯수) |
ex1) build type이 'debug'이고, product flavors가 'demo'인 경우
=> build variants는 'demoDebug' 버전이며,
여기에는 'demo' product flavors, 'debug' build types, main/ 소스 세트에 포함된 구성 및 리소스의 조합한 결과가 포함됩니다.
ex2) 'demo' 및 'full' product flavors 을 생성하고 기본 'debug' 및 'release' build types인 경우
-> Gradle은 아래와 같은 build variants을 만듭니다.
demoDebug
demoRelease
fullDebug
fullRelease
ex3) 갯수 계산하기
flavor dimension "mode" 에 속하는 product flavors ['demo', 'full']을 생성
flavor dimension "api" 에 속하는 product flavors ['minApi24', 'minApi23', 'minApi21']을 생성하고,
build types은 'debug' 및 'release' 인 경우,
=> 총 12개의 build variants을 만듭니다.
[minApi24, minApi23, minApi21]갯수 * [demo, full]갯수 * [debug, release]갯수 = 12개
- 빌드 변형: [minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
- 해당 APK: app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
빌드 변형에 대응하는 APK ex) app-minApi24-demo-debug.apk
build types(빌드 유형)
- 다양한 빌드 및 패키징 설정(예: 디버그 옵션, 서명 키)을 지정한다.
- 기본적으론 'debug' 와 'release' 2가지 build types를 사용한다.(새 모듈을 생성 시 자동으로 생성)
ㄴ> 'debug' build types이 빌드 구성 파일에 없더라도, Android 스튜디오는 debuggable true로 이 build types을 구성한다. 이를 통해 보안 Android 기기에서 앱을 디버그할 수 있으며 일반 디버그 키 저장소로 앱 서명을 구성한다.
[build types 설정하기]
=> app 단 build.gradle.kts 내 / android{} 블록 내 / buildTypes {} 블록에 build type들의 구성 및 설정 사항들을 지정할 수 있다.
android {
...
buildTypes {
// build type들의 구성 및 설정 사항들을 지정
...
}
}
[build types 추가하기]
=> 앱 단 build.gradle.kts 에 create("추가할 build types명") { ... }블록을 추가
android {
...
buildTypes {
...
create("추가할 build types명") {
// "추가할 build types명"의 구성&설정 사항을 명시
}
}
}
[다른 build types구성을 그대로 복사해오기]
=> 'init'With(getByName("복사할build types명"))' 속성을 사용하면, 다른 build types에서 구성을 복사해오고 '추가할 build types명'의 별도 구성을 추가할 수 있다.
- Sample) 앱 단 build.gradle.kts 에 'staging'이라는 build types 추가
android {
defaultConfig {
manifestPlaceholders["hostName"] = "www.example.com"
...
}
buildTypes {
getByName("release") {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}
getByName("debug") {
applicationIdSuffix = ".debug"
isDebuggable = true
}
create("staging") {
initWith(getByName("debug")) // 1. "debug"버전을 설정을 복사한 다음
// 2. 아래 코드에서 매니페스트 자리 표시자와 응용프로그램 ID를 변경함
manifestPlaceholders["hostName"] = "internal.example.com"
applicationIdSuffix = ".debugStaging"
}
}
}
- build types 공식문서 :
https://developer.android.com/reference/tools/gradle-api/current/com/android/build/api/dsl/BuildType
product flavors (제품 버전)
- 특정 기능과 기기 요구사항(예: 맞춤 소스 코드, 리소스, 최소 API 수준)을 지정한다.
- 'demo', 'dev', 'pre-prod', 'prod' 등 다양하게 사용된다.
[product flavors 설정 및 추가하기]
=> app 단 build.gradle.kts 내 / android{} 블록 내 / productFlavors {} 블록에 product flavors 들의 구성 및 설정 사항들을 지정할 수 있다.
android {
...
productFlavors {
// productFlavors들의 구성 및 설정 사항들을 지정
...
}
}
- product flavors 은 defaultConfig와 동일한 속성을 모두 사용할 수 있다.
ㄴ> 이유) defaultConfig가 실제로 ProductFlavor 클래스에 속하기 때문
ㄴ> Sample) 각 버전마다 applicationId와 같은 defaultConfig 속성값을 별도로 지정할 수 있다
android {
// defaultConfig 속성 예
defaultConfig {
applicationId = "아이디1"
minSdk = 21
targetSdk = 33
versionCode = majorVersion * 100000 + minorVersion * 100 + patchVersion
versionName = "$majorVersion.$minorVersion.$patchVersion"
vectorDrawables.useSupportLibrary = true
multiDexEnabled = true
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
manifestPlaceholders["kakaoNativeAppKey"] = "${Constants.Common.KAKAO_NATIVE_APP_KEY}"
}
// defaultConfig 속성이지만 productFlavors에 다르게 적용할 경우 속성 별도 지정 가능
productFlavors {
create("dev") {
applicationId = "dev 버전용 아이디"
}
create("prod") {
applicationId = "prod 버전용 아이디"
}
}
}
[product flavor별 applicationId 설정하기]
- 왜 필요한가?
앱은 동일한 applicationId를 가질 수 없다.
이는 여러 버전이 있는 하나의 앱도 마찬가지이며, 특히 Google Play 스토어에서 product flavors별 다양한 앱 버전이 각각의 등록정보를 갖추도록 하려면 applicationId가 서로 다른 별개의 product flavors을 만들어야 한다.
- [방법]
#1) productFlavors 블록 내부의 각 버전에 applicationId 속성을 product flavors마다 각각 정의
#2*) applicationIdSuffix를 사용하여 공통 applicationId값에 세그먼트만 추가하여 모두 다르게 적용
ㄴ> sample) 샘플대로 적용 시 'free' 제품 버전의 애플리케이션 ID는 'com.example.myapp.free'
android {
defaultConfig {
applicationId = "com.example.myapp"
}
productFlavors {
create("free") {
applicationIdSuffix = ".free"
}
create("pro") {
applicationIdSuffix = ".pro"
}
}
}
[build types별로도 applicationId 설정하기]
- 사용 CASE) 하나의 단말에서 디버그 빌드와 출시 빌드를 모두 넣어보고자 할 때
- [방법] build types에도 applicationIdSuffix를 사용하여 세그먼트를 추가해준다.
android {
...
buildTypes {
getByName("debug") {
applicationIdSuffix = ".debug"
}
}
}
ㄴ> 샘플의'free debug' 빌드 변형의 애플리케이션 ID는 'com.example.myapp.free.debug'이다.
[ applicationId 명명 방식 ]
(product flavor과 build types 모두 applicationIdSuffix설정한 경우)
<defaultConfig 블록의 applicationId값><해당 product flavor의 applicationIdSuffix 값><해당 빌드 유형의 applicationIdSuffix 값> |
+) Google Play에서 APK를 사용하여 배포하는 기존 앱(2021년 8월 전에 만들어짐)이 있고, 각각 다른 기기 설정(예: API 수준)을 타겟팅하는 다중 APK를 배포하는 데 동일한 앱 등록정보를 사용하려는 경우 각 빌드 변형에는 동일한 애플리케이션 ID를 사용해야 하지만 각 APK에는 다른 versionCode를 부여해야 합니다. AAB를 사용하는 게시는 기본적으로 단일 버전 코드와 애플리케이션 ID를 사용하는 단일 아티팩트를 사용하므로 영향을 받지 않습니다.
flavor dimension (버전 차원)
- product flavors 의 집합이다.
- 모든 버전은 flavor dimension에 속해 있어야하므로 모든 버전을 flavor dimension에 할당해야 한다
- 모듈에 하나의 flavor dimension만 지정한다면 -> (Android Gradle 플러그인은) 자동으로 모든 버전을 동일한 flavor dimension에 할당한다.
[flavor dimension 추가하기]
=> app 단 build.gradle.kts 내 / android{} 블록 내 / flavorDimensions += "추가할 flavor dimension명"으로 추가
=> 여러개 추가 시 'flavorDimensions += listOf("추가할 flavor dimension명1", "추가할 flavor dimension명2")
- Sample1) version'이라는 flavor dimension을 생성 + product flavors 'demo'와 'full'을 추가 + 버전별 자체적인 applicationIdSuffix와 versionNameSuffix를 제공
android {
...
defaultConfig {...}
buildTypes {
getByName("debug"){...}
getByName("release"){...}
}
// Specifies one flavor dimension.
flavorDimensions += "version"
productFlavors {
create("demo") {
// Assigns this product flavor to the "version" flavor dimension.
// If you are using only one dimension, this property is optional,
// and the plugin automatically assigns all the module's flavors to
// that dimension.
dimension = "version"
applicationIdSuffix = ".demo"
versionNameSuffix = "-demo"
}
create("full") {
dimension = "version"
applicationIdSuffix = ".full"
versionNameSuffix = "-full"
}
}
}
- [사용 CASE Sample]
product flavors('full' , 'demo' 제품 버전)을 그룹화하는 flavor dimension인'mode' 을 생성
API 수준을 기반으로 제품 버전을 그룹화하는product flavors들이 속한 flavor dimension인'api' 을 생성할 때 유용하다.
android {
...
buildTypes {
getByName("debug") {...}
getByName("release") {...}
}
// Specifies the flavor dimensions you want to use. The order in which you
// list the dimensions determines their priority, from highest to lowest,
// when Gradle merges variant sources and configurations. You must assign
// each product flavor you configure to one of the flavor dimensions.
flavorDimensions += listOf("api", "mode")
productFlavors {
create("demo") {
// Assigns this product flavor to the "mode" flavor dimension.
dimension = "mode"
...
}
create("full") {
dimension = "mode"
...
}
// Configurations in the "api" product flavors override those in "mode"
// flavors and the defaultConfig block. Gradle determines the priority
// between flavor dimensions based on the order in which they appear next
// to the flavorDimensions property, with the first dimension having a higher
// priority than the second, and so on.
create("minApi24") {
dimension = "api"
minSdk = 24
// To ensure the target device receives the version of the app with
// the highest compatible API level, assign version codes in increasing
// value with API level.
versionCode = 30000 + (android.defaultConfig.versionCode ?: 0)
versionNameSuffix = "-minApi24"
...
}
create("minApi23") {
dimension = "api"
minSdk = 23
versionCode = 20000 + (android.defaultConfig.versionCode ?: 0)
versionNameSuffix = "-minApi23"
...
}
create("minApi21") {
dimension = "api"
minSdk = 21
versionCode = 10000 + (android.defaultConfig.versionCode ?: 0)
versionNameSuffix = "-minApi21"
...
}
}
}
...
ㄴ> Gradle은 다음과 같은 이름 지정 체계로 build variants 총 12개를 생성함
- build variants: [minApi24, minApi23, minApi21][Demo, Full][Debug, Release]
- 해당 APK: app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk
variants필터
- 자동 조합되어 생성되는 build variants 들 중 특정 build variant 을 삭제할 때 beforeVariants 블록을 사용
- ex) 위 Sample에서 'minApi21' 및 'demo' 제품 버전을 조합하는 모든 빌드 변형 구성을 필터링하는 경우
android {
...
buildTypes {...}
flavorDimensions += listOf("api", "mode")
productFlavors {
create("demo") {...}
create("full") {...}
create("minApi24") {...}
create("minApi23") {...}
create("minApi21") {...}
}
}
androidComponents {
beforeVariants { variantBuilder ->
// To check for a certain build type, use variantBuilder.buildType == "<buildType>"
if (variantBuilder.productFlavors.containsAll(listOf("api" to "minApi21", "mode" to "demo"))) {
// Gradle ignores any variants that satisfy the conditions above.
variantBuilder.enable = false
}
}
}
...
다음 포스팅이 존재합니다.
1편 : 현재 포스팅
2편 : https://nemomemo.tistory.com/188
3편 : https://nemomemo.tistory.com/189
[출처]
- [공식] https://developer.android.com/studio/build/build-variants?hl=ko