우당탕 build-logic 적용기

Assist·2025년 6월 20일
2

Android

목록 보기
25/25

안녕하세요
오늘은 Android 프로젝트에서 build - logic을 적용기를 써볼려고 합니다.

왜 build-logic인가

기존에 앱 개발을 할때 라이브러리를 임포트 할때 이렇게 했습니다.

dependencies {
    implementation(libs.compose.nav)
}

이런식으로 libs.versions.toml 파일에 정의한 라이브러리를 dependencies에서 가져와서 했을겁니다.

저는 Clean Architecture을 적용해서 Feature 별로 모듈을 나눠서 중복된 라이브러리가 정말 많았습니다.

그리고 무엇보다 Build 시간이 엄청 오래 걸렸습니다!!!

이를 해결 하기 위해 방법을 찾다가 build-logic 이라는걸 알았씁니다.

https://youtu.be/7iag6zpGd98?si=uBW-bTXW2h7rYi_2

관련 드로이드 나이츠 영상입니다.

그래서 왜 좋은건데

중복된 코드가 사라집니다

중복 했던 라이브러리를 하나로 정의 하면서 더이상 모듈을 생성할때 필요한 라이브러리를 libs.versions.toml 파일 찾아가면서 하나 하나씩 복사 붙여넣기 할 필요 없이 kotlin으로 정의 하고 가져오면 됩니다!

기존

import java.util.Properties

plugins {
    alias(libs.plugins.android.library)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.androidx.compose.compiler)
    id("kotlin-kapt")
    id("com.google.dagger.hilt.android")
}
val properties = Properties()
val localPropertiesFile = project.rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
    localPropertiesFile.inputStream().use { inputStream ->
        properties.load(inputStream)
    }
}
android {
    namespace = "com.****.****"
    compileSdk = 35

    defaultConfig {
        minSdk = 31

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles("consumer-rules.pro")
        val ****: String = properties.getProperty("***", "")
        buildConfigField("String", "***", ***)
    }

    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
        buildConfig = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.13"
    }
}
kapt {
    correctErrorTypes = true
}
dependencies {
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.appcompat)
    implementation(libs.androidx.activity.compose)
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.ui)
    implementation(libs.androidx.ui.graphics)
    implementation(libs.androidx.ui.tooling.preview)
    implementation(libs.androidx.material3)
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core)
    //hilt
    implementation(libs.hilt.android)
    kapt(libs.hilt.compiler)
    //project
    implementation(project(":domain"))
    implementation(project(":core"))
    //firebase
    implementation(libs.firebase.crash)
    //image
    implementation(libs.glide.compose)
    implementation("io.coil-kt.coil3:coil-compose:3.2.0")
    implementation("io.coil-kt.coil3:coil-network-okhttp:3.2.0")
    //nav
    implementation(libs.compose.nav)
}

build-logic

import java.util.Properties

plugins {
    id("***.android.library")
}

val properties = Properties()
val localPropertiesFile = project.rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
    localPropertiesFile.inputStream().use { inputStream ->
        properties.load(inputStream)
    }
}

android {
    namespace = "****.***"

    defaultConfig {
        val ****: String = properties.getProperty("****", "")
        buildConfigField("String", "***", ***)
    }
}

dependencies {
    implementation(project(":domain"))
    implementation(project(":core"))
    implementation(libs.glide.compose)
    implementation(libs.coil.compose)
    implementation(libs.coil.okhttps)
    implementation(libs.firebase.crash)
    implementation(libs.compose.nav)
}

딱 봐도 확 줄은거 보이시죠? 제가 build-logic을 적용한 첫번째 이유입니다.

빌드 속도가 빨라져요!!

  • 기존 프로젝트는 clean / Rebuild 할때 라이브러리를 모두 다시 빌드하여 시간이 많이 걸렸습니다.

  • build-logic은 서브 빌드로 분리되어 캐시 활용, IDE가 더 빠르게 동작합니다.

이게 제가 build-logic을 적용한 두번째 이유입니다.

어떻게 적용할까?

build-logic 파일을 만들어요

project-root/
├── build-logic/ -> 파일을 만들어 주세요 
├── app/
└── settings.gradle.kts

파일 생성은 마우스 우클릭 -> new -> File 생성하십시요

build-logic/
├── build.gradle.kts         // build-logic 자체 설정
├── settings.gradle.kts      
└── convention/
    ├── LibraryPlugin 
      

동일 하게 파일 와 LibraryPlugin 의 Kotlin class 파일을 만들어 주세요

자 이제 다 왔습니다.

이제 정의한 libs.version.toml 파일을 불러와 써야겠지요

build-logic / setting.gradle.kts 로 가줍니다.

rootProject.name = "build-logic"
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
    versionCatalogs {
        create("libs") {
            from(files("../gradle/libs.versions.toml")) -> 이게 libs File 
        }
    }
}

그럼 이제 코드를 작성해 봅시다. 저희가 해볼껀 Hilt을 적용한 Plugin을 만들어 볼겁니다.

  • 저는 kotlin 2.1.0 을 사용함으로 compiler 설정을 합니다
class LibraryPlugin : Plugin<Project> {
    override fun apply(project: Project) = with(project) {
        // libs.versions.toml 파일에 정의된 버전 카탈로그를 가져오는 부분
        val libs = extensions.getByType<VersionCatalogsExtension>().named("libs") 

        // Android 라이브러리 플러그인 적용
        pluginManager.apply("com.android.library")
        // Kotlin Android 플러그인 적용
        pluginManager.apply("org.jetbrains.kotlin.android")
        // KAPT (Kotlin Annotation Processing Tool) 적용
        pluginManager.apply("org.jetbrains.kotlin.kapt")
        // Hilt 플러그인 적용 (DI)
        pluginManager.apply("com.google.dagger.hilt.android")
        // Jetpack Compose 플러그인 적용
        pluginManager.apply("org.jetbrains.kotlin.plugin.compose")

        // Android 라이브러리 설정
        extensions.getByType<LibraryExtension>().apply {
            compileSdk = 35 // 사용할 compile SDK 버전

            defaultConfig {
                minSdk = 31 // 최소 지원 SDK 버전
                testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
                consumerProguardFiles("consumer-rules.pro")
            }

            buildFeatures.buildConfig = true // BuildConfig 클래스 생성 여부
            buildFeatures.compose = true     // Jetpack Compose 사용 여부

            // Compose Kotlin 컴파일러 확장 버전
            composeOptions.kotlinCompilerExtensionVersion = "1.5.13"

            compileOptions {
                sourceCompatibility = JavaVersion.VERSION_17 // Java 17 사용
                targetCompatibility = JavaVersion.VERSION_17
            }
        }

        // Kotlin 코드에서도 JVM 17을 사용하도록 설정
        extensions.getByType<KotlinAndroidProjectExtension>().apply {
            jvmToolchain(17)
        }

        // 의존성 추가 (libs.versions.toml 기반)
        dependencies {
            // Hilt 의존성 추가
            "implementation"(libs.findLibrary("hilt-android").get())
            "kapt"(libs.findLibrary("hilt-compiler").get())
        }
    }
}

자 이제 코드 등록을 완료 했으면 build-logic / build.gradle.kts로 이동해줍니다

// Kotlin DSL로 Gradle Plugin을 작성할 수 있게 해주는 플러그인
plugins {
    `kotlin-dsl`
}

// 이 플러그인이 배포될 경우 사용할 그룹 ID (예: maven group), 내부 프로젝트라면 식별용
group = "velog.buildlogic" -> 변수명 설정 

// 버전 정보 (보통은 배포용, 내부에선 크게 중요 X)
version = "1.0-SNAPSHOT"

// Gradle Plugin으로 등록할 custom plugin 정의
gradlePlugin {
    plugins {
        // "test.library" 라는 ID로 사용할 수 있는 Plugin을 정의
        create("hiltLibraryConvention") {
            id = "test.library" // 실제 build.gradle.kts에서 사용할 ID (기억하세요) 
            implementationClass = "convention.LibraryPlugin" // 플러그인 구현 클래스 경로
        }
    }
}

// 사용할 라이브러리 저장소 정의
repositories {
    google()
    mavenCentral()
}

// 플러그인을 만들 때 필요한 Gradle 관련 의존성들
dependencies {
    // Android Gradle Plugin API를 사용할 수 있도록 함 (compileOnly = 컴파일 시만 사용, 런타임 미포함)
    compileOnly("com.android.tools.build:gradle:8.6.0")
    
    // Kotlin Android Plugin을 사용할 수 있도록 함
    compileOnly("org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin:2.1.0")
}

자 이렇게 하시면 build-logic 코드 작성은 끝났습니다. 그럼 마지막으로
rootProject / setting.gradles.kts 로 가주세요

pluginManagement {
    repositories {
        google {
            content {
                includeGroupByRegex("com\\.android.*")
                includeGroupByRegex("com\\.google.*")
                includeGroupByRegex("androidx.*")
            }
        }
        includeBuild("build-logic") -> 이렇게 폴더명을 추가해주세요 
        mavenCentral()
        gradlePluginPortal()
    }
}

자 그럼 적용하러 가볼까요

import java.util.Properties

plugins {
    id("test.library") -> 이렇게 추가하시면 끝입니다. 
}

android {
    namespace = "****.***"

    defaultConfig {

    }
}

dependencies {
   //hilt 만 적용할거면 할거 없음 아닐시 평소처럼 라이브러리 추가 하세요 
}

총평

  • 빌드 속도가 빨라져서 좋다 기존 10분 -> 3분
  • 중복된 라이브러리 임포트 안해서 너무 좋다
profile
안드로이드만 좋아하는 특이한 개발자

0개의 댓글