Android 개발에서 Build Variant는 build type과 product flavor의 조합으로, APK 또는 AAB가 어떤 매개변수, 리소스 및 코드로 빌드될지 결정합니다. 각 빌드 변형은 고유한 applicationId, 서명 키 및 포함된 종속성을 가진 별도의 Gradle 구성을 나타냅니다. Google Android Developers, 2025에 따르면, Build Variants를 올바르게 구성하면 각 변형에 불필요한 리소스를 제외하여 빌드 시간을 최대 40%까지 단축할 수 있습니다. 빌드 변형 시스템은 최신 Android 프로젝트에서 구성 관리의 기초입니다.
핵심 요점
Build Variant는 하나의 Build Type과 하나의 Product Flavor를 결합한 결과입니다. 프로젝트에 Product Flavors가 정의되지 않은 경우 Build Variant는 Build Type과 일치합니다. Gradle은 모든 FlavorDimensions, Product Flavors 및 Build Types의 데카르트 곱으로 전체 변형 세트를 자동 생성합니다. 예를 들어, free/paid flavor와 debug/release type의 경우 freeDebug, freeRelease, paidDebug, paidRelease의 4가지 변형이 생성됩니다.
각 Build Variant는 <Flavor><Type> 형식의 고유한 이름을 가지며 flavor는 대문자로 시작합니다. Gradle은 이 변형에 대해 별도의 작업(assembleFreeDebug, installFreeDebug, bundleFreeRelease)을 생성합니다. Android Studio에서는 Build Variants 패널(View → Tool Windows → Build Variants)을 통해 변형 간 전환이 가능합니다. 변형을 선택하면 컴파일할 코드, 포함할 리소스 및 생성할 APK/AAB가 결정됩니다.
Build Variants 시스템은 세 가지 주요 작업을 해결합니다: 다양한 환경(dev/staging/production)에 대한 구성 분리, 여러 앱 버전(free/paid) 생성, 빌드 A/B 테스트. Build Variants가 없으면 개발자가 수동으로 플래그와 구성을 전환해야 하므로 인적 오류가 발생합니다. Gradle Inc., 2024의 연구에 따르면, Build Variants를 도입하면 세 개 이상의 배포 환경이 있는 프로젝트에서 빌드 오류가 60% 감소합니다.
AGP(Android Gradle Plugin)는 구성 단계에서 모든 조합을 계산합니다. 프로젝트에 각각 2개와 3개의 flavor가 있는 두 개의 차원이 있는 경우, Gradle은 2 × 2 × 3 = 12개의 조합을 생성하고 여기에 Build Types 수(보통 2)를 곱합니다. 각 조합은 고유한 이름과 작업 세트를 받습니다. AGP는 각 변형에 대해 source set을 자동 추가합니다: src/freeDebug/, src/paidRelease/, 그리고 일반화된 src/free/ 및 src/debug/. 리소스 읽기 우선순위: variant → flavor → type → main.
// 예: 4개의 Build Variants
// flavorDimensions "version", "server"
// version: demo, prod
// server: mock, live
// buildTypes: debug, release
// 합계: 2 × 2 × 2 = 8개 변형
android {
flavorDimensions "version", "server"
productFlavors {
demo { dimension "version" }
prod { dimension "version" }
mock { dimension "server" }
live { dimension "server" }
}
}
Build Type은 애플리케이션을 어떻게 빌드할지(디버그 정보 포함 여부, 최적화 여부, 서명 방식)를 정의합니다. Product Flavor는 무엇을 빌드할지(제품 버전)를 정의합니다. Build Type은 빌드 메커니즘(debug, release, staging)입니다. Product Flavor는 제품 변형(free, paid, enterprise, demo)입니다. 두 개념은 직교하여 모든 Build Type을 모든 Product Flavor에 적용할 수 있습니다.
기본 Build Types에는 debug(debuggable=true, minification=false, signing=debug.keystore)와 release(debuggable=false, minification=true, signing=production.keystore)가 포함됩니다. 기본 Product Flavor는 하나이며 이름이 없습니다(사실상 main source set). 개발자는 고유한 Build Types(예: debuggable=true 및 minification=true인 “staging”)와任意の数のProduct Flavors를 추가할 수 있습니다. 또 다른 차이점은 Build Types는 차원으로 그룹화할 수 없지만 Product Flavors는 가능하다는 것입니다.
주요 실무적 차이점: build.gradle의 defaultConfig는 모든 Variants에 적용되지만 productFlavors 및 buildTypes에서 재정의할 수 있습니다. buildType에 추가된 BuildConfigField는 해당 유형의 모든 flavor에 표시되고, productFlavor에 추가된 것은 해당 flavor의 모든 유형에 표시됩니다. 두 곳 모두에서 필드가 정의된 경우 buildType이 우선합니다(체인에서 마지막에 적용됨).
| 특성 | Build Type | Product Flavor |
|---|---|---|
| 목적 | 빌드 방법 | 빌드 대상 |
| 예시 | debug, release, staging | free, paid, demo, enterprise |
| 기본값 | debug + release | 하나 (main) |
| Source Set | src/debug/, src/release/ | src/free/, src/paid/ |
| 차원 | 없음 | flavorDimensions |
| 적용 순서 | flavor 후, 재정의 | defaultConfig 후 |
| BuildConfigField | flavor 재정의 | defaultConfig 재정의 |
Build Variants 구성은 모듈 수준 build.gradle 파일의 android 블록에서 수행됩니다. 먼저 buildTypes를 매개변수와 함께 선언한 다음 flavorDimensions 및 productFlavors를 선언합니다. Gradle은 이러한 선언을 기반으로 변형을 자동 생성합니다. 각 변형은 모듈의 defaultConfig를 상속받아 지정된 필드를 재정의합니다. 선언 순서는 우선순위에 영향을 미칩니다: buildTypes는 productFlavors 이후에 적용됩니다.
Gradle 스크립트에서 특정 Build Variant에 액세스하려면 android.applicationVariants(앱 모듈) 또는 android.libraryVariants(라이브러리 모듈)를 사용합니다. 이는 구성 런타임 시 각 변형의 구성을 수정하기 위해 반복할 수 있는 컬렉션입니다. 예를 들어, “demo”라는 단어를 포함하는 모든 변형에 프로그래밍 방식으로 buildConfigField를 추가할 수 있습니다.
Android Gradle Plugin 8.x는 람다를 통해 변형을 구성하는 더 깔끔한 API인 onVariants에 대한 지원을 추가했습니다. 이전 API(variantOutput, variantFilter)는 더 이상 사용되지 않음으로 표시되었습니다. 라이브러리 모듈의 경우 onEach와 함께 onVariants를 사용하는 것이 좋습니다. variantOutput에서 onVariants로 마이그레이션하는 것은 AGP를 7.x에서 8.x로 업그레이드할 때 권장되는 단계입니다.
android {
buildTypes {
debug {
debuggable true
minification false
signingConfig signingConfigs.debug
}
release {
debuggable false
minification true
proguardFiles "proguard-rules.pro"
signingConfig signingConfigs.release
}
staging {
debuggable true
minification true
versionNameSuffix "-staging"
}
}
flavorDimensions "tier", "region"
productFlavors {
free { dimension "tier" }
paid { dimension "tier" }
us { dimension "region" }
eu { dimension "region" }
}
}
android.onVariants { variant ->
if (variant.name.contains("Demo")) {
variant.setEnabled(false)
}
}
각 Build Variant는 고유한 source sets 계층(소스 코드, 리소스 및 매니페스트가 포함된 디렉터리)을 받습니다. source set은 src/<variantName>/(예: src/freeDebug/)에 위치하며 java/, res/, AndroidManifest.xml, assets/를 포함할 수 있습니다. 변형의 source set에 파일이 존재하면 기본 source set(src/main/)의 동일한 이름의 파일을 재정의합니다. 리소스의 경우 교체가 아닌 병합이 발생합니다. 시스템은 모든 활성 source sets의 리소스를 병합하여 변형별 리소스에 우선순위를 부여합니다.
Build Variant에 대한 source sets는 체인으로 구축됩니다: src/main/ → src/flavor/ → src/type/ → src/flavorType/. 예를 들어, paidRelease의 경우 먼저 main, 그다음 paid, 그다음 release, 마지막으로 paidRelease가 적용됩니다. 각 후속 source set는 이전 것을 재정의합니다. 즉, src/release/res/values/strings.xml은 src/paid/의 동일한 문자열을 재정의하지만, src/paidRelease/res/는 더 높은 우선순위를 갖습니다.
변형에 source sets를 사용하는 것은 리소스를 사용자 지정하는 권장 방법입니다. 코드에서 BuildConfig.FLAVOR를 확인하고 로직을 분기하는 대신, 다른 source sets에 다른 파일을 배치하기만 하면 됩니다. 예를 들어, 무료 및 유료 버전의 아이콘은 각각 src/free/res/ 및 src/paid/res/에 배치하고, 다른 권한이 있는 AndroidManifest는 src/free/AndroidManifest.xml 및 src/paid/AndroidManifest.xml에 배치합니다. 이는 더 깔끔하고 빠르며(리소스는 컴파일 타임에 처리, 런타임 확인 불필요) 안전합니다(코드 버그로 인해 무료 버전에 유료 기능이 실수로 포함되는 것을 방지).
멀티 모듈 프로젝트에서는 각 모듈(라이브러리)이 고유한 Build Variants를 가질 수 있습니다. AGP는 변형을 자동 동기화합니다: 앱 모듈이 paidRelease를 빌드하면 모든 종속 라이브러리도 paidRelease에 해당하는 변형으로 빌드됩니다. 라이브러리에 product flavors가 없지만 앱 모듈에 있는 경우 문제가 발생합니다. 이 경우 라이브러리는 한 번(유형에 따라 release 또는 debug) 빌드됩니다.
라이브러리 모듈의 경우 Build Variant는 기본적으로 앱 모듈의 Build Type과 일치합니다(라이브러리에는 product flavors가 없기 때문). 라이브러리가 앱 모듈의 flavor에 적응해야 하는 경우 라이브러리에서 동일한 flavorDimensions 및 productFlavors를 선언해야 합니다. AGP는 정확한 이름 일치로 flavor를 매칭합니다. Gradle은 루트 프로젝트에서 subprojects 또는 Convention Plugins를 사용한 빌드 구성을 통한 flavor 동기화를 권장합니다.
AGP 8.1부터 라이브러리는 multiple variants를 게시할 수 있습니다. 즉, 모든 라이브러리 변형을 maven 저장소에 동시에 게시할 수 있습니다. 이는 앱 모듈이 유료 flavor를 사용하지만 라이브러리가 무료 버전으로만 게시된 경우의 문제를 해결합니다. Multiple variants publishing(MVP)을 통해 종속 프로젝트가 필요한 변형을 자동으로 선택할 수 있습니다. MVP를 활성화하려면 라이브러리의 build.gradle에 publishing { multipleVariants { ... } }을 추가합니다.
때로는 일부 Build Variants를 비활성화해야 할 필요가 있습니다. 예를 들어, mockRelease 조합이 의미가 없는 경우(목 서버가 프로덕션에 배포되어서는 안 됨)입니다. Gradle은 variantFilter를 제공합니다. 각 변형의 속성을 확인하고 setIgnore(true)로 비활성화할 수 있는 DSL 블록입니다. VariantFilter는 작업 생성 전 구성 단계에서 적용되므로 비활성화된 변형은 assemble 및 install 작업을 생성하지 않습니다.
필터링은 빌드 속도를 높이는 데도 유용합니다. 프로젝트에 8개의 변형이 있지만 개발자가 하나만 작업하는 경우 나머지 7개 변형도 구성을 거칩니다. variantFilter를 사용하면 비활성화된 변형이 작업을 생성하지 않으므로 6개 이상의 flavor 차원이 있는 프로젝트의 구성 시간이 30-50% 단축됩니다. CI/CD에서는 명령줄 매개변수 -PbuildOnly=paidRelease를 통해 변형을 동적으로 필터링할 수 있습니다.
android {
variantFilter { variant ->
// release용 mock과 production용 demo 비활성화
def names = variant.flavors*.name
def isMock = names.contains("mock")
def isDemo = names.contains("demo")
def isRelease = variant.buildType.name == "release"
if ((isMock && isRelease) || (isDemo && !isMock)) {
variant.setIgnore(true)
}
}
}
// 매개변수를 통한 동적 필터링
if (project.hasProperty("buildOnly")) {
def target = project.property("buildOnly")
android.variantFilter { variant ->
variant.setIgnore(variant.name != target)
}
}
자주 묻는 질문
제한은 없지만 Gradle은 모든 flavor와 유형의 데카르트 곱을 생성합니다. 3개의 차원에 각각 3개의 flavor와 3개의 build type이 있으면 27개의 변형이 생성됩니다. 너무 많은 변형은 구성을 느리게 합니다. 하나의 모듈에서는 10-12개 이하의 변형을 권장합니다.
flavorDimensions는 Product Flavors를 독립적인 축으로 그룹화합니다. 예를 들어, “tier” 차원(free, paid)과 “region” 차원(us, eu)입니다. 차원이 없으면 모든 flavor가 하나의 축에 속하며, Gradle은 전체에서 하나의 flavor만 선택합니다(free+us와 paid+eu를 별도 변형으로 가질 수 없음).
productFlavor 또는 buildType 블록에서 applicationId를 지정합니다. 예를 들어, 무료 버전의 경우: free { applicationId “com.example.app.free” }. 매니페스트에서 ${applicationId}를 사용하면 Gradle이 자동으로 값을 대체합니다. 이렇게 하면 두 변형을 모두 하나의 기기에 설치할 수 있습니다.
iOS에서 Build Variants에 해당하는 것은 Scheme + Configuration의 조합입니다. Xcode Schemes는 다양한 매개변수로 Debug/Release 구성을 통해 설정됩니다. 여러 버전(free/paid)의 경우 Build Configurations 및 Preprocessor Macros가 사용됩니다. Android에서는 개념이 더 공식화되어 Gradle에 내장되어 있습니다.
네, 각 변형마다 APK 크기가 다를 수 있습니다. Debug 빌드에는 디버그 정보, SDK 및 지원되지 않는 리소스가 포함됩니다. minification 및 resource shrinking이 적용된 Release 빌드는 최소 크기를 생성합니다. Product Flavor도 크기에 영향을 미칩니다: 유료 라이브러리가 없는 무료 버전은 해당 라이브러리 크기만큼 유료 버전보다 작습니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.