Android

[다시 만들어보는 클린아키텍쳐] build-logic 편

최데브 2024. 7. 31. 17:36

build-logic 도 이전 포스팅에 있었던 모듈의 하나다.

다만 이 모듈에는 Version Catalog, Convention Plugin 이라는 개념을 추가해서 설명할것이다.

 

Version Catalog

는 이름에서 느껴지는것처럼 버전을 관리해준다. 즉 프로젝트 전반에 있는 라이브러리 의존성을 관리하는 방식이다.

libs.versions.tomi 라는 파일이 있을텐데 요놈이 그 역할을 한다.

알아본김에 좀 더 알아보자.

이 파일을 열어보면 크게 네 파트로 나뉘는데 

[versions]

[libraries]

[plugins]

[bundles]

가 있다.

 

versions : 라이브러리의 버전을 정의한다. ex) glide = 5.0.0

libaries : 해당 라이브러리 정보를 적는다. ( group : name : version 형태에 대한 정보)

plugins : 플러그인 id와 버전 정보를 작성한다.

bundles : 함께 묶을 라이브러리들을 정의한다. ex ) [“retrofit”, “okhttp-interceptor”]

 

[versions]

라이브러리의 버전입니다. 심플하게 아래와 같이 정의할 수 있다.

[versions]
ktx = "1.9.0"

[libraries]

라이브러리에 대한 세부 정보를 정의한다. group : name : version 과 같은 네이밍 컨벤션을 따르며, 영역에 해당하는 라이브러리 정보를 기입해주면된다

[versions]
ktx = "1.9.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "ktx" }

이렇게 정의되었을 때, build.gradle 에서는 아래처럼 접근이 가능한다.

dependencies {
    implementation(libs.androidx.core.ktx)
    ...
}

[plugins]

gradle plugin 버전을 정의

[versions]
androidGradlePlugin = "7.4.1"

[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }

build.gradle에는 다음과 같이 적용할 수 있다

// Top-level build.gradle.kts
plugins {
   alias(libs.plugins.android.application) apply false

}

// module build.gradle.kts
plugins {
   alias(libs.plugins.android.application)

}

[bundles]

해당 섹션을 통해 앞서 [libraries] 섹션에 정의한 종속성들을 하나로 묶어서 관리할 수도 있다. 같이 쓰이는 라이브러리들을

이렇게 묶어서 사용하면 편할 것이다,

[versions]
ktx = "1.9.0"
lifecycle = "2.7.0"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "ktx" }
androidx-lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime", version.ref = "lifecycle"}

[bundles]
androidx = ["androidx-core-ktx", "androidx-lifecycle-runtime"]

해당 bundle 라이브러리는 bundles라는 Accessor를 통해 접근가능하다.

dependencies {
  implementation(libs.bundles.androidx)
}

 

이렇게 의존성 관리를 편하게 만들어준다.

또 여기서 관리하면 멀티모듈로 프로젝트를 구성하더라도 이 카탈로그에 적힌대로 가져와서 import 할 수 있다.

 

앞에 이야기가 길었지만 사실 이번글에서 핵심은 

Convention Plugin 을 설명하는것이다.

 

먼저 추가하기전 build-logic 이라는 모듈을 Java or Kotlin Library로 만들어주자.

그리고 build-logic의 build.gradle 을 아래처럼 만들어주자.

plugins {
    `kotlin-dsl`
    `kotlin-dsl-precompiled-script-plugins`
}

dependencies {
    implementation(libs.android.gradlePlugin)
    implementation(libs.kotlin.gradlePlugin)
    implementation(libs.verify.detektPlugin)
    compileOnly(libs.compose.compiler.gradle.plugin)
}

gradlePlugin {
    plugins {
        register("androidHilt") {
            id = "com.hyoseok.hilt"
            implementationClass = "com.hyoseok.samplecleanarchitecture.HiltAndroidPlugin"
        }
        register("kotlinHilt") {
            id = "example.kotlin.hilt"
            implementationClass = "com.hyoseok.samplecleanarchitecture.HiltKotlinPlugin"
        }
    }
}

 

또 잊지말아야할건 root 수준의 setting.gradle 에서

pluginManagement {
    includeBuild("build-logic") // 이 부분 추가

그리고 아래에 자동으로 추가 됐을 build-logic 은 지워준다. 중복해서 적어두면 에러가 발생하는걸 볼 수 있다.

include(":build-logic") // 삭제

 

그리고 hilt 를 플러그인으로 만들어주는 상황이라고 한다면

build-logic 모듈에 HiltAndroid.kt 를 만들어보자. 

gradle 파일에 직접 설정해주는것과 비슷하게 보이지만 코틀린 코드로 작성이 된걸 볼 수 있다.

HiltAndroid.kt

internal fun Project.configureHiltAndroid() {
    with(pluginManager) {
        apply("dagger.hilt.android.plugin")
        apply("org.jetbrains.kotlin.kapt")
    }

    val libs = extensions.libs
    dependencies {
        "implementation"(libs.findLibrary("hilt.android").get())
        "kapt"(libs.findLibrary("hilt.android.compiler").get())
        "kaptAndroidTest"(libs.findLibrary("hilt.android.compiler").get())
    }
}

internal class HiltAndroidPlugin : Plugin<Project> {

    override fun apply(target: Project) {
        with(target) {
            configureHiltAndroid()
        }
    }
}

 

 

 

자 이렇게 코드를 만들어준다고 hilt가 적용이 될까? 아니다.

실제로 사용되는곳에 적용될 수 있도록 해줘야하는데 몇가지 작업이 필요하다.

 

위에 build-logic의 build.gradle를 적용하는 쪽 코드에보면

`kotlin-dsl-precompiled-script-plugins` 라는게 있는데 이걸 알고 넘어가야한다.

kotlin-dsl에 포함된 플러그인인데 이걸 사용하면 원하는대로 

configureHiltAndroid 나 다른 확장함수들을 조합해서 하나의 플러그인으로 미리 만들어둘 수 있다.

 

 

위 사진에 있는 위치에 ~~.gradle.kts 파일을 생성하자 이름은 원하는대로 만들면된다.

그 아래에 보이는 gradle 이 아닌 파일들은 빌드하고 나면 precomplied 되어 자동으로 생성된다.

 

hyoseok.android.application.gradle.kts 파일의 내부를 예시로 봐보자.

plugins {
    id("com.android.application")
}

configureKotlinAndroid()
configureHiltAndroid()
configureKotestAndroid()
configureCoilAndroid()


dependencies {

}

여길 보면 위에서 우리가 만들어줬던 configureHiltAndroid 가 보인다.

다른 것들도 다 동일한 방식으로 만들어주면 여기에 한번에 포함시킬 수 있게된다.

그럼 이제 실제로 적용을 하는 마지막 부분이다.

 

app 모듈의 build.gradle에서 (예를 들어서 app 모듈인것이지 실제로 사용할때는 필요한 곳에 맞게 조합해서 쓰면 된다.)

plugins {
    id("hyoseok.android.application") // 이런식으로 추가해주면 적용이 완료된다.
    id("hyoseok.android.compose")
    id("com.google.android.gms.oss-licenses-plugin")
}

 

이런 방식으로 gradle 을 관리하면 멀티모듈을 사용할때 중복되는 gradle 코드가 많이 줄어든다.

그리고 여러 모듈에 공통적으로 바뀌어야할 상황이 있다면 작성해준 플러그인만 변경하면 된다.

 

추가로 아래 부분을 위에서 봤을텐데 이건 왜 적어준거냐고 물으신다면

gradlePlugin {
    plugins {
        register("androidHilt") {
            id = "com.hyoseok.hilt"
            implementationClass = "com.hyoseok.samplecleanarchitecture.HiltAndroidPlugin"
        }
        register("kotlinHilt") {
            id = "example.kotlin.hilt"
            implementationClass = "com.hyoseok.samplecleanarchitecture.HiltKotlinPlugin"
        }
    }
}

 

 

다른 모듈에서 아래처럼 id를 적어주어 적용해줄수도 있다!

plugins {
    id("com.hyoseok.hilt")
    id("kotlinx-serialization")
}

 

 

 

 

다음은 어떻게 모듈을 구성할지 알아보자.

 

반응형