004. (KMM Mobile) 4. BuildSrc 적용 및 의존성 설정

4. BuildSrc 적용 및 의존성 설정

이전 포스팅에서 생성한 프로젝트에 의존성 관리는 위한 BuildSrc 모듈을 추가해보자.

참고 프로젝트에 따라 build-logic으로 생성하기도 한다.

4.1. BuildSrc 모듈 생성

루트 폴더 하위에 buildSrc 폴더와 build.gradle.kts 스크립트 파일을 추가한다.

이후

build.gradle.kts에 아래 스크립트를 작성한다.

1
2
3
4
5
6
7
8
9
import org.gradle.kotlin.dsl.`kotlin-dsl`

repositories {
mavenCentral()
}

plugins {
`kotlin-dsl`
}

이후 아래와 같이 소스 폴더를 생성한다.

이후 Gradle Sync를 수행하면 .gradle 폴더가 빌드를 수행하면 build 폴더가 생성됨을 확인할 수 있다.

4.2. 의존성 관리를 위한 Deps 클래스 추가

buildSrc/src/main/kotlin 경로에 Deps 오브젝트 클래스 파일을 추가한 뒤 아래 코드를 작성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
object Deps {

// COMPOSE
private const val activityComposeVersion = "1.7.2"
const val activityCompose = "androidx.activity:activity-compose:$activityComposeVersion"

const val composeVersion = "1.5.1"
const val composeUi = "androidx.compose.ui:ui:$composeVersion"
const val composeUiTooling = "androidx.compose.ui:ui-tooling:$composeVersion"
const val composeUiToolingPreview = "androidx.compose.ui:ui-tooling-preview:$composeVersion"
const val composeFoundation = "androidx.compose.foundation:foundation:$composeVersion"
const val composeMaterial = "androidx.compose.material:material:$composeVersion"
const val composeIconsExtended = "androidx.compose.material:material-icons-extended:$composeVersion"

const val composeMaterial3 = "androidx.compose.material3:material3:1.1.2"

private const val composeNavigationVersion = "2.5.3"
const val composeNavigation = "androidx.navigation:navigation-compose:$composeNavigationVersion"

private const val coilComposeVersion = "2.1.0"
const val coilCompose = "io.coil-kt:coil-compose:$coilComposeVersion"

// KOTLIN DATE TIME
private const val dateTimeVersion = "0.4.0"
const val kotlinDateTime = "org.jetbrains.kotlinx:kotlinx-datetime:$dateTimeVersion"

// HILT
private const val hiltVersion = "2.42"
private const val hiltCompilerVersion = "1.0.0"
const val hiltAndroid = "com.google.dagger:hilt-android:$hiltVersion"
const val hiltAndroidCompiler = "com.google.dagger:hilt-android-compiler:$hiltVersion"
const val hiltCompiler = "androidx.hilt:hilt-compiler:$hiltCompilerVersion"
const val hiltNavigationCompose = "androidx.hilt:hilt-navigation-compose:$hiltCompilerVersion"

// KTOR
private const val ktorVersion = "2.1.3"
const val ktorCore = "io.ktor:ktor-client-core:$ktorVersion"
const val ktorSerialization = "io.ktor:ktor-client-content-negotiation:$ktorVersion"
const val ktorSerializationJson = "io.ktor:ktor-serialization-kotlinx-json:$ktorVersion"
const val ktorAndroid = "io.ktor:ktor-client-android:$ktorVersion"
const val ktorIOS = "io.ktor:ktor-client-ios:$ktorVersion"

// GRADLE PLUGINS
const val kotlinVersion = "1.9.10"
const val kotlinGradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"

private const val gradleVersion = "7.3.0"
const val androidBuildTools = "com.android.tools.build:gradle:$gradleVersion"

private const val sqlDelightGradleVersion = "1.5.3"
const val sqlDelightGradlePlugin = "com.squareup.sqldelight:gradle-plugin:$sqlDelightGradleVersion"

const val hiltGradlePlugin = "com.google.dagger:hilt-android-gradle-plugin:$hiltVersion"

// SQLDELIGHT
private const val sqlDelightVersion = "1.5.4"
const val sqlDelightRuntime = "com.squareup.sqldelight:runtime:$sqlDelightVersion"
const val sqlDelightAndroidDriver = "com.squareup.sqldelight:android-driver:$sqlDelightVersion"
const val sqlDelightNativeDriver = "com.squareup.sqldelight:native-driver:$sqlDelightVersion"
const val sqlDelightCoroutinesExtensions = "com.squareup.sqldelight:coroutines-extensions:$sqlDelightVersion"

// TESTING
private const val assertKVersion = "0.25"
const val assertK = "com.willowtreeapps.assertk:assertk:$assertKVersion"

private const val turbineVersion = "0.7.0"
const val turbine = "app.cash.turbine:turbine:$turbineVersion"

private const val jUnitVersion = "4.13.2"
const val jUnit = "junit:junit:$jUnitVersion"

private const val testRunnerVersion = "1.5.1"
const val testRunner = "androidx.test:runner:$testRunnerVersion"

const val composeTesting = "androidx.compose.ui:ui-test-junit4:$composeVersion"
const val composeTestManifest = "androidx.compose.ui:ui-test-manifest:$composeVersion"

const val hiltTesting = "com.google.dagger:hilt-android-testing:$hiltVersion"
}

4.3. androidApp 모듈 의존성 변경

이제 androidApp의 의존성을 아래와 같이 교체할 수 있다.

before

1
2
3
4
5
6
7
8
9
// androidApp build.gradle.kts
dependencies {
implementation(projects.shared)
implementation(libs.compose.ui)
implementation(libs.compose.ui.tooling.preview)
implementation(libs.compose.material3)
implementation(libs.androidx.activity.compose)
debugImplementation(libs.compose.ui.tooling)
}

after

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// androidApp build.gradle.kts
dependencies {
implementation(projects.shared)
implementation(Deps.composeUi)
implementation(Deps.composeUiTooling)
implementation(Deps.composeUiToolingPreview)
implementation(Deps.composeFoundation)
implementation(Deps.composeMaterial)
implementation(Deps.activityCompose)
implementation(Deps.composeIconsExtended)
implementation(Deps.composeNavigation)
implementation(Deps.coilCompose)

implementation(Deps.composeMaterial3)

implementation(Deps.hiltAndroid)
kapt(Deps.hiltAndroidCompiler)
kapt(Deps.hiltCompiler)
implementation(Deps.hiltNavigationCompose)

implementation(Deps.ktorAndroid)

androidTestImplementation(Deps.testRunner)
androidTestImplementation(Deps.jUnit)
androidTestImplementation(Deps.composeTesting)
debugImplementation(Deps.composeTestManifest)

kaptAndroidTest(Deps.hiltAndroidCompiler)
androidTestImplementation(Deps.hiltTesting)
}

이때 kaptkaptAndroidTest에서 오류가 발생하니, plugins 블록에 kapt 관련 코드를 추가한다.

before

1
2
3
4
5
// androidApp build.gradle.kts
plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.kotlinAndroid)
}

after

1
2
3
4
5
6
7
8
// androidApp build.gradle.kts
plugins {
id("com.android.application")
kotlin("android")
kotlin("kapt")
id("dagger.hilt.android.plugin")
kotlin("plugin.serialization") version Deps.kotlinVersion
}

4.4. 프로젝트 의존성 변경

위에서 추가한 id("dagger.hilt.android.plugin") 스크립트 때문에 또 오류가 발생할 것이다.

프로젝트의 build.gradle.kts에서 아래와 같이 의존성을 추가로 설정하자.

before

1
2
3
4
5
6
7
8
// root build.gradle.kts
plugins {
alias(libs.plugins.androidApplication).apply(false)
alias(libs.plugins.androidLibrary).apply(false)
alias(libs.plugins.kotlinAndroid).apply(false)
alias(libs.plugins.kotlinMultiplatform).apply(false)
alias(libs.plugins.kotlinCocoapods).apply(false)
}

after

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// root build.gradle.kts
buildscript {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
dependencies {
classpath(Deps.kotlinGradlePlugin)
classpath(Deps.androidBuildTools)
classpath(Deps.sqlDelightGradlePlugin)
classpath(Deps.hiltGradlePlugin)
}
}

allprojects {
repositories {
google()
mavenCentral()
}
}

tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
}

4.5. shared 모듈 의존성 변경

다시 공용 모듈인 shared 모듈로 돌아와서 의존성을 변경하자.

before

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// shared build.gradle.kts
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.kotlinCocoapods)
alias(libs.plugins.androidLibrary)
}

@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
kotlin {
targetHierarchy.default()

androidTarget {
compilations.all {
kotlinOptions {
jvmTarget = "1.8"
}
}
}
iosX64()
iosArm64()
iosSimulatorArm64()

cocoapods {
summary = "Some description for the Shared Module"
homepage = "Link to the Shared Module homepage"
version = "1.0"
ios.deploymentTarget = "14.1"
podfile = project.file("../iosApp/Podfile")
framework {
baseName = "shared"
}
}

sourceSets {
val commonMain by getting {
dependencies {
//put your multiplatform dependencies here
}
}
val commonTest by getting {
dependencies {
implementation(libs.kotlin.test)
}
}
}
}

android {
namespace = "com.namhoonkim.kmm.translator"
compileSdk = 34
defaultConfig {
minSdk = 24
}
}

after

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// shared build.gradle.kts
plugins {
kotlin("multiplatform")
kotlin("native.cocoapods")
id("com.android.library")
kotlin("plugin.serialization") version Deps.kotlinVersion
id("com.squareup.sqldelight")
}

@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
kotlin {
androidTarget {
compilations.all {
kotlinOptions {
jvmTarget = "1.8"
}
}
}
iosX64()
iosArm64()
iosSimulatorArm64()

cocoapods {
summary = "Some description for the Shared Module"
homepage = "Link to the Shared Module homepage"
version = "1.0"
ios.deploymentTarget = "14.1"
podfile = project.file("../iosApp/Podfile")
framework {
baseName = "shared"
}
}

sourceSets {
val commonMain by getting {
dependencies {
implementation(Deps.ktorCore)
implementation(Deps.ktorSerialization)
implementation(Deps.ktorSerializationJson)
implementation(Deps.sqlDelightRuntime)
implementation(Deps.sqlDelightCoroutinesExtensions)
implementation(Deps.kotlinDateTime)
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
implementation(Deps.assertK)
implementation(Deps.turbine)
}
}
val androidMain by getting {
dependencies {
implementation(Deps.ktorAndroid)
implementation(Deps.sqlDelightAndroidDriver)
}
}
val androidUnitTest by getting
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)

dependencies {
implementation(Deps.ktorIOS)
implementation(Deps.sqlDelightNativeDriver)
}
}
val iosX64Test by getting
val iosArm64Test by getting
val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
}
}

android {
namespace = "com.namhoonkim.kmm.translator"
compileSdk = 34
defaultConfig {
minSdk = 24
}
}

이제 오류 없이 정상적으로 빌드가 되었음을 확인할 수 있다.

References