Gradle 빌드 개요

Android 애플리케이션은 일반적으로 Gradle 빌드 시스템을 사용하여 빌드됩니다. 빌드를 구성하는 방법을 자세히 알아보기 전에 빌드에 관한 개념을 살펴보고 시스템을 전체적으로 파악해 보겠습니다.

빌드란 무엇인가요?

빌드 시스템은 소스 코드를 실행 가능한 애플리케이션으로 변환합니다. 빌드에는 애플리케이션이나 라이브러리를 분석, 컴파일, 연결, 패키징하는 여러 도구가 포함되는 경우가 많습니다. Gradle은 작업 기반 접근 방식을 사용하여 이러한 명령어를 정리하고 실행합니다.

작업은 입력을 출력으로 변환하는 명령어를 캡슐화합니다. 플러그인은 작업과 구성을 정의합니다. 빌드에 플러그인을 적용하면 플러그인의 작업이 등록되고 입력과 출력을 사용하여 함께 연결됩니다. 예를 들어 빌드 파일에 Android Gradle 플러그인 (AGP)을 적용하면 APK 또는 Android 라이브러리를 빌드하는 데 필요한 모든 작업이 등록됩니다. java-library 플러그인을 사용하면 Java 소스 코드에서 jar를 빌드할 수 있습니다. Kotlin 및 기타 언어에도 유사한 플러그인이 있지만 다른 플러그인은 플러그인을 확장하기 위한 것입니다. 예를 들어 protobuf 플러그인은 AGP나 java-library과 같은 기존 플러그인에 protobuf 지원을 추가하기 위한 것입니다.

Gradle은 구성보다 관례를 선호하므로 플러그인에는 기본값이 기본적으로 제공되지만 선언적 도메인별 언어 (DSL)를 통해 빌드를 추가로 구성할 수 있습니다. DSL은 빌드 방법이 아닌 빌드 대상을 지정할 수 있도록 설계되었습니다. 플러그인의 로직은 '방법'을 관리합니다. 이 구성은 프로젝트 (및 하위 프로젝트)의 여러 빌드 파일에 지정됩니다.

작업 입력은 파일 및 디렉터리뿐만 아니라 Java 유형 (정수, 문자열 또는 맞춤 클래스)으로 인코딩된 기타 정보일 수 있습니다. 출력은 디스크에 작성해야 하므로 디렉터리나 파일만 가능합니다. 작업 출력을 다른 작업 입력에 연결하면 작업이 서로 연결되어 하나가 다른 작업보다 먼저 실행되어야 합니다.

Gradle은 빌드 파일에서 임의의 코드와 작업 선언을 작성하는 것을 지원하지만, 이렇게 하면 도구에서 빌드를 이해하기가 더 어려워지고 유지관리도 어려워질 수 있습니다. 예를 들어 플러그인 내의 코드에 대한 테스트는 작성할 수 있지만 빌드 파일에는 작성할 수 없습니다. 대신 빌드 로직과 작업 선언을 플러그인 (본인 또는 다른 사용자가 정의)으로 제한하고 빌드 파일에서 해당 로직을 사용하는 방법을 선언해야 합니다.

Gradle 빌드가 실행되면 어떻게 되나요?

Gradle 빌드는 세 단계로 실행됩니다. 이러한 각 단계에서는 빌드 파일에 정의된 코드의 서로 다른 부분을 실행합니다.

  • 초기화는 빌드에 포함할 프로젝트와 하위 프로젝트를 결정하고 빌드 파일과 적용된 플러그인이 포함된 클래스 경로를 설정합니다. 이 단계에서는 빌드할 프로젝트와 플러그인 및 라이브러리를 가져올 위치를 선언하는 설정 파일에 중점을 둡니다.
  • 구성은 각 프로젝트의 작업을 등록하고 빌드 파일을 실행하여 사용자의 빌드 사양을 적용합니다. 구성 코드에는 실행 중에 생성된 데이터나 파일에 대한 액세스 권한이 없습니다.
  • 실행은 애플리케이션의 실제 '빌드'를 실행합니다. 구성의 출력은 사용자가 요청한 모든 필수 빌드 단계 (명령줄에 제공되거나 빌드 파일의 기본값으로 제공된 작업)를 나타내는 작업의 방향성 비순환 그래프 (DAG)입니다. 이 그래프는 태스크 선언에 명시적으로 표시되거나 입력 및 출력을 기반으로 하는 태스크 간의 관계를 나타냅니다. 태스크의 입력이 다른 태스크의 출력인 경우 다른 태스크 후에 실행해야 합니다. 이 단계에서는 그래프에 정의된 순서대로 오래된 작업을 실행합니다. 마지막 실행 이후 작업의 입력이 변경되지 않은 경우 Gradle은 작업을 건너뜁니다.

자세한 내용은 Gradle 빌드 수명 주기를 참고하세요.

구성 DSL

Gradle은 도메인별 언어 (DSL)를 사용하여 빌드를 구성합니다. 이 선언적 접근 방식은 단계별 (명령형) 안내를 작성하는 대신 데이터를 지정하는 데 중점을 둡니다. Kotlin 또는 Groovy를 사용하여 빌드 파일을 작성할 수 있지만 Kotlin을 사용하는 것이 좋습니다.

DSL은 도메인 전문가와 프로그래머 모두가 프로젝트에 더 쉽게 참여할 수 있도록 노력하며, 데이터를 더 자연스러운 방식으로 나타내는 작은 언어를 정의합니다. Gradle 플러그인은 DSL을 확장하여 작업에 필요한 데이터를 구성할 수 있습니다.

예를 들어 빌드의 Android 부분을 구성하는 것은 다음과 같습니다.

Kotlin

android {
    namespace = "com.example.app"
    compileSdk {
        version = release(36) {
            minorApiLevel = 1
        }
    }
    // ...

    defaultConfig {
        applicationId = "com.example.app"
        minSdk {
            version = release(23)
        }
        targetSdk {
            version = release(36)
        }
        // ...
    }
}

Groovy

android {
    namespace = 'com.example.app'
    compileSdk {
        version = release(36) {
            minorApiLevel = 1
        }
    }
    // ...

    defaultConfig {
        applicationId = 'com.example.app'
        minSdk {
            version = release(23)
        }
        targetSdk {
            version = release(36)
        }
        // ...
    }
}

실제로 DSL 코드는 다음과 유사합니다.

fun Project.android(configure: ApplicationExtension.() -> Unit) {
    ...
}

interface ApplicationExtension {
    var namespace: String?

    fun compileSdk(configure: CompileSdkSpec.() -> Unit) {
        ...
    }

    val defaultConfig: DefaultConfig

    fun defaultConfig(configure: DefaultConfig.() -> Unit) {
        ...
    }
}

DSL의 각 블록은 이를 구성하는 람다를 사용하는 함수와 이에 액세스하는 동일한 이름의 속성으로 표현됩니다. 이렇게 하면 빌드 파일의 코드가 데이터 사양처럼 느껴집니다.

외부 종속 항목

Maven 빌드 시스템은 종속 항목 사양, 저장소 및 관리 시스템을 도입했습니다. 라이브러리는 저장소 (서버 또는 디렉터리)에 저장되며 버전과 다른 라이브러리에 대한 종속성을 포함한 메타데이터가 있습니다. 검색할 저장소, 사용할 종속 항목 버전을 지정하면 빌드 시스템에서 빌드 중에 이를 다운로드합니다.

Maven 아티팩트는 그룹 이름 (회사, 개발자 등), 아티팩트 이름 (라이브러리 이름), 아티팩트 버전으로 식별됩니다. 일반적으로 group:artifact:version로 표시됩니다.

이 접근 방식을 사용하면 빌드 관리가 크게 개선됩니다. 이러한 저장소를 'Maven 저장소'라고 부르는 경우가 많지만, 이는 아티팩트가 패키징되고 게시되는 방식에 관한 것입니다. 이러한 저장소와 메타데이터는 Gradle을 비롯한 여러 빌드 시스템에서 재사용되었으며 Gradle은 이러한 저장소에 게시할 수 있습니다. 공개 저장소를 사용하면 모든 사용자가 사용할 수 있도록 공유할 수 있고, 회사 저장소는 내부 종속 항목을 사내에 유지합니다.

프로젝트를 하위 프로젝트(Android 스튜디오에서는 '모듈'이라고도 함)로 모듈화할 수도 있으며, 이는 종속 항목으로도 사용할 수 있습니다. 각 하위 프로젝트는 하위 프로젝트 또는 최상위 프로젝트에서 사용할 수 있는 출력 (예: jar)을 생성합니다. 이렇게 하면 다시 빌드해야 하는 부분을 격리하여 빌드 시간을 개선하고 애플리케이션에서 책임을 더 잘 분리할 수 있습니다.

빌드 종속 항목 추가에서 종속 항목을 지정하는 방법을 자세히 알아보세요.

빌드 변형

Android 애플리케이션을 만들 때는 일반적으로 여러 변형을 빌드하려고 합니다. 변형에는 서로 다른 코드가 포함되거나 서로 다른 옵션으로 빌드되며 빌드 유형과 제품 버전으로 구성됩니다.

빌드 유형은 선언된 빌드 옵션을 다양하게 합니다. 기본적으로 AGP는 '출시' 및 '디버그' 빌드 유형을 설정하지만 이를 조정하고 더 추가할 수 있습니다 (예: 스테이징 또는 내부 테스트용).

디버그 빌드는 애플리케이션을 축소하거나 난독화하지 않으므로 빌드 속도가 빨라지고 모든 심볼이 그대로 유지됩니다. 또한 애플리케이션을 '디버그 가능'으로 표시하고 일반 디버그 키로 서명하며 기기에 설치된 애플리케이션 파일에 대한 액세스를 사용 설정합니다. 이렇게 하면 애플리케이션을 실행하는 동안 파일과 데이터베이스에 저장된 데이터를 탐색할 수 있습니다.

출시 빌드는 애플리케이션을 최적화하고, 출시 키로 서명하며, 설치된 애플리케이션 파일을 보호합니다.

제품 버전을 사용하면 애플리케이션에 포함된 소스와 종속 항목 변형을 변경할 수 있습니다. 예를 들어 애플리케이션에 'demo' 및 'full' 버전 또는 'free' 및 'paid' 버전을 만들 수 있습니다. 'main' 소스 세트 디렉터리에 공통 소스를 작성하고, 제품 버전에 따라 이름이 지정된 소스 세트에서 소스를 재정의하거나 추가합니다.

AGP는 빌드 유형과 제품 버전의 각 조합에 대해 변형을 생성합니다. 버전을 정의하지 않으면 변형의 이름이 빌드 유형을 따릅니다. 두 가지를 모두 정의하면 변형의 이름은 <flavor><Buildtype>가 됩니다. 예를 들어 빌드 유형이 release, debug이고 버전이 demo, full이면 AGP는 다음과 같은 변형을 생성합니다.

  • demoRelease
  • demoDebug
  • fullRelease
  • fullDebug

다음 단계

빌드 개념을 살펴봤으니 프로젝트에서 Android 빌드 구조를 살펴보세요.