[Android] Gradle의 빌드 과정 원리

이황근·2024년 8월 14일

Android

목록 보기
9/9

https://medium.com/@renovatio0424/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%9D%BC%EB%A9%B4-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%98%EB%8A%94-gradle-%EC%9B%90%EB%A6%AC-c39a3299ba6d

위의 글을 읽으면서 내용을 정리하였습니다.

Gradle이란

Gradle은 소프트웨어를 빌드할 수 있는 오픈소스 빌드 자동화 툴입니다.
빌드란 : 소스코드 -> 컴파일 -> 실행파일 (exe, apk) 이 과정을 얘기합니다.

Gradle은 build.gradlesettings.gradle 파일을 통해 빌드를 할 수 있도록 되어 있습니다.

Gradle에서의 Task

Gradle은 Task 를 기반으로 빌드를 진행합니다.
Task란 build.gradle 파일에 정의된 가장 작은 작업을 의미합니다.

위 사진은 여러 Task 간의 종속성을 그린 예시 그림입니다.
여기서 Task B의 코드에 변경 사항이 생겼다면 Task B만 빌드하면 됩니다. 이거를 Task Dependency Mechanism이라고 합니다.
그리고 여기서 Task는 CompileJava, test, jar 등의 여러가지 종료가 있습니다.
화살표 방향을 잘 보면 Task A는 가장 먼저 실행되고 Task B는 A가 실행이 되어야지 실행될 수 있습니다.

Task A: 소스 코드를 컴파일하는 작업(compileJava).
Task B: 컴파일된 코드를 테스트하는 작업(test).
Task C: 컴파일된 코드에 대해 코드 스타일 검사를 수행하는 작업(checkStyle).

위와 같은 예시가 있을 수 있습니다.

Gradle 고정 빌드 과정 (Fixed Build Phases)

1. 초기화 단계

초기화 단계는 빌드에 추가할 프로젝트를 선택하고 프로젝트 인스턴스를 만드는 사전 작업이라고 생각하면 됩니다.

Root Directory를 보면 settings.gradle을 확인할 수 있습니다.
settings.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 = "kakaobank-android"
include(":app")
include(":core:domain")
include(":core:data")
include(":core:database")
include(":core:designsystem")
include(":core:model")
include(":feature:login")
include(":build-logic")

위와 같이 pluginManagement dependencyResolutionManagement include 3가지로 구성이 되어 있는 것을 확인할 수 있습니다.


Include

Include는 루트 프로젝트에 여러 서브 프로젝트를 포함시킬 수 있게 합니다.
그래서 Root Project를 빌드할 때 Sub Project도 같이 빌드가 되게 됩니다.

단 여기서 각 모듈의 Build는 각 모듈의 Build.Gradle을 따릅니다.


Dependency Resolution Management

이 부분은 레포지토리 모드를 설정하는 부분으로 서브 프로젝트들이 자체적으로 제포지토리를 설정하는 것을 금지하는 것입니다.
레포지토리란 외부 라이브러리나 의존성을 관리하고 배포하는 저장소를 의미합니다.
Maven Central은 오픈 Java 라이브러리와 패키지를 사용할 수 있게 합니다. 그리고 Google은 Android 관련 라이브러리와 도구를 제공합니다.
의존성은 Retrofit, Glide와 같은 라이브러리들

왜 쓰는거야?
서브 프로젝트를 루트 프로젝트에 의존적이게 하여 개별적으로 다른 레포지토리를 사용 못하게 함으로써 중앙 관리의 이점을 얻을 수 있습니다.


pluginManagement

  1. 플러그인을 다운로드할 레포지토리를 지정할 수 있습니다.
  2. 사용할 플러그인의 버전을 중앙 관리할 수 있습니다.
    플러그인은 Gradle 빌드 시스템의 기능을 확장하거나 특정 작업을 자동화하는

서브 모듈이나 서브 프로젝트에서 새로운 플러그인을 추가하는 것은 가능하지만 이미 정의된 플러그인의 버전을 변경하는 것은 불가능합니다.


초기화 단계에서의 Setting.gradle

  1. 루트 디렉토리에 있는 settings.gradle.kts를 가장 먼저 읽는다
  2. pluginManagement 를 통해서 정의된 레포지토리에서 Plugin을 검색하고 다운 받는다
  3. 레포지토리에서 라이브러리 패키지와 같은 의존성을 레포지토리에서 검색하고 다운 받는다.
  4. 서브프로젝트(모듈)을 루트 프로젝트에 포함시키고 각 서프 프로젝트의 경로를 확인하고 해당 서브 프로젝트의 build.gradle.kts 를 로드할 준비를 한다.

초기화 단계에서의 Build.gradle (Root Project 수준)

kotlin // 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 alias(libs.plugins.jetbrains.kotlin.jvm) apply false }
Root Project 수준의 Build.Gradle은 Root Project - Sub Project까지 모두 사용하는 플러그인을 얘기합니다.

아까전에 Settings.gradle에서 pluginManagement 에서 레포지토리를 설정해주었죠?
Gradle은 플러그인을 선언할 때 (plugin 블록 ) 먼저 pluginManagement 블록을 통해 거기에 나와 있는 레포지토리에 plugin이 있는지 확인을 합니다.

조금 애매하긴 한데 초기화 단계와 구성 단계의 중간 과정이라고 생각하겠습니다. Build.gradle에서 필요한 플러그인들을 적용을 하고 apply false 코드 블록은 이 시점에서는 적용되지 않지만, 선언된 플러그인은 초기화 단게에서 준비가 된 것입니다.

apply false는 주로 Root에서 적용하지만 서브 모듈이나 서브 프로젝트에서도 적용할 수는 있습니다

  // 서브프로젝트 build.gradle.kts
  plugins {
      id("com.android.library") apply false
  }

  // 필요 시 플러그인을 적용
  if (someCondition) {
      apply(plugin = "com.android.library")
  }

2. 구성 단계(Configuration)

초기화 단게는 'gradle/init/' 경로의 'init.gradle + *gradle' 파일을 읽어들여 Gradle 인스턴스를 생성한 다음 프로젝트의 'settings.gradle' 파일로 Settings 인스턴스를 적용하는 단계로 이루어진다고 합니다. 이제 구성 단계로 넘어가 봅시다.

구성 단계에서는 초기 단계에서 해두었던 빌드할 대상 프로젝트를 위한 설정을 적용합니다.
구성 단계에서는 Android Gradle Plugin을 통해 아까 생성된 인스턴스에 빌드 설정을 적용합니다.

plugins {
    alias(libs.plugins.android.library)
    alias(libs.plugins.jetbrains.kotlin.android)
}

Plugin

여기서 libs.plugins.android.library가 의미하는 것이 AGP입니다.

plugins {
    alias(libs.plugins.android.library)
    alias(libs.plugins.jetbrains.kotlin.android)
}

Plugin 블록은 Android Gradle Plugin을 빌드에 적용을 하는 것입니다.

Android

android {
    namespace = "com.kakaobank.tutorial.kakaobank_android.core.data"
    compileSdk = 34

    defaultConfig {
        minSdk = 29

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles("consumer-rules.pro")
    }

    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"
    }
}

여기서는 패키지 이름(namespace), compileSdk, 기본 설정, 빌드타입, 컴파일 옵션, 등을 설정합니다.

Dependency 블록

    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.appcompat)
    implementation(libs.material)
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core)

여기서는 이 모듈에서 사용하는 종속 항목을 정의합니다. 예를 들어 외부 라이브러리에서 사용할 종속 항목이 있으면 여기 부분에서 사용하면 됩니다.

이 내용들을 이용해서 멀티 모듈에서 Build.Gradle이 어떻게 구성이 되는지 살펴보겠습니다.

참고

https://medium.com/@renovatio0424/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%9D%BC%EB%A9%B4-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%98%EB%8A%94-gradle-%EC%9B%90%EB%A6%AC-c39a3299ba6d
https://everyday-develop-myself.tistory.com/300

profile
낭만 개발자

0개의 댓글