이번 영상에서는 Groovy로 작성된 build.gradle 파일에 KTS를 적용하는 법에 대해 알아보도록 하겠습니다.
현재 Android Studio에서 새로 작성하는 프로젝트의 build.gradle은 기본적으로 Groovy-DSL로 만들어지기 때문에 KTS를 적용하기 위해서는 수동으로 스크립트를 변환하여야 합니다. 여기서는 지금까지 만들어 온 프로젝트의 스크립트를 변환하면서 사용법을 알아보도록 하겠습니다.
Groovy는 '와 "를 모두 사용가능하지만 KTS는 "만을 허용하기 때문에 우선은 ' 를 "로 변환합니다. 일괄변환 기능을 사용하면 됩니다.
다음은 함수로 처리되어야 하는 부분에 ()를 붙여줍니다.
plugins블럭 내부의 idproguardFilesdependencies블럭 내부의 implementation 등프로퍼티로 처리되어야 하는 부분에 =를 붙여줍니다. 바꾸는 김에 자바 11의 기능을 사용할 수 있도록 컴파일에 사용할 자바 소스코드 버전도 11로 올려 줍니다. 여기까지는 Groovy인 상태로도 빌드에 성공합니다.
compileSdkapplicationIdminSdktargetSdkversionCodeversionNametestInstrumentationRunnersourceCompatibilitytargetCompatibilitybuildFeatures블럭 내부의 viewBindingkapt블럭 내부의 correctErrorTypes이 상태에서 두 build.gradle의 파일 이름을 build.gradle.kts로 변경하고 빌드하면 빌드가 실패합니다. 일단은 KTS를 사용할 수 있게 하기 위해 오류가 나오는 부분은 주석처리하고 빌드를 성공시키도록 합니다.
이제 빌드가 실패하는 부분을 수정합니다.
android {
buildTypes {
+ named("release") {
- minifyEnabled false
+ isMinifyEnabled = false
}
}
}
Project level의 커스텀 태스크는 다음과 같이 수정합니다.
- task clean(type: Delete) {
- delete rootProject.buildDir
- }
+ tasks.register<Delete>("clean") {
+ delete(rootProject.buildDir)
+ }
다음은 settings.gradle의 내용을 수정하고 settings.gradle.kts로 변환한 뒤 빌드를 수행합니다.
-include ':app'
+include(":app")
이제 모든 gradle 파일에 KTS가 적용되었습니다.
현재는 app/build.gradle.kts에 모든 디펜던시 정보를 기입하고 있습니다만 buildSrc 디렉토리를 사용하면 디펜던시 정보를 외부로 빼내어 따로 관리할 수 있습니다.
우선은 프로젝트 아래에 buildSrc 디렉토리를 만든 뒤 내부에 다음 내용의 build.gradle.kts 파일을 만들어 KTS를 활성화 한 뒤 싱크합니다. 그러면 프로젝트에서 buildSrc 디렉토리를 인식할 수 있게 됩니다.
repositories {
mavenCentral()
}
plugins {
`kotlin-dsl`
}
다음은 buildSrc > src > main > java 폴더를 생성합니다. 그리고 java 내부에 디펜던시 정보를 담은 object 클래스를 만들어 줍니다.
object DefaultConfig {
const val COMPILE_SDK_VERSION = 32
const val MIN_SDK_VERSION = 23
const val TARGET_SDK_VERSION = 32
const val VERSION_CODE = 1
const val VERSION_NAME = "1.0"
}
object Dependencies {
const val CORE_KTX = "androidx.core:core-ktx:${Versions.CORE_KTX}"
const val APP_COMPAT = "androidx.appcompat:appcompat:${Versions.APP_COMPAT}"
const val MATERIAL = "com.google.android.material:material:${Versions.MATERIAL}"
const val CONSTRAINT_LAYOUT =
"androidx.constraintlayout:constraintlayout:${Versions.CONSTRAINT_LAYOUT}"
const val RETROFIT = "com.squareup.retrofit2:retrofit:${Versions.RETROFIT}"
const val RETROFIT_CONVERTER_MOSHI =
"com.squareup.retrofit2:converter-moshi:${Versions.RETROFIT}"
const val MOSHI = "com.squareup.moshi:moshi:${Versions.MOSHI}"
const val MOSHI_KAPT = "com.squareup.moshi:moshi-kotlin-codegen:${Versions.MOSHI}"
const val OKHTTP = "com.squareup.okhttp3:okhttp:${Versions.OKHTTP}"
const val OKHTTP_LOGGING_INTERCEPTOR =
"com.squareup.okhttp3:logging-interceptor:${Versions.OKHTTP}"
const val LIFECYCLE_VIEWMODEL_KTX =
"androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.LIFECYCLE}"
const val LIFECYCLE_RUNTIME_KTX =
"androidx.lifecycle:lifecycle-runtime-ktx:${Versions.LIFECYCLE}"
const val LIFECYCLE_SAVEDSTATE =
"androidx.lifecycle:lifecycle-viewmodel-savedstate:${Versions.LIFECYCLE}"
const val COROUTINE_CORE = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.COROUTINE}"
const val COROUTINE_ANDROID =
"org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.COROUTINE}"
const val COIL = "io.coil-kt:coil:${Versions.COIL}"
const val RECYCLERVIEW = "androidx.recyclerview:recyclerview:${Versions.RECYCLERVIEW}"
const val NAVIGATION_FRAGMENT_KTX =
"androidx.navigation:navigation-fragment-ktx:${Versions.NAVIGATION}"
const val NAVIGATION_UI_KTX = "androidx.navigation:navigation-ui-ktx:${Versions.NAVIGATION}"
const val ROOM_RUNTIME = "androidx.room:room-runtime:${Versions.ROOM}"
const val ROOM_KTX = "androidx.room:room-ktx:${Versions.ROOM}"
const val ROOM_KAPT = "androidx.room:room-compiler:${Versions.ROOM}"
const val ROOM_PAGING = "androidx.room:room-paging:${Versions.ROOM}"
const val KOTLIN_SERIALIZATION =
"org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.KOTLIN_SERIALIZATION}"
const val PREFERENCES_DATASTORE = "androidx.datastore:datastore-preferences:${Versions.DATASTORE}"
const val PAGING = "androidx.paging:paging-runtime-ktx:${Versions.PAGING}"
const val WORKMANGER = "androidx.work:work-runtime-ktx:${Versions.WORKMANGER}"
const val DAGGER_HILT = "com.google.dagger:hilt-android:${Versions.HILT}"
const val DAGGER_HILT_KAPT = "com.google.dagger:hilt-compiler:${Versions.HILT}"
const val ACTIVITY_KTX =
"androidx.activity:activity-ktx:${Versions.ACTIVITY_KTX}"
const val FRAGMENT_KTX =
"androidx.fragment:fragment-ktx:${Versions.FRAGMENT_KTX}"
const val HILT_EXTENSION_WORK = "androidx.hilt:hilt-work:${Versions.HILT_EXTENSION}"
const val HILT_EXTENSION_KAPT = "androidx.hilt:hilt-compiler:${Versions.HILT_EXTENSION}"
}
object Plugins {
const val ANDROID_APPLICATION = "com.android.application"
const val ANDROID_LIBRARY = "com.android.library"
const val KOTLIN_ANDROID = "org.jetbrains.kotlin.android"
const val SECRETS_GRADLE_PLUGIN =
"com.google.android.libraries.mapsplatform.secrets-gradle-plugin"
const val SAFEARGS = "androidx.navigation.safeargs.kotlin"
const val KOTLIN_SERIALIZATION = "org.jetbrains.kotlin.plugin.serialization"
const val DAGGER_HILT = "com.google.dagger.hilt.android"
const val HILT_PLUGIN = "dagger.hilt.android.plugin"
const val PARCELIZE = "kotlin-parcelize"
const val KAPT = "kotlin-kapt"
}
object Testing {
const val JUNIT4 = "junit:junit:${Versions.JUNIT4}"
const val ANDROID_JUNIT = "androidx.test.ext:junit:${Versions.ANDROID_JUNIT}"
const val ESPRESSO_CORE = "androidx.test.espresso:espresso-core:${Versions.ESPRESSO_CORE}"
}
object Versions {
// Project
const val AGP = "7.1.3"
const val KOTLIN = "1.6.10"
const val SECRETS_GRADLE = "2.0.1"
// Androidx
const val CORE_KTX = "1.7.0"
const val APP_COMPAT = "1.4.1"
const val MATERIAL = "1.5.0"
const val CONSTRAINT_LAYOUT = "2.1.3"
// Testing
const val JUNIT4 = "4.13.2"
const val ANDROID_JUNIT = "1.1.3"
const val ESPRESSO_CORE = "3.4.0"
// Library
const val RETROFIT = "2.9.0"
const val MOSHI = "1.13.0"
const val OKHTTP = "4.9.3"
const val LIFECYCLE = "2.4.1"
const val COROUTINE = "1.6.0"
const val COIL = "1.4.0"
const val RECYCLERVIEW = "1.2.1"
const val NAVIGATION = "2.4.1"
const val ROOM = "2.4.2"
const val KOTLIN_SERIALIZATION = "1.3.2"
const val DATASTORE = "1.0.0"
const val PAGING = "3.1.1"
const val WORKMANGER = "2.7.1"
const val HILT = "2.41"
const val ACTIVITY_KTX = "1.4.0"
const val FRAGMENT_KTX = "1.4.1"
const val HILT_EXTENSION = "1.0.0"
}
마지막으로 각 build.gradle.kts 파일이 위에서 정의한 클래스 정보를 사용하도록 수정해 줍니다.
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id(Plugins.ANDROID_APPLICATION) version Versions.AGP apply false
id(Plugins.ANDROID_LIBRARY) version Versions.AGP apply false
id(Plugins.KOTLIN_ANDROID) version Versions.KOTLIN apply false
id(Plugins.SECRETS_GRADLE_PLUGIN) version Versions.SECRETS_GRADLE apply false
id(Plugins.SAFEARGS) version Versions.NAVIGATION apply false
id(Plugins.KOTLIN_SERIALIZATION) version Versions.KOTLIN apply false
id(Plugins.DAGGER_HILT) version Versions.HILT apply false
}
//task clean(type: Delete) {
// delete rootProject.buildDir
//}
tasks.register<Delete>("clean") {
delete(rootProject.buildDir)
}
plugins {
id(Plugins.ANDROID_APPLICATION)
id(Plugins.KOTLIN_ANDROID)
id(Plugins.KAPT)
id(Plugins.SECRETS_GRADLE_PLUGIN)
id(Plugins.SAFEARGS)
id(Plugins.PARCELIZE)
id(Plugins.HILT_PLUGIN)
}
android {
compileSdk = DefaultConfig.COMPILE_SDK_VERSION
defaultConfig {
applicationId = "com.qualitybitz.booksearchapp"
minSdk = DefaultConfig.MIN_SDK_VERSION
targetSdk = DefaultConfig.TARGET_SDK_VERSION
versionCode = DefaultConfig.VERSION_CODE
versionName = DefaultConfig.VERSION_NAME
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
named("release") {
// minifyEnabled false
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
}
kapt {
correctErrorTypes = true
}
}
dependencies {
implementation(Dependencies.CORE_KTX)
implementation(Dependencies.APP_COMPAT)
implementation(Dependencies.MATERIAL)
implementation(Dependencies.CONSTRAINT_LAYOUT)
testImplementation(Testing.JUNIT4)
androidTestImplementation(Testing.ANDROID_JUNIT)
androidTestImplementation(Testing.ESPRESSO_CORE)
// Retrofit
implementation(Dependencies.RETROFIT)
implementation(Dependencies.RETROFIT_CONVERTER_MOSHI)
// Moshi
implementation(Dependencies.MOSHI)
kapt(Dependencies.MOSHI_KAPT)
// Okhttp
implementation(Dependencies.OKHTTP)
implementation(Dependencies.OKHTTP_LOGGING_INTERCEPTOR)
// Lifecycle
implementation(Dependencies.LIFECYCLE_VIEWMODEL_KTX)
implementation(Dependencies.LIFECYCLE_RUNTIME_KTX)
implementation(Dependencies.LIFECYCLE_VIEWMODEL_KTX)
// Coroutine
implementation(Dependencies.COROUTINE_CORE)
implementation(Dependencies.COROUTINE_ANDROID)
// Coil
implementation(Dependencies.COIL)
// Recyclerview
implementation(Dependencies.RECYCLERVIEW)
// Navigation
implementation(Dependencies.NAVIGATION_FRAGMENT_KTX)
implementation(Dependencies.NAVIGATION_UI_KTX)
// Room
implementation(Dependencies.ROOM_RUNTIME)
implementation(Dependencies.ROOM_KTX)
kapt(Dependencies.ROOM_KAPT)
implementation(Dependencies.ROOM_PAGING)
// Kotlin serialization
implementation(Dependencies.KOTLIN_SERIALIZATION)
// DataStore
implementation(Dependencies.PREFERENCES_DATASTORE)
// Paging
implementation(Dependencies.PAGING)
// WorkManager
implementation(Dependencies.WORKMANGER)
// Hilt
implementation(Dependencies.DAGGER_HILT)
kapt(Dependencies.DAGGER_HILT_KAPT)
// ViewModel delegate
implementation(Dependencies.ACTIVITY_KTX)
implementation(Dependencies.FRAGMENT_KTX)
// Hilt extension
implementation(Dependencies.HILT_EXTENSION_WORK)
kapt(Dependencies.HILT_EXTENSION_KAPT)
}
여기에서 다루지 않은 build.gradle의 더 많은 사용법에 대해서는 Gradle 공식 홈페이지에서 제공하는 Migrating build logic from Groovy to Kotlin 문서나, 구글에서 만든 공식 앱인 The Google I/O Android App을 보시면 도움이 될 것 같습니다.