Android 빌드 과정

Android 빌드 과정은 개발자가 작성한 소스 코드, 리소스 파일, 라이브러리 등을

최종적으로 안드로이드 기기에서 실행 가능한 APK(Android Package Kit) 파일로 만드는 과정입니다.

이 과정은 여러 단계를 거치며, 각 단계에서 다양한 도구와 기술이 사용됩니다.

1. 빌드 준비:

  • Gradle 설정: build.gradle 파일에서 빌드 설정을 확인하고 필요한 플러그인, 의존성 라이브러리, 빌드 변수(Build Variant) 등을 설정합니다.

  • Manifest 병합: 애플리케이션의 AndroidManifest.xml 파일과 라이브러리의 매니페스트 파일을 병합하여 최종 매니페스트 파일을 생성합니다.

  • 리소스 병합: 애플리케이션의 리소스 파일과 라이브러리의 리소스 파일을 병합하고, 중복된 리소스를 처리합니다.

2. 컴파일:

  • Java/Kotlin 컴파일: Java 또는 Kotlin 소스 코드를 컴파일하여 .class 파일(Java 바이트코드)을 생성합니다.

  • AIDL 컴파일: AIDL(Android Interface Definition Language) 파일을 컴파일하여 .java 인터페이스 파일을 생성합니다.

  • R 클래스 생성: 리소스 파일을 컴파일하여 R 클래스를 생성합니다. R 클래스는 리소스 ID를 정수 값으로 매핑하여 코드에서 리소스에 접근할 수 있도록 합니다.

3. DEX 변환:

  • D8/R8 컴파일: .class 파일들을 DEX(Dalvik Executable) 형식으로 변환합니다. DEX는 Android 런타임에서 실행 가능한 바이트코드 형식입니다.

  • 코드 축소 (R8): 불필요한 코드를 제거하고 난독화하여 APK 파일 크기를 줄입니다.

4. APK 패키징:

  • APK 생성: DEX 파일, 컴파일된 리소스, AndroidManifest.xml 파일, 서명 정보 등을 포함하는 APK 파일을 생성합니다.

  • 서명: APK 파일에 개발자의 서명을 추가하여 앱의 무결성과 출처를 보장합니다.

  • 정렬: APK 파일 내의 파일들을 정렬하여 앱 설치 및 로딩 속도를 향상시킵니다.

5. 배포:

  • Google Play Store: APK 파일을 Google Play Store에 업로드하여 사용자에게 배포합니다.

  • Ad-hoc 배포: APK 파일을 직접 사용자에게 전달하여 설치하도록 합니다.

빌드 도구:

  • Gradle: Android 빌드 시스템으로, 빌드 과정을 자동화하고 관리하는 데 사용됩니다.

  • Android Studio: Android 공식 IDE로, Gradle 기반 빌드 시스템을 내장하고 있으며, 편리한 빌드 및 배포 기능을 제공합니다.

빌드 변형 (Build Variant):

  • 빌드 유형 (Build Type): debug, release 등 빌드 유형에 따라 다른 설정을 적용할 수 있습니다.
  • 제품 특성 (Product Flavor): free, paid 등 제품 특성에 따라 다른 기능이나 리소스를 포함하는 APK를 생성할 수 있습니다.

Android 멀티 모듈 구성 방법

안드로이드 앱을 멀티 모듈로 구성하면 코드의 재사용성, 유지보수성, 빌드 성능을 향상시킬 수 있습니다.

멀티 모듈 구성은 앱을 여러 개의 작은 모듈로 분리하여 각 모듈이 특정 기능이나 레이어를 담당하도록 하는 것을 의미합니다.

멀티 모듈 구성의 장점:

  • 재사용성 향상: 공통 기능을 모듈화하여 여러 프로젝트에서 재사용할 수 있습니다.
  • 유지보수 용이: 모듈별로 코드를 분리하여 관리하므로 코드의 복잡성을 줄이고 유지보수를 쉽게 할 수 있습니다.
  • 빌드 성능 향상: 변경된 모듈만 다시 빌드하여 빌드 시간을 단축할 수 있습니다.
  • 협업 용이: 모듈별로 개발을 분담하여 팀 협업을 효율적으로 진행할 수 있습니다.
  • 테스트 용이성: 모듈 단위로 테스트를 수행하여 테스트 커버리지를 높이고 오류를 조기에 발견할 수 있습니다.

멀티 모듈 구성 방법:

  1. 모듈 분리 기준 설정:
    • 기능별: 각 기능(로그인, 검색, 설정 등)을 담당하는 모듈로 분리합니다.
    • 레이어별: UI, 데이터, 도메인 등 레이어별로 모듈을 분리합니다.
    • 공통 모듈: 여러 모듈에서 공통으로 사용하는 기능(네트워크, 유틸리티 등)을 모듈화합니다.
  2. 모듈 생성:
    • Android Studio에서 File -> New -> New Module을 선택합니다.
    • Android Library 또는 Java/Kotlin Library를 선택하여 모듈을 생성합니다.
  3. 모듈 간 의존성 설정:
    • build.gradle 파일에서 implementation 또는 api 키워드를 사용하여 모듈 간의 의존성을 설정합니다.
    • api는 해당 모듈의 API를 외부에 공개할 때 사용하고, implementation은 외부에 공개하지 않을 때 사용합니다.
  4. 코드 및 리소스 이동:
    • 각 모듈에 맞게 코드와 리소스를 이동하고, 필요한 경우 인터페이스를 정의하여 모듈 간 통신을 구현합니다.

멀티 모듈 구성 예시:

MyApplication
├── app (Application Module)
│   └── ...
├── core (Core Module)
│   └── ... (네트워크, 유틸리티 등)
├── feature (Feature Modules)
│   ├── login
│   │   └── ... (로그인 관련 코드)
│   ├── search
│   │   └── ... (검색 관련 코드)
│   └── settings
│       └── ... (설정 관련 코드)
└── data (Data Module)
    └── ... (데이터베이스, API 관련 코드)

주의 사항:

  • 순환 의존성 방지: 모듈 간에 순환(cycle) 의존성이 발생하지 않도록 주의해야 합니다, 단뱡향 지향
  • 인터페이스 활용: 모듈 간의 결합도를 낮추기 위해 인터페이스를 적극적으로 활용합니다.
  • 테스트 코드 작성: 각 모듈에 대한 테스트 코드를 작성하여 품질을 보장합니다.

Gradle Plugin

Gradle 플러그인은 재사용 가능한 빌드 로직을 패키지로 구성하여 다양한 프로젝트와 빌드에서 사용할 수 있습니다.

Gradle을 사용하면 자신만의 플러그인을 구현할 수 있으므로 빌드 로직을 재사용하고 다른 사람들과 공유할 수 있습니다.

Plugin이란 빌드에 관련된 로직(task, dependency…) 등을 모아서 재사용할 수 있게 도와주는 역할을 합니다.

  • org.jetbrains.kotlin.android, com.android.application 등 안드로이드에서 사용하는 Plugin이며 내부적으로 Android 빌드에 필요한 코드로 구성되어 있습니다.

buildScript 형식으로 프로젝트 내부에서 사용할 수 있으며, 독립 형태로 만들어서 배포하여 프로젝트 외부에서도 사용할 수 있습니다.

Gradle Plugin 구현

1. 프로젝트 단위 settings.gradle.kts에 Gradle Plugin 프로젝트를 추가합니다.

1
2
3
4
5
pluginManagement {
    repositories {
        includeBuild("build-logic")
    }
}

2. 루트 프로젝트에 Gradle Plugin 프로젝트를 만들고 build.gradle.kts, settings.gradle.kts를 생성합니다.

프로젝트 구조

Project
- build-logic
  - build.gradle.kts
  - settings.gradle.kts

build.gradle.kts

  • Plugin 구현에 필요한 의존성을 추가할 수 있습니다.
    • Hilt를 구현한다면 com.google.dagger:hilt-android-gradle-plugin 같이 필요한 의존성을 추가합니다.
    • VersionCatalog를 사용할 수 있습니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
plugins {
    `kotlin-dsl`
}

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

dependencies {

}

settings.gradle.kts

  • VersionCatalog를 추가하여 사용할 수 있습니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }

    versionCatalogs {
        create("libs") {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}

3. Plugin 구현

  • Plugin 인터페이스를 상속받고 공통된 로직을 구현합니다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
internal class HiltPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        applyPlugin(target.pluginManager)
        applyDependency(target.dependencies, target.getVersionCatalog())
        applyKapt(target.extensions.getByType())
    }

    private fun applyPlugin(manager: PluginManager) = with(manager) {
        apply("com.google.dagger.hilt.android")
        apply("org.jetbrains.kotlin.kapt")
    }

    private fun applyDependency(handler: DependencyHandler, libs: VersionCatalog) = with(handler) {
        implementation(libs.findLibrary("hilt-android").get())
        kapt(libs.findLibrary("hilt-compiler").get())
    }

    private fun applyKapt(extension: KaptExtension) = with(extension) {
        correctErrorTypes = true
    }
}