1. Version Catalog
- 버전 카탈로그는 의존성 버전과 라이브러리를 중앙에서 관리할 수 있게 해주는 기능
root project 의 gradle 폴더 -> libs.versions.toml 파일에 정의
- versions : 라이브러리 버전
- libraries : 라이브러리 의존성
- bundles : 라이브러리를 묶어서 한 번에 선언
- plugins: 어떤 플러그인을 사용하는지
[versions]
kotlin = "2.0.0"
hilt = "2.51"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "kotlin" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
사용하는 방법
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.hilt.android)
}
2. Convention Module
- 모듈 간 중복되는 gradle 설정을 하나의 공통 규칙으로 추출한 것 . 규칙을 정의한 다음 해당 컨벤션을 필요한 모듈에 적용함으로써 보다 간편하게 빌드 관리를 수행할 수 있음.
3. Build-logic
- Build-logic 모듈은 공통 빌드 스크립트를 포함해 빌드 구성 자체도 모듈화.
생성 방법
먼저 build-logic 모듈을 생성하고 (Android Library로 생성) , 안에 convention 모듈을 생성한다. (java or kotlin library로 생성)
2. build-logic 모듈의 settings.kts 수정
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
versionCatalogs{
create("libs"){
// 작성한 버전 카탈로그 toml 파일을 가져와준다
from(files("../gradle/libs.versions.toml"))
}
}
}
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
rootProject.name = "build-logic"
include(":convention")
3. root 수준의 settings.gradle.kts 에서 includeBuild("build-logic") 추가
이후 include(":build-logic"), include(":build-logic:convention") 는 제거
pluginManagement {
includeBuild("build-logic")
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
4. custom gradle plugin 구현
gradle plugin을 커스텀화하면 의존성을 grouping하여 필요한 의존성을 한번에 설정 가능하다
AndroidCompose.kt , KotlinAndroid.kt , ProjectExtensions.kt 파일은 보통 컨벤션 plugin 내부에서 중복설정을 피하고 코드 재사용하기 위해 따로 정의해두는 유틸 함수 파일이다.
// AndroidCompose.kt
internal fun Project.configureAndroidCompose(
commonExtension: CommonExtension<*,*,*,*,*,*>
) {
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
commonExtension.apply {
buildFeatures.compose = true
composeOptions {
kotlinCompilerExtensionVersion = libs.findVersion("compose.compiler").get().requiredVersion
}
}
dependencies {
"api"(platform(libs.findLibrary("androidx.compose.bom").get()))
"implementation"(libs.findBundle("compose").get())
"debugImplementation"(libs.findBundle("compose.debug").get())
}
}
//KotlinAndroid.kt
internal fun Project.configureKotlinAndroid(
commonExtension: CommonExtension<*,*,*,*,*,*>
) {
commonExtension.apply {
compileSdk = 34
defaultConfig {
minSdk = 26
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
}
}
fun CommonExtension<*,*,*,*,*,*>.kotlinOptions(block:KotlinJvmOptions.()->Unit) {
(this as ExtensionAware).extensions.configure("kotlinOptions",block)
}
//ProjectExtensions.kt
val Project.libs: VersionCatalog
get() = extensions.getByType<VersionCatalogsExtension>().named("libs")
커스텀 플러그인 구현
//AndroidApplicationComposeConventionPlugin
class AndroidApplicationComposeConventionPlugin:Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("com.android.application")
val extension = extensions.getByType<BaseAppModuleExtension>()
configureAndroidCompose(extension)
}
}
}
//AndroidApplicationConventionPlugin
internal class AndroidApplicationConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.android.application")
apply("org.jetbrains.kotlin.android")
}
extensions.configure<ApplicationExtension> {
configureKotlinAndroid(this)
}
}
}
}
//AndroidHiltConventionPlugin
class AndroidHiltConventionPlugin:Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.google.devtools.ksp")
apply("dagger.hilt.android.plugin")
}
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
dependencies {
"implementation"(libs.findLibrary("hilt.android").get())
"ksp"(libs.findLibrary("hilt.compiler").get())
}
}
}
}
//AndroidLibraryComposeConventionPlugin
class AndroidLibraryComposeConventionPlugin:Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("com.android.library")
val extension = extensions.getByType<LibraryExtension>()
configureAndroidCompose(extension)
}
}
}
//AndroidLibraryConventionPlugin
class AndroidLibraryConventionPlugin:Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.android.library")
apply("org.jetbrains.kotlin.android")
apply("kotlin-parcelize")
}
extensions.configure<LibraryExtension> {
configureKotlinAndroid(this)
defaultConfig.targetSdk = 35
defaultConfig {
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
viewBinding.enable = true
buildTypes {
getByName("release") {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
}
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
dependencies{
"implementation"(libs.findLibrary("junit").get())
}
}
}
}
//FeatureConventionPlugin
class FeatureConventionPlugin: Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("todolist.plugin.android.library")
apply("todolist.plugin.hilt")
}
val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
dependencies {
"implementation"(libs.findLibrary("androidx.appcompat").get())
"implementation"(libs.findLibrary("androidx.core.ktx").get())
"implementation"(libs.findLibrary("androidx.hilt.navigation.compose").get())
"implementation"(libs.findLibrary("androidx.lifecycle.runtime.ktx").get())
"implementation"(libs.findLibrary("androidx.lifecycle.viewmodel.ktx").get())
"implementation"(libs.findLibrary("androidx.lifecycle.viewmodel.compose").get())
"implementation"(libs.findLibrary("androidx.lifecycle.runtime.compose").get())
"implementation"(libs.findLibrary("coroutines.android").get())
"implementation"(libs.findLibrary("coroutines.core").get())
}
}
}
}
//JavaLibraryConventionPlugin
class JavaLibraryConventionPlugin: Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("java-library")
apply("org.jetbrains.kotlin.jvm")
}
extensions.configure<JavaPluginExtension>
{
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
extensions.configure<KotlinProjectExtension>
{
jvmToolchain(17)
}
}
}
}
나는 이렇게 총 7개의 커스텀 플러그인을 작성했다.
그 다음 최종적으로 플러그인을 등록해준다.
5. build.gradle 에 플러그인 등록
id : 내가 외부에서 사용 할 이름 정의
implementationClass: 작성한 플러그인 클래스명
//build.gradle.kts(:build-logic:convention)
plugins {
`kotlin-dsl`
}
group = "com.example.buildlogic"
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
compileOnly(libs.kotlin.gradle.plugin)
compileOnly(libs.android.gradle.plugin)
compileOnly(libs.compose.compiler.extension)
compileOnly(libs.ksp.gradlePlugin)
}
gradlePlugin {
plugins {
register("AndroidApplicationPlugin") {
id = "todolist.plugin.application"
implementationClass = "AndroidApplicationConventionPlugin"
}
register("AndroidApplicationComposePlugin") {
id = "todolist.plugin.application.compose"
implementationClass = "AndroidApplicationComposeConventionPlugin"
}
register("JavaLibraryPlugin") {
id = "todolist.plugin.java.library"
implementationClass = "JavaLibraryConventionPlugin"
}
register("AndroidHiltPlugin") {
id = "todolist.plugin.hilt"
implementationClass = "AndroidHiltConventionPlugin"
}
register("AndroidLibraryPlugin") {
id = "todolist.plugin.android.library"
implementationClass = "AndroidLibraryConventionPlugin"
}
register("AndroidLibraryComposePlugin") {
id = "todolist.plugin.android.library.compose"
implementationClass = "AndroidLibraryComposeConventionPlugin"
}
register("FeaturePlugin") {
id = "todolist.plugin.feature"
implementationClass = "FeatureConventionPlugin"
}
}
}
이제 사용을 해보자!
현재 나의 프로젝트 모듈 구조는 아래와 같이 구성되어 있다.
예를 들어 core 모듈의 build.gradle.kts 파일 변경 코드이다
plugins {
id("todolist.plugin.android.library")
id("todolist.plugin.hilt")
}
android {
namespace = "com.example.core"
}
이렇게 간단하게 작성해주면 끝!! 정말 쉽다. 그 많던 코드가 많이 간소화 된걸 볼 수 있다.
'안드로이드' 카테고리의 다른 글
[안드로이드] 멀티모듈, 컴포즈UI, Hilt, Navigation 사용해서 간단한 게시판 프로젝트 만들기 (2) | 2025.06.12 |
---|---|
[안드로이드] Hilt @Qualifier란 ? (0) | 2022.10.31 |
[안드로이드] StateFlow 알아보기 (0) | 2022.10.19 |
[안드로이드] mvvm 패턴 (0) | 2022.10.16 |
[안드로이드] Hilt - 2 (@Binds vs @Provides) (0) | 2022.10.14 |