이 글은 누구를 위한 것인가
- 단일 모듈 앱이 커져서 빌드 시간이 5분을 넘는 팀
- 멀티 모듈로 전환하고 싶지만 어떻게 시작해야 할지 모르는 팀
- 팀원이 10명 이상으로 코드 충돌이 많아지는 Android 팀
들어가며
앱이 커질수록 단일 모듈의 빌드 시간과 코드 충돌이 증가한다. 멀티 모듈은 기능별로 코드를 분리하고, 변경된 모듈만 재빌드해서 빌드 속도를 3-5배 높인다.
이 글은 bluefoxdev.kr의 Android 아키텍처 가이드 를 참고하여 작성했습니다.
1. 멀티 모듈 아키텍처 설계
[모듈 구조]
:app
진입점, DI 통합, Navigation 그래프
feature 모듈 (기능별 분리):
:feature:home
:feature:search
:feature:checkout
:feature:profile
각 화면/기능 담당, UI + ViewModel
core 모듈 (공통 기반):
:core:ui 공통 컴포넌트, 테마
:core:network Retrofit, OkHttp 설정
:core:database Room, 공통 DAO
:core:common 유틸, 확장 함수
data 모듈 (데이터 레이어):
:data:user 사용자 Repository
:data:product 상품 Repository
:data:order 주문 Repository
[의존성 방향 규칙]
feature → core, data (허용)
data → core (허용)
core → data (금지)
feature → feature (금지 — 순환 의존)
app → feature, core, data (허용)
[빌드 속도 최적화]
모듈 분리: 변경된 모듈만 재컴파일
병렬 빌드: org.gradle.parallel=true
캐시: org.gradle.caching=true
빌드 타입별 최소화: debug에서 ProGuard 비활성화
2. 멀티 모듈 설정
// settings.gradle.kts
include(":app")
include(":feature:home")
include(":feature:search")
include(":feature:checkout")
include(":core:ui")
include(":core:network")
include(":core:common")
include(":data:product")
include(":data:order")
// gradle/libs.versions.toml (버전 카탈로그)
# gradle/libs.versions.toml
[versions]
kotlin = "2.0.0"
androidGradlePlugin = "8.4.0"
compose = "2024.06.00"
hilt = "2.51.1"
retrofit = "2.11.0"
room = "2.6.1"
[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
// build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt
// 공통 빌드 설정을 Convention 플러그인으로 공유
import org.gradle.api.Plugin
import org.gradle.api.Project
class AndroidFeatureConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.android.library")
apply("org.jetbrains.kotlin.android")
apply("com.google.dagger.hilt.android")
apply("org.jetbrains.kotlin.kapt")
}
// 공통 Android 설정
extensions.configure<com.android.build.gradle.LibraryExtension> {
compileSdk = 35
defaultConfig { minSdk = 24 }
}
// 공통 의존성
val libs = extensions.getByType<org.gradle.api.artifacts.VersionCatalogsExtension>()
.named("libs")
dependencies.apply {
add("implementation", libs.findLibrary("hilt.android").get())
add("kapt", libs.findLibrary("hilt.compiler").get())
}
}
}
}
// feature:checkout/build.gradle.kts
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
id("my.android.feature") // Convention 플러그인 적용
}
android {
namespace = "com.myapp.feature.checkout"
}
dependencies {
implementation(project(":core:ui"))
implementation(project(":core:common"))
implementation(project(":data:order"))
implementation(project(":data:product"))
}
// 모듈 간 통신: Navigation
// feature:home → feature:checkout 이동
// feature가 직접 feature에 의존하지 않고 route string 사용
object NavigationRoutes {
const val CHECKOUT = "checkout/{orderId}"
fun checkout(orderId: String) = "checkout/$orderId"
}
// app 모듈에서 Navigation 그래프 통합
// NavHost에서 모든 feature의 navGraph 합성
// Hilt: 모듈별 DI 설정
// data:order/src/main/kotlin/di/OrderModule.kt
@Module
@InstallIn(SingletonComponent::class)
abstract class OrderModule {
@Binds
abstract fun bindOrderRepository(impl: OrderRepositoryImpl): OrderRepository
companion object {
@Provides
@Singleton
fun provideOrderApi(retrofit: Retrofit): OrderApi =
retrofit.create(OrderApi::class.java)
}
}
# gradle.properties
# 빌드 성능 최적화
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC
kotlin.incremental=true
마무리
멀티 모듈화는 팀 규모 10명, 빌드 시간 3분+ 부터 고려하라. 처음 분리는 어렵지만 Convention 플러그인으로 공통 설정을 관리하면 신규 모듈 추가가 쉬워진다. 가장 중요한 규칙: feature 모듈은 다른 feature 모듈에 의존하지 않는다 — 모듈 간 이동은 Navigation route string으로.