모듈에는 build.gradle이라는 파일이 포함되어 있다.
멀티 모듈을 사용하기 위해 모듈을 생성하면 따로 추가하지 않아도 build.gradle 파일이 포함되어 생성되며, 추가 된 모듈은 settings.gradle 파일에 include: 모듈 형식으로 자동으로 추가된다.
Gradle - Groovy 언어를 기반으로 한 오픈소스 빌드 도구
빌드도구 - 애플리케이션 생성을 자동화 하기 위한 프로그램
빌드 과정을 간단히 요약하자면 다음과 같다.
- settings.gradle이 먼저 실행되고, 정의 된 각각의 모듈에 대한 Project 인스턴스를 생성한다.
- 이후 각 모듈의 Project 인스턴스를 통해 빌드 설정을 진행하게 되는데, Project 인스턴스를 사용하는 부분이 바로 모듈 내에 있는 build.gradle이다.
멀티 모듈을 위해 모듈을 여러개 생성하게 되면 build.gradle(Module) 파일이 모듈 개수만큼 생성되게 되는데, 그만큼 build.gradle 파일 내에 중복되는 코드들이 많아지게 된다.
이러한 공통된 코드들은 build-logic을 통해 관리할 수 있다.
1. 모듈 생성

2. 기본 세팅
settings.gradle(Project)
pluginManagement {
includeBuild("build-logic") // 추가
...
}
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") // 추가
// include ':build-logic' 제거
- includeBuild(): 독립적인 Gradle 빌드를 복합 빌드로 통합하는 데 사용
- enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
// 기존
implementation(project(":feature:main"))
// enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 설정 후
implementation(projects.feature.main)
build-logic 모듈
- settings.gradle
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
include(":convention")
버전 관리를 libs.versions.toml를 통해 하기 위한 설정
- gradle.properties
org.gradle.parallel=true // 병렬 빌드 활성화, 빌드 속도 향상
org.gradle.caching=true // 캐싱 활성화, 미변경 작업 캐시에서 가져옴
org.gradle.configureondemand=true // 필요한 프로젝트만 구성, 초기화 속도 최적화
build-logic:convention
convention 모듈 생성

build.gradle
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
`kotlin-dsl`
}
group = "com.project.convention"
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
compileOnly(libs.android.tools.build.gradle.plugin)
compileOnly(libs.kotlin.gradle.plugin)
compileOnly(libs.ksp.gradlePlugin)
//compileOnly(libs.room.gradlePlugin)
}
gradlePlugin {
plugins {
// 추후 작성
}
}
compileOnly() : 컴파일 과정에서만 필요
// libs.versions.toml
[libraries]
...
android-tools-build-gradle-plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "agp" }
kotlin-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
ksp-gradlePlugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
3. Convention 작성
convention에서는 각 모듈에서 사용할 공통 플러그인과 플러그인에서 사용하는 공통 코드들을 정의한다.
기존 app 모듈의 build.gradle을 다음과 같이 플러그인으로 전환할 수 있다.

configureAndroid는 아래와 같이 구성되어 있다.
internal fun Project.configureAndroid(commonExtension: CommonExtension<*, *, *, *, *, *>) {
commonExtension.apply {
compileSdk = 34
defaultConfig {
minSdk = 28
testInstrumentationRunner = "com.google.samples.modularization.testing.HiltTestRunner"
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
// kotlinOptions
tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
val warningsAsErrors: String? by project
allWarningsAsErrors.set(warningsAsErrors.toBoolean())
freeCompilerArgs.addAll(
listOf(
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
),
)
}
}
}
}
위처럼 플러그인을 정의하고 난 뒤엔 convention 모듈의 build.gradle의 gradle.plugin() 내에서 등록할 수 있다.
libs.versions.toml에서 커스텀 플러그인 정의한 뒤
[plugins]
custom-plugin-feature = { id = "custom.plugin.feature", version = "unspecified" }
convention 모듈의 build.gradle에서 다음과 같이 등록할 수 있다.
gradlePlugin {
plugins {
register("CustomPluginFeature") { // register name
id = "custom.plugin.feature" // libs.versions.toml에서 정의한 플러그인 id
implementationClass = "FeatureConventionPlugin" //플러그인 클래스 이름
}
}
}
4. 모듈에서 사용
이처럼 등록한 플러그인을 다른 모듈의 build.gradle에서 불러와 사용할 수 있다.
build.gradle(:feature)
plugins {
alias(libs.plugins.custom.plugin.feature) // build-logic에서 등록한 플러그인
}
android {
namespace = "com.project.feature.calendar"
}
AndroidApplicationConventionPlugin
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import com.project.convention.configureAndroid
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
class AndroidApplicationConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.android.application")
apply("org.jetbrains.kotlin.android")
apply("custom.plugin.hilt")
}
extensions.configure<BaseAppModuleExtension> {
configureAndroid(commonExtension = this)
}
}
}
}
AndroidLibraryConventionPlugin
import com.android.build.gradle.LibraryExtension
import com.project.convention.configureAndroid
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
class AndroidLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.android.library")
apply("org.jetbrains.kotlin.android")
}
extensions.configure<LibraryExtension> {
configureAndroid(this)
}
}
}
}
HiltConventionPlugin
import com.project.convention.getLibrary
import com.project.convention.implementation
import com.project.convention.ksp
import com.project.convention.libs
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies
class HiltConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.google.devtools.ksp")
apply("dagger.hilt.android.plugin")
}
dependencies {
implementation(libs.getLibrary("dagger.hilt.android"))
ksp(libs.getLibrary("hilt.compiler"))
}
}
}
}
JvmLibraryConventionPlugin
import com.project.convention.configureKotlinJvm
import org.gradle.api.Plugin
import org.gradle.api.Project
class JvmLibraryConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("org.jetbrains.kotlin.jvm")
}
configureKotlinJvm()
}
}
}
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
internal fun Project.configureKotlinJvm() {
extensions.configure<JavaPluginExtension> {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
val warningsAsErrors: String? by project
allWarningsAsErrors.set(warningsAsErrors.toBoolean())
freeCompilerArgs.addAll(
listOf(
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-Xconsistent-data-class-copy-visibility",
),
)
}
}
}
RoomConventionPlugin
import com.project.convention.getLibrary
import com.project.convention.implementation
import com.project.convention.ksp
import com.project.convention.libs
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies
class RoomConventionPlugin: Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.google.devtools.ksp")
}
dependencies {
implementation(libs.getLibrary("androidx.room.ktx"))
implementation(libs.getLibrary("room.runtime"))
ksp(libs.getLibrary("androidx.room.compiler"))
}
}
}
}