Migrating Groovy DSL to Kotlin DSL

Kotlin DSL 이란?

DSL이란 Domain Specific Language의 약어로 특정 분야에 최적화된 프로그래밍 언어를 뜻한다. 상용구 코드를 최소화 하기 위해 명령형 코드 대신 선언적 코드 형식을 따른다.
Kotlin DSL은 코틀린의 언어적인 특징으로 가독성이 좋고 간략한 코드를 사용하여 Gradle 스크립팅을 하는 것을 목적으로 하는 DSL이다.

빌드 스크립트에서 사용하는 객체, 함수, 속성들은 Gradle API와 적용한 plugin API에서 가져온다.

왜 Kotlin DSL을 사용해야 하지?

왜 굳이 Groovy DSL 에서 Kotlin DSL로 변경해야 하는지 의문이 생길 수 있다. Kotlin DSL로 변경하면 다음과 같은 장점이 생긴다.

  • 컴파일 타임에 에러 확인
  • 코드 탐색
  • 자동 완성
  • 구문 강조
  • IDE의 지원으로 향상된 편집환경
  • 소스코드와 동일한 언어의 사용

반면 단점도 존재한다. 

  • 빌드 캐시가 Invalidation 되거나 클린 빌드시에 Groovy DSL보다 느리다.
  • Java8이상에서 동작
  • 새로운 라이브러리 버전 Inspection 기능 미지원

느려지는 부분이 가장 뼈 아픈 부분이지만, Kotlin DSL이 가지는 장점을 무시하긴 어렵다. 아래의 를 참고하여, 각자의 상황과 중요시 하는 부분에 대한 가치를 고민하고 마이그레이션 하는 것을 추천한다.

Kotlin DSL 사용하기

Gradle 스크립트를 Kotlin DSL을 사용하는 방법은 간단하다. 기존 *.gradle 파일을 *.gradle.kts 로 변경하면 된다.

예) build.gradle -> build.gradle.kts
Note:Kotlin DSL에서는 당연히 코틀린 문법을 따르므로 기본적으로 문자열은 모두 따옴표 (" ") 로 묶어 표현한다.

자세한 내용은 다음의 문서를 참고하자.

기본적으로 자주 변경하는 부분에 대한 예제코드는 다음과 같다.

플러그인 블럭

// Groovy DSL 
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
}
plugins {
    id("com.android.application")
    kotlin("android")
    kotlin("kapt")
//    id ("kotlin-android") 위의 코드와 동일한 내용
//    id ("kotlin-kapt") 위의 코드와 동일한 내용
}
Note : Gradle 공식 문서를 보면 플러그인 {} 블록을 사용하여 Gradle 플러그인을 선언하면 편집 환경이 크게 향상 되기 떄문에 적극 권장하고 있다.

android 블럭

//Groovy DSL
android {
    compileSdkVersion 30
    buildToolsVersion "30.0.2"

    defaultConfig {
        applicationId "com.charlezz.test"
        minSdkVersion 22
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    buildFeatures{
        dataBinding = true
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }
}
android {
    compileSdkVersion(30)
    buildToolsVersion("30.0.2")

    defaultConfig {
        applicationId("com.charlezz.test")
        minSdkVersion(22)
        targetSdkVersion(30)
        versionCode(1)
        versionName("1.0")

        testInstrumentationRunner("androidx.test.runner.AndroidJUnitRunner")
    }

    buildTypes {
        getByName("release") {
            minifyEnabled(false)
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
   
    buildFeatures.dataBinding = true

    compileOptions {
        sourceCompatibility(JavaVersion.VERSION_1_8)
        targetCompatibility(JavaVersion.VERSION_1_8)
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }
}

dependencis 블록

dependencies {
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    kapt 'com.google.dagger:dagger-compiler:2.34.1'
}
dependencies {
    implementation("androidx.constraintlayout:constraintlayout:2.0.4")
    testImplementation("junit:junit:4.+")
    androidTestImplementation("androidx.test.ext:junit:1.1.2")
    kapt("com.google.dagger:dagger-compiler:2.34.1")
}

buildSrc로 버전관리 하기

buildSrc는 빌드 로직을 포함할 수 있는 Gradle 프로젝트 루트 디렉토리다. buildSrc과 Kotlin DSL을 사용해서 매우 적은 구성으로 커스텀 빌드 코드를 작성하고 전체 프로젝트에서 이 로직을 공유 할 수 있다. buildSrc를 변경하면 전체 프로젝트의 빌드캐시가 무효화되기 때문에 잦은 수정을 피해야 한다.

프로젝트 정보 또는 라이브러리의 버전 등을 관리해보는 예제를 알아보자.

먼저 buildSrc 디렉토리를 만든다.

디렉토리를 만들고 루트에는 build.gradle.kts와 추가하고, 하위 디렉토리 src/main/kotlin 에는 Versions라는 클래스를 추가한다.

이제 sync Project with Gradle Files 를 눌러 설정을 마치도록 한다.

위의 스크린샷 처럼 프로젝트 계층에 buildSrc가 추가 된 것을 확인한다.

build 캐시는 git에서 관리하지 않도록 다음의 내용을 .ignore에 추가하자.

buildSrc/build

이제 Versions에 버전을 정의하고 이를 참조하는 예제를 살펴보자.

다음과 같이 버전명을 명시 한다.

object Versions {
    const val kotlin = "1.4.31"
}

모듈레벨의 build.gradle.kts에서는 해당 버전을 참조 할 수 있다.

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlin")
}

만약 의존성 아티팩트 단위로 버전관리를 하고 싶다면 별도의 클래스(예: Dependencies.kt)를 하나 더 생성하자.

object Dependencies {
    const val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlin}"
}

이제 Dependencies 의 멤버 참조를 통해 의존성을 관리 할 수 있다.

dependencies {
    // implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlin")
    implementation(Dependencies.kotlinStdLib)
}

현재까지의 트러블 슈팅..

1.Hilt이슈

현재 Hilt가 특정 환경에서 Kotlin DSL과 함께 사용시 빌드가 안되는 현상이 있다. 최신 Gradle 버전과 최신 Hilt 스냅샷을 사용하면 빌드가 된다. https://github.com/google/dagger/issues/2634

2. DFM에서 buildFeatures.dataBinding 이슈

예를 들어 dataBinding을 활성화 할 때 다음과 같은 코드를 사용한다.

buildFeatures.dataBinding = true

하지만 DFM의 build.gradle.kts 에서는 다음과 같이 이전 방식으로 코드를 작성해야 한다.

dataBinding.isEnabled = true

app모듈에서 참조하는 buildFeatures는 BuildFeatures 인터페이스를 상속한 ApplicationBuildFeatures이지만 DFM에서는 그냥 BuildFeature를 참조하기 때문인 것으로 보이는데, 이를 해결 할 수 있는 방법을 아직 못찾은 상태… 추후 AGP 업데이트에서 지원할 것으로 기대한다. (아시는분 계시면 댓글로 부탁드립니다)

강제로 DynamicFeatureBuildFeatures로 캐스팅해서 데이터바인딩을 활성화 시킬수도 있겠지만, 추천하진 않는다.

android {
    if(buildFeatures is DynamicFeatureBuildFeatures){
        (buildFeatures as DynamicFeatureBuildFeatures).dataBinding = true
    }
}

 

Buy me a coffeeBuy me a coffee
카테고리: Android

2개의 댓글

독거공대생 · 2021년 6월 2일 9:32 오후

질문있습니다!. 빌드스크립트에서 컴파일 타임에 에러가 안나고 런타임에 나는 경우도 있나요? 어떤 경우일까요?

    Charlezz · 2021년 6월 2일 10:55 오후

    코드를 작성하는 시점에 IDE에서 syntax error를 잡아준다는 의미였습니다 ㅎ

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.