0. Intro
Gradle 은 빌드도구이다. Java, Kotlin, Python 등 내가 작성한 소스 코드들을 컴파일하고 실행 가능한 파일로 만들어주는 기본적인 빌드 수행을 한다. 하지만 Android 앱 빌드에는 특수한 작업들이 필요하다.
예시는 아래와 같다.
- Android 앱은 `.apk` 또는 `.aab` 형식으로 패키징해야 한다.
- `.class` 파일을 Android의 `Dalvik(ART)` 실행 환경에서 사용할 수 있는 `.dex` 파일로 변환해야 한다.
- res/layout.xml, res/drawable/ 같은 Android 리소스를 빌드 과정에서 최적화해야 한다.
- `AndroidManifest` 에 선언된 앱 정보, 권한, 컴포넌트 설정을 Gradle과 연동해야 한다.
- 빌드 변형 (`Build Variants`) 를 처리하여 Debug, Release, Flavor 등 다양한 버전을 빌드해야 한다.
- `ProGuard/R8` 난독화를 처리하여 코드 최적화 및 보안을 강화해야 한다.
Gradle은 이런 기능을 기본적으로 지원하지 않는다. 때문에 안드로이드 앱을 빌드할 수 있도록 돕는 플러그인이 필요한데 그것이 `Android Gradle Plugin (AGP)` 이다. 이렇듯 Gradle과 AGP 조합은 안드로이드 앱을 쉽게 빌드가능하게 한다.
1. Android Gradle Plugin
AGP는 Android 프로젝트를 생성하면 기본적으로 포함되어 있다. 즉, 개발자가 직접 설치할 필요는 없고 build.gradle 파일에서 버전만 관리하면 된다.
Project Structure에서 Android Gradle Plugin Version을 확인 및 변경할 수도 있고, build.gradle 파일에서도 직접 확인가능하다.
AGP 7.0 이전에서는 프로젝트 수준의 build.gradle 에서 적용했지만, 최신 Gradle 에서는 App 모듈 수준 build.gradle 파일의 plugins { }에서만 설정하면 된다.
//AGP 7.0 이전의 android_proejct/build.gradle.kts
dependencies {
classpath "com.android.tools.build:gradle:8.2.0"
}
//AGP 7.0 이후의 android_proejct/app/build.gradle.kts
plugins {
id 'com.android.application'
...
}
2. AGP와 Gradle의 버전 호환
AGP 버전에 따라 Gradle 버전도 호환되는 것으로 사용해야 한다. 현재 AGP 8.4 기준 Gradle 의 최소 버전은 8.6을 사용해야한다. Gradle버전 또한 Project Structure에서 확인 및 변경할 수 있고, `gradle-wrapper.properties` 에서도 수정 가능하다.
distributionUrl은 Gradle 배포판의 경로를 지정하는 변수이다. gradle-8.6-all 이 Gradle 버전이고, 이 값만 바꾸면 된다.
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
gradle-wrapper는 무엇이고, 안드로이드 프로젝트를 생성할 때 기본적으로 생기는 gradle 관련 파일들을 하나씩 살펴보겠다.
아래부터는 AGP가 아닌 Gradle 자체에서 제공하는 기능이다.
3. gradle-wrapper (gradlew)
Wrapper는 특정 버전의 Gradle을 실행하는 스크립트로, 시스템에 해당 버전의 Gradle이 없다면 자동으로 설치해 준다. 프로젝트를 실행하는 즉시 자동으로 지정된 Gradle 버전을 다운로드하여 적용된다. 개발자가 새로운 환경에서 개발을 시작하더라도, gradle을 설치할 필요 없이 바로 동일한 환경에서 실행할 수 있도록 해준다.
- `gradle-wrapper.jar`
- Gradle이 실행되는 JAR 파일로, gradlew 스크립트는 이 JAR 파일을 통해 실제로 Gradle을 실행한다.
- `gradle-wrapper.properties`
- Gradle 버전을 설정하는 파일이다.
- 이 파일에서 설정된 Gradle 버전으로 gradlew가 자동으로 Gradle을 다운로드하고 실행하게 된다.
그리고 OS 환경에 맞춰 gradle을 손쉽게 실행할 수 있게 해주는 배치 스크립트 `gradlew(Linux/Mac)`와 `gradlew.bat(Windows)` 까지 생성되어 있다.
Gradle의 버전을 바꿔야하는 상황에서, 스튜디오의 Project Structure에서 팀원들간 다같이 바꾸도록 약속하는 것이 좋을까? Gradle Wrapper을 사용하는 것이 좋을까? 팀원마다 수동으로 Gradle 버전을 바꾸는 것은 비효율적이고 오류를 유발한 가능성이 높다.
Gradle Wrapper을 사용하면, 프로젝트를 실행하는 즉시 자동으로 지정된 Gradle 버전을 다운로드하여 적용되므로 실수할 가능성이 없다.
또한 Project Structure에서 변경하는 방식은 로컬 환경에만 적용되고, gradlew는 모든 환경에서 동일하게 동작한다. gradlew은 CI/CD 서버나 다른 팀원의 컴퓨터에서도 gradlew 실행시, 해당 프로젝트에 맞는 동일한 Gradle 버전이 자동으로 다운로드 된다. OS와도 무관하게 같은 버전의 Gradle을 다운로드해서 사용 가능하다.
터미널에서 ./gradlew 명령어를 입력하면, gradle에 대한 설정이나 설치를 진행한 적이 없음에도 빌드가 잘 돌아간다. 바로 이 gradle wrapper 덕분이다.
4. gradle.properties
Gradle 빌드 설정과 관련된 여러가지 옵션을 제공한다
사용자의 홈 디렉토리에 위치시켜 사용자가 실행하는 모든 Gradle 빌드에 적용할 수 있지만, 여기선 프로젝트 내 루트 디렉토리에 위치하는 것만 다룬다.
gradle.properties에서 설정할 수 있는 대표적인 속성들은 아래와 같다.
- JVM 설정: org.gradle.jvmargs
- 빌드 캐시: org.gradle.caching
- Gradle 데몬 활성화: org.gradle.daemon
- 병렬 빌드: org.gradle.parallel
- 안드로이드 설정: android.useAndroidX, android.enableJetifier
- 로그 설정: org.gradle.logging.level
아래는 프로젝트 셋팅시 기본으로 지정되어있는 설정들이다.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 //Gradle 빌드를 실행하는 JVM의 설정을 제어
android.useAndroidX=true
kotlin.code.style=official
android.nonTransitiveRClass=true
- `org.gradle.jvmargs`
- -Xmx2048m
- JVM의 최대 힙 메모리 크기를 설정하는 옵션
- 2048m은 2GB로, Gradle 빌드를 실행할 때 사용되는 최대 메모리 크기를 지정한다.
- 빌드 시 큰 메모리 요구가 있을 수 있기 때문에, 메모리 크기를 늘려서 빌드를 더 빠르게 하거나 메모리 부족 문제를 방지할 수 있다.
- -Dfile.encoding=UTF-8
- 파일 인코딩을 UTF-8로 설정함으로써, 프로젝트에서 다루는 모든 파일이 UTF-8 인코딩으로 읽고 쓰여지도록 한다.
- 이는 다국어 환경에서 유용하고, 인코딩 문제를 방지하기 위해 사용된다.
- -Xmx2048m
- `useAndroidX`
- AndroidX 라이브러리를 사용하도록 설정하는 옵션
- AndroidX는 구글이 Android 지원 라이브러리를 더 이상 업데이트하지 않고, 새로운 라이브러리를 AndroidX라는 이름으로 출시하면서 제공된 라이브러리의 집합이다.
- 이 설정이 true로 되어 있으면, AndroidX 라이브러리를 사용하고, 구 Android Support Library는 더 이상 사용되지 않는다. 예를 들어, `android.support` 패키지 대신 `androidx` 패키지가 사용된다.
- AndroidX 라이브러리를 사용하도록 설정하는 옵션
- `kotlin.code.style`
- 코틀린 코드 스타일을 지정하는 옵션
- gradle은 코드를 정렬하는 기능은 하지 않기 때문에, 이 옵션은 Intellij IDE에서 사용하는 옵션이다.
- `android.nonTransitiveRClass`
- R 클래스의 범위(scope) 관리를 설정하는 옵션
- R 클래스는 Android 앱에서 리소스 파일들 (e.g strings.xml, drawables.xml)에 대한 참조를 포함하는 자동 생성된 클래스
- android.nonTransitiveRClass=true 설정을 활성화하면, 각 모듈은 자신만의 R 클래스를 갖게 되며, 다른 모듈에서 정의된 리소스를 직접 참조할 수 없다.
- 이 설정은 모듈화된 프로젝트에서 리소스 간 충돌을 방지하거나, 리소스의 불필요한 노출을 막을 때 유용하다. 이 옵션을 사용하면 APK 빌드시 용량이 크게 줄어든다.
5. settings.gradle
프로젝트 전체에 적용되는 설정을 정의한다
gradle.properties에서 설정할 수 있는 대표적인 속성들은 아래와 같다.
- Gradle이 어떤 프로젝트를 포함할지 지정하는 역할을 한다.
- 프로젝트의 이름을 지정할 수 있다. 기본적으로는 디렉토리 이름이 프로젝트 이름으로 사용되지만, settings.gradle 파일에서 명시적으로 지정할 수 있다.
- 멀티 프로젝트 빌드에서는 여러 하위 모듈(서브 프로젝트)을 하나의 루트 프로젝트 아래에서 관리한다. settings.gradle에서 어떤 모듈들이 포함될지 정의할 수 있다.
- 외부 Gradle 플러그인을 추가하거나, 플러그인 관리용 설정을 할 수 있다.
pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "sample-android"
include(":app")
`pluginManagement` 만 살펴본다.
pluginManagement 블록은 Gradle 플러그인의 설정과 저장소 관리를 정의하는 영역이다. 여기서는 프로젝트에서 사용할 플러그인들이 저장될 리포지토리를 설정하고, 그 안에서 어떤 그룹의 플러그인들을 포함할지 지정한다.
repositoreis {} 내부
이 블록은 플러그인을 찾을 리포지토리들을 설정한다.
- `google {}`
- google() 저장소는 Google의 Maven 리포지토리로, Android와 관련된 플러그인과 라이브러리가 포함된다.
- content {} 블록을 사용하여, Google 저장소에서 특정 그룹에 속하는 플러그인만 포함시키도록 설정한다. 이는 특정 그룹의 플러그인들만 Gradle에서 사용할 수 있게 하기 위한 필터링 기능이다.
- includeGroupByRegex("com\\.android.*")
com.android로 시작하는 모든 플러그인 그룹을 포함한다. (e.g com.android.application, com.android.library) - includeGroupByRegex("com\\.google.*")
com.google로 시작하는 모든 플러그인 그룹을 포함한다. Google의 일부 플러그인들이 해당된다. - includeGroupByRegex("androidx.*")
androidx로 시작하는 모든 플러그인 그룹을 포함한다. AndroidX 관련 라이브러리 및 플러그인들이 이에 해당된다.
- includeGroupByRegex("com\\.android.*")
- `mavenCentral()`
- Maven Central은 공식 Maven 리포지토리로, 다양한 라이브러리와 플러그인들이 제공된다.
- `gradlePluginPortal()`
- Gradle Plugin Portal은 Gradle 플러그인 전용 저장소로, Gradle에서 제공하는 공식 플러그인들이 포함되어 있다.
- Gradle Plugin Portal은 Gradle 플러그인 전용 저장소로, Gradle에서 제공하는 공식 플러그인들이 포함되어 있다.
6. build.gradle (root)
build.gradle(루트 프로젝트의 build.gradle) 파일은 Gradle 빌드 시스템에서 프로젝트 수준의 설정을 담당하는 파일이다. 이 파일은
루트 프로젝트의 빌드를 정의하고, 하위 모듈들에 대한 공통 설정을 적용하는 역할을 한다.
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.jetbrains.kotlin.android) apply false
alias(libs.plugins.android.library) apply false
}
- apply false는 해당 플러그인을 자동으로 적용하지 않도록 설정한다.
- 즉, 플러그인을 선언만 하고, 나중에 필요한 모듈에서 개별적으로 적용할 수 있도록 한다. 실제 기본 프로젝트 셋팅을 할 때 App 모듈의 build.gradle에서 개별적으로 적용되어있는 것을 볼 수 있다.
- 대부분의 경우, 의존성은 dependencies {} 블록에 추가하면 되지만, 어떤 라이브러리들은 플러그인을 통해서만 사용해야 하는 경우가 있다.
- 예를 들어, Android Gradle Plugin (AGP), Kotlin Gradle Plugin 등은 그냥 의존성만 추가하면 동작하지 않고, plugins {} 블록에 플러그인을 추가해야 프로젝트가 제대로 빌드된다.
- task를 선언할 수 있다. task는 gradle의 작업 단위인데, 특정한 동작을 수행하기 위해 만들어두는 일종의 명령어이다. 터미널에 ./gradlew task 명령어를 입력해보면, 기본으로 내장되어 있는 task의 목록이 나온다.
7. build.gradle (app)
마지막은 app 모듈에 적용되는 build.gradle 파일이다. 여기서 선언된 내용은 app 모듈에만 적용된다.
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.example.sample"
minSdk 24
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
...//
}
buildTypes 와 productFlavors만 살펴본다. compileOptions와 kotlinOptions에 대해서는 이전 블로그에서 다뤘으며, compileSdk, targetSdk, minSdk 은 다음 블로그에서 정리해보도록 한다.
`buildTypes`는 주로 debug (개발용) 빌드와 release (배포용) 빌드를 구분할 때 사용된다. buildTypes 내에서 각 빌드의 구체적인 동작을 제어할 수 있다. 예를 들어 release로 빌드하면 난독화를 시키고 APK 용량을 축소시키는 옵션이 적용된다. debug와 release 두 가지 기본 빌드 타입이 기본으로 제공되고 이 외에도 개발자가 빌드 타입을 추가적으로 정의할 수 있다.
또 `productFlavors` 별로 사용할 변수를 지정할 수 있다. 가장 대표적인 사용 예시는 flavor 별로 서버 주소를 테스트 서버/실서버로 다르게 적용하는 것이다. 각 flavor마다 다른 앱 ID를 설정하여 두 버전이 구분될 수 있도록 해주고 버전 이름에 접미사를 추가할 수도 있다. 나같은 경우 인앱 결제 테스트 빌드를 구별하기 위해 flavor을 추가했는데 이렇게 productFlavors가 설정되면, buildTypes인 debug 및 release 와 결합하여 총 4가지의 빌드 구성을 만들 수 있다. (freeDebug, paidDebug, freeRelease, paidRelease)
Reference
'🐸 Android' 카테고리의 다른 글
[Gradle] Java Versions in Android Builds (0) | 2025.01.30 |
---|---|
[Android] Convention Plugin (0) | 2025.01.30 |
FCM Notification 2 - background 에서 푸쉬 알람받기 (0) | 2023.12.12 |
FCM Notification 1 - 안드로이드 13에서 Notification 권한 허가 변경 (0) | 2023.12.12 |
[Android] 렌더링 관점에서 효율적인 ListAdapter (1) | 2023.09.07 |