안녕하세요
오늘은 Android 프로젝트에서 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을 적용한 두번째 이유입니다.
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을 만들어 볼겁니다.
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 만 적용할거면 할거 없음 아닐시 평소처럼 라이브러리 추가 하세요
}