멀티 모듈 만들어보기

Kyu hyunSung·2025년 5월 12일

Android

목록 보기
8/10
post-thumbnail

Android 앱 모듈화 가이드
Now in Android

  • 유지보수가 편하도록 코드베이스 구조화 하는 방법.

주로 덩치가 큰 앱을 만들때 나누는 방법이다.

예시 : architecture-samples


1. 만드는 방법

본인은 클린 아키텍처 기반해서 만들거다

여기서 미리 생각해야할 것들이 몇가지 정도 있음

모듈 선택인데

  1. Android Application (앱 진입점)
  2. Android Library (대부분의 모듈)
  3. Java/Kotlin Library (순수 로직)
  4. Dynamic Feature (큰 앱만)
  5. Benchmark (성능 중요한 앱만)

정도 씀 여기서

안드로이드 의존성이 있으면

Android Library

아니면

Java/Kotlin Librar로 만들면됨.

가장 기본적으로는 이정도 만들어지긴함

your-project/
├── app/                     ← Android Application
├── core/
│   ├── data/               ← Android Library (Repository)
│   ├── network/            ← Android Library (API)
│   ├── domain/             ← Kotlin Library (UseCase)
│   └── ui/                 ← Android Library (Compose)
└── feature/
    └── main/               ← Android Library (ViewModel + Screen)

1-1. File -> New -> New Module..

  • 참고로 모듈안에 모듈을 만들고싶다면

대충 뭐 이렇ㄱ하면됨

모듈타입설명
:appAndroid Application앱 진입점, 메인 네비게이션 설정, DI 구성
:domainKotlin Library비즈니스 로직, 엔티티, 유스케이스, 리포지토리 인터페이스
:dataAndroid Library리포지토리 구현, 데이터 소스, 로컬/원격 데이터 접근
:presentationAndroid LibraryMVI 아키텍처 컴포넌트, ViewModel, UI 상태 관리, 이벤트 처리
:core:uiAndroid Library재사용 가능한 UI 컴포넌트, 테마 정의
:core:commonKotlin Library유틸리티, 확장 함수, 공통 코드
:feature:authAndroid Library인증 관련 화면 및 로직
:feature:onboardingAndroid Library온보딩 화면
:feature:salesAndroid Library매출 관리 기능
:feature:menuAndroid Library메뉴 관리 기능
:feature:orderAndroid Library주문 관리 기능
:feature:staffAndroid Library직원 관리 기능
  • 본인은 이런 기능들이 필요해서 이렇게 구조를 짰다.

  • settings.gradle.kts에 가면 저렇게 나와있을거임
pluginManagement {
    repositories {
        google {
            content {
                includeGroupByRegex("com\\.android.*")
                includeGroupByRegex("com\\.google.*")
                includeGroupByRegex("androidx.*")
            }
        }
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "Barrion"
include(":app")

// 여기에 넣을 모듈 추가
include(":domain")
include(":data")
include(":presentation")
include(":core:ui")
include(":core:common")
include(":feature:auth")
include(":feature:onboarding")
include(":feature:sales")
include(":feature:menu")
include(":feature:order")
include(":feature:staff")

1-2. Convention Plugin

모듈을 만든거 때문에 수많은 Build.gradle.kts가 생성됐을것이다.

어떤건 Gradle이 있고 없고 일일이 체크도 못하고 귀찮다.

공통적으로 관리하는 방법은 두가지가 있는데

  1. buildSrc 디렉토리로 로직 관리
  2. build-logic:convention 모듈 생성

2번대로 해볼건데 방법은 일단 위 사진처럼 모듈을 만들고

아래 두개의 코드를 만들어서 넣어주셈

  • build.gradle.kt(convention)

@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
// 이 주석은 KTIJ-19369 이슈가 수정될 때까지 DSL_SCOPE_VIOLATION 경고를 억제함

plugins {
    `kotlin-dsl` // Kotlin DSL 플러그인을 적용해 Gradle 스크립트를 Kotlin으로 작성할 수 있게 함
}

java {
    sourceCompatibility = JavaVersion.VERSION_17 // 소스 코드 호환성을 Java 17로 설정
    targetCompatibility = JavaVersion.VERSION_17 // 컴파일된 바이트코드 호환성을 Java 17로 설정
}

dependencies { //원하는 플러그인 의존성
    // 모든 Gradle 플러그인 의존성을 한 번에 추가
    compileOnly(libs.android.gradlePlugin) // Android Gradle 플러그인 의존성 (현재 주석 처리됨)
    compileOnly(libs.kotlin.gradlePlugin) // Kotlin Gradle 플러그인 의존성 (현재 주석 처리됨)
    compileOnly(libs.ksp.gradlePlugin) // KSP(Kotlin Symbol Processing) Gradle 플러그인 의존성 (현재 주석 처리됨)
}
  • 참고로 libs.version.toml에 있는 gradle 의존성과 이름이 일치해야함
[versions]
agp = "8.7.3"
kotlin = "2.0.0"
ksp = "2.0.0-1.0.21"

[libraries]
android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "agp" }
kotlin-gradlePlugin = { 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" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }

[bundles]
gradle-plugins = ["android-gradlePlugin", "kotlin-gradlePlugin", "ksp-gradlePlugin"]

본인은 번들로 하나로 묶어서 사용할 예정이다.

  • setting.gradles.kts(build-logic)
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
    versionCatalogs {
        create("libs") {
            from(files("../gradle/libs.versions.toml"))
        }
    }
}

rootProject.name = "build-logic"
include(":convention")

아마 이 파일을 생성하면

이렇게 뜰 수도 있는데 별 문제 없으니 걱정ㄴ

IDE 기능 못쓰는거뿐임

원래 있던 기존의 setting.gradle.kts(Project Settings) 를 수정해주자

// Gradle 플러그인 관리 설정 (빌드 초기 단계에서 처리됨)
pluginManagement {
   // build-logic 디렉토리를 복합 빌드로 포함
   // 이를 통해 build-logic에서 정의한 Convention 플러그인을 프로젝트에서 사용 가능
   includeBuild("build-logic")
   
   // 플러그인을 찾을 저장소 설정
   repositories {
       // Google 저장소 - 특정 그룹만 포함하도록 제한
       google {
           content {
               includeGroupByRegex("com\\.android.*") // Android 플러그인
               includeGroupByRegex("com\\.google.*")  // Google 플러그인
               includeGroupByRegex("androidx.*")      // AndroidX 플러그인
           }
       }
       mavenCentral() // Maven Central 저장소
       gradlePluginPortal() // Gradle 플러그인 포털
   }
}

// 의존성 해결 방법 관리 설정
dependencyResolutionManagement {
   // 프로젝트별 저장소 설정을 금지하고 이 설정만 사용하도록 강제
   repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
   
   // 의존성을 찾을 저장소 설정
   repositories {
       google()      // Android/Google 라이브러리용
       mavenCentral() // 대부분의 오픈소스 라이브러리용
   }
}

// 루트 프로젝트 이름 설정
rootProject.name = "Barrion"

// 프로젝트에 포함할 모듈 정의
include(":app")               // 앱 모듈 (애플리케이션 진입점)
include(":domain")            // 도메인 모듈 (비즈니스 로직, 엔티티)
include(":data")              // 데이터 모듈 (저장소, API 통신)
include(":presentation")      // 프레젠테이션 모듈 (UI 공통 로직)

// 코어 모듈 (여러 모듈에서 공유되는 기능)
include(":core:ui")           // UI 공통 요소 (컴포넌트, 테마)
include(":core:common")       // 공통 유틸리티, 확장 함수 등

// 기능별 모듈
include(":feature:auth")      // 인증 기능
include(":feature:onboarding") // 온보딩 기능
include(":feature:sales")     // 매출 관리 기능
include(":feature:menu")      // 메뉴 관리 기능
include(":feature:order")     // 주문 관리 기능
include(":feature:staff")     // 직원 관리 기능

// build-logic:convention은 복합 빌드로 포함되었으므로 include에서 제외
//include(":build-logic:convention")

중요한건

IncludeBuild VS Include

자세한건 스택오버플로우 형님들 글 참고

확장함수 추가해야함

  • Const.kt (KotlinAndroid에 넣어도됨)
package com.example.convention

import org.gradle.api.JavaVersion

/**
 * 프로젝트 전반에서 사용할 공통 상수를 정의하는 객체
 * 컴파일 SDK, 최소 SDK, 타겟 SDK 및 Java 버전과 같은 설정을 중앙에서 관리
 */

object Const {
    const val COMPILE_SDK = 35      // 컴파일 SDK 버전
    const val MIN_SDK = 21          // 최소 지원 SDK 버전
    const val TARGET_SDK = 35       // 타겟 SDK 버전
    val JAVA_VERSION = JavaVersion.VERSION_17  // 사용할 Java 버전
    const val NAMESPACE_PREFIX = "com.example.barrion"  // 패키지명 프리픽스
}
  • KotlinAndroid.kt
package com.example.convention

import com.android.build.api.dsl.CommonExtension
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.api.plugins.ExtensionAware
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions

/**
 * 안드로이드 프로젝트의 Kotlin 관련 설정을 구성하는 확장 함수
 * 애플리케이션 및 라이브러리 모듈에서 재사용 가능
 * @param commonExtension 안드로이드 빌드 설정을 포함하는 확장 객체
 */
internal fun Project.configureKotlinAndroid(
    commonExtension: CommonExtension<*, *, *, *, *, *>,
) {
    // 올바른 방식으로 VersionCatalogsExtension을 가져옴
    val libs = extensions.getByType(VersionCatalogsExtension::class.java).named("libs")

    commonExtension.apply {
        // 컴파일 SDK 버전 설정
        compileSdk = Const.COMPILE_SDK

        defaultConfig {
            // 최소 SDK 버전 설정
            minSdk = Const.MIN_SDK
            // 테스트 인스트루먼테이션 러너 설정
            testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
            // 벡터 드로어블 지원 라이브러리 사용 설정
            vectorDrawables {
                useSupportLibrary = true
            }
        }

        // 빌드 타입 설정 (release 빌드에 대한 설정)
        buildTypes {
            getByName("release") {
                // 코드 난독화 비활성화 (필요에 따라 활성화할 수 있음)
                isMinifyEnabled = false
                // ProGuard 규칙 파일 설정
                proguardFiles(
                    getDefaultProguardFile("proguard-android-optimize.txt"),
                    "proguard-rules.pro"
                )
            }
        }

        // Java 컴파일 옵션 설정
        compileOptions {
            // 소스 및 타겟 호환성을 Java 17로 설정
            sourceCompatibility = Const.JAVA_VERSION
            targetCompatibility = Const.JAVA_VERSION
        }

        // Kotlin 컴파일 옵션 설정 (아래에서 정의된 확장 함수 사용)
        kotlinOptions {
            // JVM 타겟을 Java 17로 설정
            jvmTarget = Const.JAVA_VERSION.toString()
        }

        // 패키징 옵션 설정
        packaging {
            resources {
                // META-INF의 특정 파일들을 패키징에서 제외
                // 일반적으로 여러 라이브러리에서 중복되는 라이센스 파일 등을 제외하기 위함
                excludes += "/META-INF/{AL2.0,LGPL2.1}"
            }
        }
    }
}

/**
 * CommonExtension에 대한 확장 함수로, kotlinOptions 설정에 접근할 수 있게 함
 * CommonExtension은 직접적으로 kotlinOptions를 제공하지 않기 때문에 필요
 * @param block KotlinJvmOptions를 구성하는 람다 함수
 */
internal fun CommonExtension<*, *, *, *, *, *>.kotlinOptions(
    block: KotlinJvmOptions.() -> Unit
) {
    // ExtensionAware 인터페이스를 통해 'kotlinOptions' 확장에 접근하고 구성
    (this as ExtensionAware).extensions.configure("kotlinOptions", block)
}
  • AndroidCompose.kt
package com.example.convention

import com.android.build.api.dsl.CommonExtension
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.getByType

/**
 * Compose 관련 설정을 구성하는 확장 함수
 * 이 함수는 애플리케이션 및 라이브러리 모듈 모두에서 재사용 가능
 * @param commonExtension 안드로이드 빌드 설정을 포함하는 확장 객체
 */
internal fun Project.configureAndroidCompose(
    commonExtension: CommonExtension<*, *, *, *, *, *>,
) {
    // 올바른 방식으로 VersionCatalogsExtension을 가져옴
    val libs = extensions.getByType(VersionCatalogsExtension::class.java).named("libs")

    commonExtension.apply {
        buildFeatures {
            // Compose 사용을 활성화
            compose = true
        }

        composeOptions {
            // 버전 카탈로그에서 정의된 Compose 컴파일러 버전을 사용
            // toml 파일에 정의된 "compose-compiler" 버전을 참조
            kotlinCompilerExtensionVersion = libs.findVersion("compose-compiler").get().requiredVersion
        }
    }

    // 프로젝트에 Compose 관련 의존성 추가
    dependencies {
        // Compose BOM(Bill of Materials)을 가져와 일관된 버전의 Compose 라이브러리 사용
        // toml 파일에 정의된 "androidx-compose-bom" 참조
        val composeBom = libs.findLibrary("androidx-compose-bom").get()
        // BOM을 의존성으로 추가하여 다른 Compose 라이브러리의 버전을 일관되게 관리
        add("implementation", platform(composeBom))

        // 각 Compose 라이브러리를 의존성으로 추가
        // toml 파일에 정의된 라이브러리 ID를 사용하여 참조
        add("implementation", libs.findLibrary("androidx-ui").get())
        add("implementation", libs.findLibrary("androidx-ui-graphics").get())
        add("implementation", libs.findLibrary("androidx-ui-tooling-preview").get())
        add("implementation", libs.findLibrary("androidx-material3").get())

        // 디버그 빌드에만 필요한 의존성 추가
        add("debugImplementation", libs.findLibrary("androidx-ui-tooling").get())
        add("debugImplementation", libs.findLibrary("androidx-ui-test-manifest").get())
    }
}

추후에 디테일하게 분석 헤봄 낟 왜 이렇게 만들어야하는지는 모름

  • 컨벤션 플러그인 추가
    - 본인이 필요한 플러그인 추가해주면 됨
package com.example.convention

import com.android.build.api.dsl.ApplicationExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.getByType

/**
 * 앱 모듈용 컨벤션 플러그인
 */
class AndroidApplicationConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        // 플러그인이 적용될 때 눈에 띄는 로그 출력
        target.logger.lifecycle("🟢 ===============================================")
        target.logger.lifecycle("🟢 AndroidApplicationConventionPlugin applied to: ${target.name}")
        target.logger.lifecycle("🟢 ===============================================")

        with(target) {
            with(pluginManager) {
                apply("com.android.application")
                apply("org.jetbrains.kotlin.android")
                apply("org.jetbrains.kotlin.kapt")
                apply("androidx.navigation.safeargs.kotlin")

                // 각 플러그인이 적용되었는지 로그로 확인
                logger.lifecycle("✅ Applied: com.android.application")
                logger.lifecycle("✅ Applied: org.jetbrains.kotlin.android")
                logger.lifecycle("✅ Applied: org.jetbrains.kotlin.kapt")
                logger.lifecycle("✅ Applied: androidx.navigation.safeargs.kotlin")
            }

            // 버전 카탈로그에 접근
            val libs = extensions.getByType<VersionCatalogsExtension>().named("libs")
            logger.lifecycle("📚 Accessed version catalog: libs")

            extensions.configure<ApplicationExtension> {
                configureKotlinAndroid(this)
                logger.lifecycle("⚙️ Configured Kotlin Android settings")

                defaultConfig {
                    applicationId = "com.example.barrion"
                    targetSdk = Const.TARGET_SDK
                    versionCode = 1
                    versionName = "1.0"
                }
                logger.lifecycle("📱 Set application default config")

                buildFeatures {
                    viewBinding = true
                    dataBinding = true
                    buildConfig = true
                }
                logger.lifecycle("🛠️ Configured build features")
            }

            // 의존성 추가 시 확인 로그
            logger.lifecycle("📦 Adding dependencies...")

            // 앱 모듈 공통 의존성 추가
            dependencies {
                // 기본 의존성
                add("implementation", libs.findLibrary("androidx-core-ktx").get())
                add("implementation", libs.findLibrary("androidx-appcompat").get())
                add("implementation", libs.findLibrary("material").get())
                add("implementation", libs.findLibrary("androidx-activity").get())
                add("implementation", libs.findLibrary("androidx-constraintlayout").get())
                add("implementation", libs.findLibrary("androidx-datastore-preferences").get())
                add("implementation", libs.findLibrary("androidx-datastore-preferences-core").get())
                add("implementation", libs.findLibrary("timber").get())
                add("implementation", libs.findLibrary("androidx-viewpager2").get())
                add("implementation", libs.findLibrary("kotlinx-serialization-json").get())
                add("implementation", libs.findLibrary("kotlinx-coroutines").get())

                // 네비게이션 번들 사용
                add("implementation", platform(libs.findLibrary("okhttp-bom").get()))

                // 번들 접근 방식 수정
                val navigation = libs.findBundle("navigation")
                if (navigation.isPresent) {
                    add("implementation", navigation.get())
                    logger.lifecycle("🧩 Added navigation bundle")
                } else {
                    // 번들이 없을 경우 개별 의존성 추가
                    add("implementation", libs.findLibrary("androidx-navigation-fragment-ktx").get())
                    add("implementation", libs.findLibrary("androidx-navigation-ui-ktx").get())
                    logger.lifecycle("🧩 Added individual navigation dependencies")
                }

                // 레트로핏 번들
                val retrofit = libs.findBundle("retrofit")
                if (retrofit.isPresent) {
                    add("implementation", retrofit.get())
                    logger.lifecycle("🧩 Added retrofit bundle")
                } else {
                    // 번들이 없을 경우 개별 의존성 추가
                    add("implementation", libs.findLibrary("retrofit-core").get())
                    add("implementation", libs.findLibrary("retrofit-converter-kotlinx").get())
                    add("implementation", libs.findLibrary("okhttp").get())
                    add("implementation", libs.findLibrary("okhttp-logging").get())
                    logger.lifecycle("🧩 Added individual retrofit dependencies")
                }
            }

            // 플러그인 적용 완료 메시지
            logger.lifecycle("✅ ===============================================")
            logger.lifecycle("✅ AndroidApplicationConventionPlugin successfully applied")
            logger.lifecycle("✅ ===============================================")

            // 확인용 태스크 추가 (선택 사항)
            tasks.register("verifyApplicationPlugin") {
                doLast {
                    logger.lifecycle("🔍 Verifying AndroidApplicationConventionPlugin...")
                    logger.lifecycle("✅ AndroidApplicationConventionPlugin is correctly applied to: ${project.name}")
                }
            }
        }
    }
}
  • 컨벤션 등록

build.gradle.kts(build-logic:convention)

@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
    `kotlin-dsl`
}

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

dependencies {
    // 버전 카탈로그를 사용한 의존성 추가
    compileOnly(libs.android.gradlePlugin)
    compileOnly(libs.kotlin.gradlePlugin)
    compileOnly(libs.ksp.gradlePlugin)

    // 만약 위 방법이 작동하지 않는다면, 하드코딩된 의존성 사용:
    // compileOnly("com.android.tools.build:gradle:8.7.3")
    // compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0")
    // compileOnly("com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.0.0-1.0.21")
}

gradlePlugin {
    plugins {
        register("androidApplication") {
            id = "barrion.android.application"
            implementationClass = "com.example.convention.AndroidApplicationConventionPlugin"
        }
    }
}
  • 적용법

적용하려고하는 모듈에

plugins {
    id("적용하려는.아까만든.모듈.ID")
}
import java.util.Properties

plugins {
    // 기존 플러그인 유지
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.kotlin.compose)
    alias(libs.plugins.kotlin.serialization)
    alias(libs.plugins.hilt.android)
    alias(libs.plugins.ksp)
    alias(libs.plugins.navigation.safeargs.kotlin)
    id("org.jetbrains.kotlin.kapt")
    
    // 커스텀 플러그인 적용
    id("barrion.android.application")
}

val properties =
    Properties().apply {
        load(project.rootProject.file("local.properties").inputStream())
    }

android {
    namespace = "com.example.barrion"
    compileSdk = 35

    defaultConfig {
        applicationId = "com.example.barrion"
        minSdk = 21
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    kotlinOptions {
        jvmTarget = "11"
    }
    buildFeatures {
        viewBinding = true
        dataBinding = true
        buildConfig = true
    }
    buildFeatures {
        compose = true
    }
}

dependencies {
    // Compose 의존성
    implementation(libs.androidx.activity.compose)
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.ui)
    implementation(libs.androidx.ui.graphics)
    implementation(libs.androidx.ui.tooling.preview)
    implementation(libs.androidx.material3)
    debugImplementation(libs.androidx.ui.tooling)
    debugImplementation(libs.androidx.ui.test.manifest)
    androidTestImplementation(platform(libs.androidx.compose.bom))
    androidTestImplementation(libs.androidx.ui.test.junit4)
    
    // 기본 안드로이드 및 UI 의존성
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.lifecycle.runtime.ktx)
    implementation(libs.androidx.appcompat)
    implementation(libs.material)
    implementation(libs.androidx.activity)
    implementation(libs.androidx.constraintlayout)
    
    // 앱 특화 UI 의존성
    implementation(libs.balloon)
    implementation(libs.core.splashscreen)
    
    // 데이터 스토리지
    implementation(libs.androidx.datastore.preferences)
    implementation(libs.androidx.datastore.preferences.core)
    
    // Hilt
    implementation(libs.hilt.android)
    ksp(libs.hilt.compiler)
    ksp(libs.hilt.android.compiler)
    
    // 이미지 로딩 (Glide와 Coil)
    implementation(libs.glide)
    ksp(libs.glide.compiler)
    implementation(libs.coil)  // 필요하지 않다면 제거 가능
    
    // 유틸리티
    implementation(libs.timber)
    implementation(libs.androidx.viewpager2)
    
    // 네비게이션
    implementation(libs.bundles.navigation)
    
    // 비동기 및 직렬화
    implementation(libs.kotlinx.serialization.json)
    implementation(libs.kotlinx.coroutines)
    
    // 네트워킹
    implementation(platform(libs.okhttp.bom))
    implementation(libs.bundles.retrofit)
    
    // 테스트 의존성
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core)
}

참고로 본인은 단일 모듈일때는 코드를 이렇게 가져갔음

import java.util.Properties

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.kotlin.compose)
    alias(libs.plugins.kotlin.serialization)
    alias(libs.plugins.hilt.android)
    alias(libs.plugins.ksp)
    alias(libs.plugins.navigation.safeargs.kotlin)
    id("org.jetbrains.kotlin.kapt")
}

val properties =
    Properties().apply {
        load(project.rootProject.file("local.properties").inputStream())
    }

android {
    namespace = "com.example.barrion"
    compileSdk = 35

    defaultConfig {
        applicationId = "com.example.barrion"
        minSdk = 21
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    kotlinOptions {
        jvmTarget = "11"
    }
    buildFeatures {
        viewBinding = true
        dataBinding = true
        buildConfig = true
    }
    buildFeatures {
        compose = true
    }
}

dependencies {

    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.lifecycle.runtime.ktx)
    implementation(libs.androidx.activity.compose)
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.ui)
    implementation(libs.androidx.ui.graphics)
    implementation(libs.androidx.ui.tooling.preview)
    implementation(libs.androidx.material3)
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core)
    androidTestImplementation(platform(libs.androidx.compose.bom))
    androidTestImplementation(libs.androidx.ui.test.junit4)
    debugImplementation(libs.androidx.ui.tooling)
    debugImplementation(libs.androidx.ui.test.manifest)

    implementation(libs.balloon)

    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.appcompat)
    implementation(libs.material)
    implementation(libs.androidx.activity)
    implementation(libs.androidx.constraintlayout)
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core)

    implementation(libs.balloon)

    // DataStore
    implementation(libs.androidx.datastore.preferences)
    implementation(libs.androidx.datastore.preferences.core)

    // Hilt
    implementation(libs.hilt.android)
    ksp(libs.hilt.compiler)
    ksp(libs.hilt.android.compiler)

    // Timber
    implementation(libs.timber)

    // Glide
    implementation(libs.glide)
    ksp(libs.glide.compiler) // annotationProcessor에서 kapt로 변경

    // coil
    implementation(libs.coil)

    // ViewPager2
    implementation(libs.androidx.viewpager2)

    // navigation
    implementation(libs.bundles.navigation)

    // Kotlin Serialization
    implementation(libs.kotlinx.serialization.json)

    // Coroutines
    implementation(libs.kotlinx.coroutines)

    // Network
    implementation(platform(libs.okhttp.bom))
    implementation(libs.bundles.retrofit)

    // splashscreen
    implementation(libs.core.splashscreen)
}

1-3. 간단 설명

어떤게 들어갔냐면

  1. 기본 플러그인 적용:

    • com.android.application - 안드로이드 애플리케이션 빌드
    • org.jetbrains.kotlin.android - 코틀린 안드로이드 지원
    • org.jetbrains.kotlin.kapt - 코틀린 애노테이션 프로세싱
    • androidx.navigation.safeargs.kotlin - 네비게이션 안전 인자
  2. 공통 의존성:

    • 기본 안드로이드 라이브러리: androidx-core-ktx, androidx-appcompat, material
    • 데이터 스토리지: androidx-datastore-preferences
    • 로깅: timber
    • 네비게이션: navigation 번들
    • 비동기 처리: kotlinx-coroutines
    • 직렬화: kotlinx-serialization-json
    • 네트워킹: retrofit, okhttp 관련 번들
  3. 안드로이드 기본 설정:

    • compileSdk, minSdk, targetSdk 기본값 설정
    • 기본 테스트 인스트루먼테이션 러너 설정

정도가 들어갔음

3줄 이상 읽기 싫으면 단순하게 봐서

  • 플러그인 적용 전
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.kotlin.compose)
    // 기타 여러 플러그인...
}

android {
    // 모든 설정 직접 정의
    compileSdk = 35
    defaultConfig { /* ... */ }
    buildTypes { /* ... */ }
    // 기타 설정...
}

dependencies {
    // 약 30-50개의 의존성 직접 선언
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.appcompat)
    // 기타 수많은 의존성...
}
  • 플러그인 적용 후
plugins {
    id("barrion.android.application") // 대부분의 공통 설정 처리
    // 필요한 추가 플러그인만 선언
}

android {
    namespace = "com.example.barrion"
    
    // 앱 특화 설정만 오버라이드
    defaultConfig {
        applicationId = "com.example.barrion"
        versionCode = 1
        versionName = "1.0"
    }
    // 플러그인에서 처리하지 않는 특별한 설정만 여기에 추가
}

dependencies {
    // 앱 특화 의존성만 명시적 선언
    implementation(libs.balloon)
    implementation(libs.core.splashscreen)
}

대부분 사람들은 공통적으로 무조건 만드는건 (앱 모듈도 커스텀으로 관리함)

  1. Kotlin 및 Android 관련
  2. Kotlin 표준 라이브러리
  3. 코루틴 (대부분 공통으로 사용)
  4. AndroidX Core 및 Lifecycle (기본 컴포넌트용)

그 외로 좀 쓰는거는

  • DI (의존성 주입) : Hilt or Koin
  • Testing : junit, mockk, turbine
  • 서버 관련 : Room, Retrofit

등등이 있으

1-4. 결론

그리고 이짓거리 할때 중요한 3가지만 생각하셈

  1. 어떤 의존성을 커스터마징 시킬것인가.
  2. Sync Now 할때 오류가 없는가.
  3. 커스텀 플러그인은 하나씩 만들고 빌드하기

참고로 위에 남긴 컨벤션 플러그인 코드에

logger.lifecycle("✅ Applied: com.android.application")
logger.lifecycle("✅ Applied: org.jetbrains.kotlin.android")
logger.lifecycle("✅ Applied: org.jetbrains.kotlin.kapt")
logger.lifecycle("✅ Applied: androidx.navigation.safeargs.kotlin")

이 내용을 추가해서 로그로 보면

이렇게 확인하면서 하는거 강추함 안그럼 삽질하고 샷건침

보면 좋은 글들
Gradle Kotlin 컨벤션 플러그인을 사용한 모듈 관리 -1 멀티모듈의 도입
Gradle Kotlin 컨벤션 플러그인을 사용한 모듈 관리 -2 Version Catalog
Gradle Kotlin 컨벤션 플러그인을 사용한 모듈 관리 -3 Convention Plugin 적용
[안드로이드 멀티 모듈] 2. 모듈 생성 후 관계 정의하기
일반적인 모듈화 패턴
안드로이드 [Kotlin] - Clean Architecture / 모듈화(1)
[Android] Version Catalog + Convention Plugin으로 build.gradle 버전을 관리해보자!

profile
디지털 치매 예방

0개의 댓글