Developing Myself Everyday
article thumbnail
Published 2023. 9. 27. 12:09
Gradle의 동작원리 이해하기 Android

 

안드로이드 개발을 한다면 이 코끼리를 지겹게 보게 됩니다. 다만 이 코끼리에 대해서 자세히 공부해보고 알아본적이 없어서 이번 게시글을 통해 Gradle의 원리를 이해해보고자 합니다.


 

 

Gradle이란?


GradleGroovy 언어를 기반으로 한 오픈소스 빌드 도구입니다. 

 


빌드 도구란 애플리케이션 생성을 자동화 하기 위한 프로그램으로 안드로이드에서는 .apk 파일을 만드는 과정을 자동화합니다.

 

 

Gradle이 나오기 전에는 AntMaven 같은 빌드 도구를 사용했습니다. 다만 xml 형식을 이용해 정적인 설정정보를 구성했기에 구조적인 단점이 존재했습니다. Gradle Groovy 언어를 이용해 코드로서 설정 정보를 구성하기 때문에 이러한 단점을 해결했습니다.

 

안드로이드에서 GradleGradle Script라는 영역에서 관리됩니다. 이제부터 이 파일들을 하나하나 알아보도록 하겠습니다.

 

 

 

Build Lifecycle에 따른 역할


Gradle의 파일을 알아보기 전에 빌드가 어떻게 진행되는지 알아봐야 합니다.

 

 

Gradle은 아래의 3개의 단계로 빌드가 진행됩니다. 

  1. 초기화(Initialization) : 빌드 대상 프로젝트를 결정하고 각각에 대한 Project 객체를 생성. settings.gradle 파일에서 프로젝트 구성 (멀티프로젝트, 싱글프로젝트 구분)
  2. 구성(Configuration) : 빌드 대상이 되는 모든 프로젝트의 빌드 스크립트를 실행. (프로젝트 객체 구성) configured Task 실행
  3. 실행(Execution) : 구성 단계에서 생성하고 설정된 프로젝트의 태스크 중에 실행 대상 결정. gradle 명령행에서 지정한 태스크 이름 인자와 현재 디렉토리를 기반으로 태스크를 결정하여 선택된 Task들을 실행

 

 

초기화(Initialization) 단계 - `settings.gradle` 파일

초기화 단계에서는 빌드를 진행할 프로젝트를 결정하고 각각에 대한 프로젝트 인스턴스 객체를 생성합니다. 이 과정은 `settings.gradle` 파일에서 진행됩니다.

 

 

 `settings.gradle` 파일은 루트 프로젝트 디렉터리에 있으며, 프로젝트 수준 저장소 설정을 정의하고, 앱을 빌드할 때 포함해야 하는 모듈을 Gradle에 알려줍니다.

pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "My Application"
include(":app")

 

 

블록에 대해 좀 더 자세하게 알아 보겠습니다.

 

pluginManagement {repositories {...}} 블록

pluginManagement {repositories {...}} 블록Gradle 플러그인 및 그들의 종속 항목을 검색하거나 다운로드하는 데 Gradle이 사용하는 저장소를 구성합니다. Gradle은 JCenter, Maven Central 및 Ivy와 같은 원격 저장소를 사전에 구성할 수 있게 지원을 제공합니다. 또한 로컬 저장소를 사용하거나 사용자 정의 원격 저장소를 정의할 수도 있습니다.

 

아래의 코드는 Gradle이 종속성을 찾기 위해 사용해야 하는 저장소로 Gradle 플러그인 포털, Google의 Maven 저장소 Maven Central 저장소를 정의합니다.

pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

 

dependencyResolutionManagement { repositories {...}} 블록

dependencyResolutionManagement { repositories {...}} 블록프로젝트의 모든 모듈에서 공통적으로 사용되는 저장소 및 종속 항목을 구성하는 곳입니다. 이는 애플리케이션을 만들 때 사용하는 라이브러리와 같은 모든 모듈에 적용됩니다.

 

repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 이 부분은 종속성 해결 관리를 위한 설정을 정의합니다. repositoriesModeFAIL_ON_PROJECT_REPOS로 설정하여, 프로젝트 수준 저장소가 사용 중지되고 프로젝트 수준 저장소에 종속성을 정의할 경우 오류가 발생하도록 설정합니다.

 

Android Studio는 기본적으로 Google의 Maven 저장소와 Maven Central 저장소를 포함하지만 (어떤 템플릿을 선택하지 않는 한) 종속 항목을 구성하지 않습니다.

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

 

모듈 추가 블록

마지막은 루트 프로젝트의 이름을 설정하고, 현재 프로젝트에 모듈을 포함시키는 부분입니다. 만약 멀티 모듈 프로젝트라면 아래와 같이 여러 개의 모듈을 추가해야 합니다.

rootProject.name = "My Application"
include(
    ":app",

    ":core:data",
    ":core:database",
    ":core:designsystem",
    ":core:domain",
    ":core:model",

    ":feature:main",
    ":feature:login",
    ":feature:signin"
)

 

 

 

초기화(Initialization) 단계 - `build.gradle` 파일

`build.gradle` 파일은 루트 프로젝트 수준과 각각의 모듈 수준에 있습니다. 모듈 수준에 있는 `build.gradle` 파일은 구성 단계에서 실행되기에 나중에 다루기로 하고 루트 프로젝트 수준의 파일에 대해 이야기 해보겠습니다.

 

루트 프로젝트 수준의 `build.gradle` 파일은 공통적으로 사용되는 종속 항목을 정의합니다.

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    id("com.android.application") version "8.1.1" apply false
    id("org.jetbrains.kotlin.android") version "1.8.10" apply false
}

 

 

 

구성(Configuration) 단계

초기화 단계의 구체적인 동작은 `gradle/init/` 경로의 `init.gradle + *.gradle` 파일을 읽어들여 Gradle 인스턴스를 생성한 다음 프로젝트의 `settings.gradle` 파일로 Settings 인스턴스를 생성하는 방식으로 이뤄집니다.

 

이렇게 설정과 빌드할 대상 프로젝트가 결정되면 구성 단계로 넘어갑니다.

 

구성 단계에서는 현재 빌드할 대상 프로젝트를 위한 설정을 적용합니다. `build.gradle` 파일을 바탕으로, Android Gradle Plugin(AGP)를 통해 초기화 단계에서 생성된 Project 인스턴스들에 빌드 설정을 적용합니다. 

 

`build.gradle` 파일

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
}

android {
    namespace = "com.example.myapplication"
    compileSdk = 33

    defaultConfig {
        applicationId = "com.example.myapplication"
        minSdk = 31
        targetSdk = 33
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary = true
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = 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"
    }
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.4.3"
    }
    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}

dependencies {

    implementation("androidx.core:core-ktx:1.9.0")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
    implementation("androidx.activity:activity-compose:1.7.0")
    implementation(platform("androidx.compose:compose-bom:2023.03.00"))
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.ui:ui-graphics")
    implementation("androidx.compose.ui:ui-tooling-preview")
    implementation("androidx.compose.material3:material3")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    debugImplementation("androidx.compose.ui:ui-tooling")
    debugImplementation("androidx.compose.ui:ui-test-manifest")
}

 

 

각 블록에 살펴보기에 앞서 `build.gradle` 파일에 대해 좀 더 자세히 알아보겠습니다.

 

`build.gradle` 파일은 파일 자체가 Project 객체로 우리가 작성하는 각 블록이 Project 객체의 메서드가 됩니다.


{} 부분은 우리가 람다를 사용할 때와 같이 메서드의 인자로 받아들여 집니다.

 

 

 

plugins { } 블록

빌드 구성에서 첫 번째 단계는 Android Gradle Plugin을 빌드에 적용하는 것입니다. 이를 위해서는 plugins { } 블록에 원하는 Plugin을 적용합니다.

 

여기서는 Android 애플리케이션 빌드를 위한 Android Plugin과 Kotlin 언어를 사용하기 위한 Kotlin Plugin이 적용되어 있습니다.

 

android { } 블록

android { } 블록에서는 안드로이드 애플리케이션 빌드 관련 설정을 정의합니다.

  • namespace: Android 애플리케이션의 패키지 이름을 정의합니다.
  • compileSdk: 컴파일에 사용할 Android API 버전을 지정합니다.
  • defaultConfig { ... }: 기본 설정을 정의하는 블록으로, 애플리케이션의 기본적인 속성을 설정합니다. 예를 들어, 애플리케이션 ID, 최소 SDK 버전, 대상 SDK 버전, 버전 코드 및 버전 이름을 여기에서 설정합니다.
  • buildTypes { ... }: 빌드 유형을 정의하는 블록으로, 여기에서는 릴리스 빌드에 대한 설정을 지정합니다. 예를 들어, 코드 축소화(minifyEnabled) 설정 및 ProGuard 규칙 파일을 여기에서 정의합니다.
  • compileOptions { ... }: 컴파일 옵션을 정의하는 블록으로, Java 소스 및 대상 호환성을 설정합니다.
  • kotlinOptions { ... }: Kotlin 옵션을 정의하는 블록으로, JVM 대상 버전을 설정합니다.
  • buildFeatures { ... }: 빌드 기능을 활성화 또는 비활성화하는 블록으로, 여기에서 Compose를 활성화합니다.
  • composeOptions { ... }: Compose 관련 옵션을 정의하는 블록으로, Compose의 Kotlin 컴파일러 확장 버전을 설정합니다.
  • packaging { ... }: 패키징 옵션을 정의하는 블록으로, 여기에서 리소스를 제외하도록 설정합니다.

 

dependencies { } 블록

dependencies { } 블록에서는 모듈 자체를 빌드하는 데 필요한 종속 항목을 정의합니다. 이곳에서 외부 라이브러리를 사용하기 위해 종속 항목을 정의합니다.

 

 

 

실행(Execution) 단계

실행 단계는 구성 단계에서 생성하고 설정된 프로젝트의 Task 중에 실행 대상을 결정합니다.

 

 

 

 

나머지 파일들


  1. gradle-wrapper.properties (Gradle Version): gradle 자체와 관련된 설정 파일
  2. proguard-rules.pro (ProGuard Rules for 프로젝트명.app): 코드 난독화 도구(ProGuard) 설정 파일
  3. gradle.properties (Project Properties): 프로젝트 수준의 그레이들 환경 설정 파일
  4. local.properties (SDK Location): 안드로이드 SDK 경로를 관리하는 파일

 

 

 

마치며


 

이렇게 해서 Gradle에 대해 좀 더 친숙해질 수 있는 시간을 가져봤습니다. 이제 어느 부분에서 오류가 발생한다면 빠르게 찾아갈 수 있게 되길 바라면서 글을 마무리하겠습니다.

 

 

 

Reference

 

egovframework:dev3.6:dep:build_tool:gradle [eGovFrame]

Gradle은 Groovy를 기반으로 한 오픈소스 빌드 도구이다. Ant의 자유도와 Maven의 관례를 통한 접근성을 바탕으로 이전 빌드툴의 단점을 보완하여 개선된 서비스를 제공한다. Ant처럼 매우 유연한 범용

www.egovframe.go.kr

 

안드로이드 개발자라면 알아야 하는 Gradle 원리

안드로이드 개발을 하면 한번쯤은 Gradle 문제로 삽질을 하는 경우가 다들 있으실 겁니다. 하지만 항상 Clean Project, Rebuild Project, Invalidate Cache & Restart 를 하며 넘어가죠. 오늘은 왜 우리가…

medium.com

 

Gradle

build.gradle의 동작원리 정리

wikidocs.net

 

profile

Developing Myself Everyday

@배준형

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!