멀티 모듈 프로젝트의 경우 gradle파일이 수십개, 수백개가 될수도 있다.
이 경우 모든 gradle마다 설정을 해주는건 매우 번거롭고 유지비수 비용이 많이 드는 작업이기 떄문에
이를 해결하기 위해 build-logic 패턴을 사용해서 공통된 gradle 설정을 관리한다.
프로젝트 뷰 모드를 Project로 바꾸고 root 폴더에 build-logic 폴더를 새로 생성한다.

이후 프로젝트의 settings.gradle.kts에서 build-logic을 추가한다.
pluginManagement {
includeBuild("build-logic")
}
pluginManagement {
repositories {
google()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
rootProject.name = "build-logic"
include(":convention")
build-logic은 빌드 스크립트/플러그인 역할을 한다.
우리가 gradle에 alias(libs.plugins.plugin) 와 같은 형태로 플러그인을 추가하면 gradle은 이를 어디서 가져올지 확인한다.
이는 settings의 pluginManagement에 선언되어 있는데,
build-logic 폴더 내에 settings.gradle.kts와 gradle.properties를 추가해주고,
includeBuild로 추가해주면서 gradle이 여기서 플러그인을 가져올 수 있도록 해준다.
또한 build-logic에서도 versionCatalog를 사용하기 위해 설정을 추가했다.
이렇게 하면 일단 사전 작업은 끝났고, 다음으로 실제로 gradle설정을 작성할 ConventionPlugin을 만들어보자.
build-logic내에서 directory를 다시 생성하고, 이곳에 build.gradle.kts파일을 생성한다.
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
// 가장 먼저 추가하고 sync
plugins {
`kotlin-dsl`
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlin {
compilerOptions {
jvmTarget = JvmTarget.JVM_11
}
}
dependencies {
implementation(libs.android.gradlePlugin)
implementation(libs.kotlin.gradlePlugin)
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
}
// ConventionPlugin 파일을 versionCatalog에 추가한 plugin과 연결하고 등록한다.
// 이를 통해 다른 모듈에서 plugin형식으로 Convention적용이 가능하다.
gradlePlugin {
val conventionPlugins = listOf(
libs.plugins.sdbk.application.get().pluginId to "kr.sdbk.convention.ApplicationConventionPlugin",
libs.plugins.sdbk.compose.get().pluginId to "kr.sdbk.convention.ComposeConventionPlugin",
libs.plugins.sdbk.feature.get().pluginId to "kr.sdbk.convention.FeatureConventionPlugin",
libs.plugins.sdbk.library.android.get().pluginId to "kr.sdbk.convention.AndroidLibraryConventionPlugin",
libs.plugins.sdbk.library.jvm.get().pluginId to "kr.sdbk.convention.JvmLibraryConventionPlugin"
)
plugins {
conventionPlugins.forEach { registerPlugin(it) }
}
}
fun NamedDomainObjectContainer<PluginDeclaration>.registerPlugin(pair: Pair<String, String>) {
val (pluginId, className) = pair
register(pluginId) {
id = pluginId
implementationClass = className
}
}
gradle파일까지 작성했으면 실제 Convnetion파일을 작성할텐데,
사용하는 함수들은 아래처럼 따로 추가해줬다.
internal fun DependencyHandler.implementation(dependencyNotation: Any): Dependency? =
add("implementation", dependencyNotation)
internal val Project.libs get() =
the<LibrariesForLibs>()
internal fun Project.applyPlugins(vararg plugins: String) =
plugins.forEach { apply(plugin = it) }
internal fun Project.implementModuleDirectory(dirName: String) {
dependencies {
rootDir.resolve(dirName).listFiles()
?.filter { it.isDirectory && file("${it.path}/$GRADLE_FILE").exists() }
?.forEach { implementation(project(":$dirName:${it.name}")) }
}
}
여기서 libs는 gradle에서 versionCatalog를 사용하는것처럼 해주는데,
LibrariesForLibs를 사용하기 위해선 gradle에 아래 의존성을 추가해줘야한다.
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
https://github.com/gradle/gradle/issues/15383
Make generated type-safe version catalogs accessors accessible from precompiled script plugins · Issue #15383 · gradle/gradle
Context In a similar way to #15382, we want to make the version catalogs accessible to precompiled script plugins. Naively, one might think that it's easier to do because precompiled script plugins...
github.com
이제 app모듈에 대한 Convetion을 작성한다.
class ApplicationConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with (target) {
// 사용할 플러그인 적용
applyPlugins(
libs.plugins.android.application.get().pluginId,
libs.plugins.kotlin.android.get().pluginId,
libs.plugins.sdbk.compose.get().pluginId
)
// gradle의 android {} 블록이라고 생각하면 편함
extensions.configure<ApplicationExtension> {
kotlinAndroid(this)
defaultConfig.testInstrumentationRunner = ConfigConstants.ANDROID_TEST_RUNNER
}
// 앱 내의 core, feature 디렉토리 내의 모듈 의존성 추가
implementModuleDirectory(DIR_CORE)
implementModuleDirectory(DIR_FEATURE)
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
}
}
}
}
마지막으로 위에서 사용하는 ComposeConvention까지 추가해보자
class ComposeConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with (target) {
applyPlugins(libs.plugins.compose.get().pluginId)
// compose convnetion은 android application 또는 android library에서만 사용
// 모듈 타입에 맞는 extension을 반환한다.
val extension = when {
plugins.hasPlugin(libs.plugins.android.application.get().pluginId) -> extensions.findByType<ApplicationExtension>()
plugins.hasPlugin(libs.plugins.kotlin.android.get().pluginId) -> extensions.findByType<LibraryExtension>()
else -> throw GradleException("This plugin should be applied either an application or library")
}
extension?.apply {
buildFeatures {
compose = true
}
}
dependencies {
implementation(platform(libs.compose.bom))
implementation(libs.bundles.compose)
}
}
}
}
모듈에서 실제 사용
plugins {
alias(libs.plugins.sdbk.application)
}
android {
namespace = "kr.sdbk.sparktalk"
defaultConfig {
applicationId = "kr.sdbk.sparktalk"
targetSdk = libs.versions.targetSdk.get().toInt()
versionCode = libs.versions.versionCode.get().toInt()
versionName = libs.versions.versionName.get()
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
전체 코드는 github에서 확인
https://github.com/ronsze/SparkTalk/tree/dev
GitHub - ronsze/SparkTalk
Contribute to ronsze/SparkTalk development by creating an account on GitHub.
github.com
'안드로이드 > 개발관련(Kotlin)' 카테고리의 다른 글
| [Android] 라이브러리 없이 직렬화/역직렬화 구현 (0) | 2025.11.30 |
|---|---|
| [Android] 라이브러리 없이 클라이언트에서 테스트 서버 구현 (0) | 2025.11.30 |
| [Android] HttpsURLCoonection을 사용한 인터넷 통신 (0) | 2025.11.30 |
| [Android] jvmTarget 지정 방식이 deprecated 된 이유 (0) | 2025.11.29 |
| [Android] TYPESAFE_PROJECT_ACCESSORS (1) | 2024.10.09 |