[Android] Project에 ktlint 적용하기

ktlint는 Kotlin을 위한 정적 분석 도구 중 하나이며, 

Kotlin 코딩 규약과 Android의 Kotlin Style 가이드에 기반하여 코드 컨벤션을 지키도록 목표합니다.

이는 표준 컨벤션을 자체적으로 제공하며, 작성한 코드의 Style, 형식을 검사하여 수정합니다.

 

적용되어 있는 규칙들은 잔뜩 있지만, 이는 스타일만 변경할 뿐

코드의 내용은 해치지 않기 때문에, 참고만 해도 될 것 같습니다.

 

1. [Android Project에 적용하는 법]

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
}

android {
    namespace = "/* name space */"
    compileSdk = 34

    defaultConfig {
        applicationId = "/* application ID */"
        minSdk = 29
        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_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

val ktlint by configurations.creating

dependencies {
    ktlint("com.pinterest:ktlint:0.49.1") {
        attributes {
            attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
        }
    }

    implementation("androidx.core:core-ktx:1.12.0")
    implementation("androidx.appcompat:appcompat:1.6.1")
    implementation("com.google.android.material:material:1.11.0")
    implementation("androidx.constraintlayout:constraintlayout:2.1.4")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}

val ktlintCheck by tasks.registering(JavaExec::class) {
    group = LifecycleBasePlugin.VERIFICATION_GROUP
    description = "Check Kotlin code style"
    classpath = ktlint
    mainClass.set("com.pinterest.ktlint.Main")
    // see https://pinterest.github.io/ktlint/install/cli/#command-line-usage for more information
    args(
        "**/src/**/*.kt",
        "**.kts",
        "!**/build/**",
    )
}

tasks.check {
    dependsOn(ktlintCheck)
}

tasks.register<JavaExec>("ktlintFormat") {
    group = LifecycleBasePlugin.VERIFICATION_GROUP
    description = "Check Kotlin code style and format"
    classpath = ktlint
    mainClass.set("com.pinterest.ktlint.Main")
    jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
    // see https://pinterest.github.io/ktlint/install/cli/#command-line-usage for more information
    args(
        "-F",
        "**/src/**/*.kt",
        "**.kts",
        "!**/build/**",
    )
}

 

다음과 같이 'build.gradle.kts(:app)' 파일에 내용을 추가한 후 sync 해주면,

기본적인 ktlint에 대한 세팅은 완료됩니다.

찾아보니 안드로이드 스튜디오에 플러그인으로 지원이 되고 있는 것 같지만,

주니어 개발자인 제 생각에는 플러그인으로 넣어 놓기 보다는,

협업할 때, 프로젝트에 ktlint를 내부에 적용시키는게 더 효율적일 것 같다고 생각합니다.

 

 

2. [Ktlint 적용 방법]

class Cal {

    fun add(a: Int, b: Int) : Int{
            return a + b;
    }




    fun sub(a: Int,b:Int) :         Int=a-b
}

 

이런 말도 안되는 'Cal.kt' 라는 더러운 코드가 존재한다는 가정 하에, 이전에 설정해준 ktlint를 적용해보겠습니다.

Android Studio 하단의 Terminal에서 `./gradlew ktlintFormat` 을 입력해주면

 

skw@seogyeong-wons-MacBook-Air Part_0 % ./gradlew ktlintFormat

> Task :app:ktlintFormat FAILED
/Users/skw/Documents/LIKELION/StudyAlone/Part_0/app/src/androidTest/java/com/woojugoing/part_0/ExampleInstrumentedTest.kt:1:9: Package name must not contain underscore (standard:package-name)
/Users/skw/Documents/LIKELION/StudyAlone/Part_0/app/src/androidTest/java/com/woojugoing/part_0/ExampleInstrumentedTest.kt:5:1: Wildcard import (standard:no-wildcard-imports)
/Users/skw/Documents/LIKELION/StudyAlone/Part_0/app/src/test/java/com/woojugoing/part_0/ExampleUnitTest.kt:1:9: Package name must not contain underscore (standard:package-name)
/Users/skw/Documents/LIKELION/StudyAlone/Part_0/app/src/test/java/com/woojugoing/part_0/ExampleUnitTest.kt:3:1: Wildcard import (standard:no-wildcard-imports)
/Users/skw/Documents/LIKELION/StudyAlone/Part_0/app/src/main/java/com/woojugoing/part_0/MainActivity.kt:1:9: Package name must not contain underscore (standard:package-name)
/Users/skw/Documents/LIKELION/StudyAlone/Part_0/app/src/main/java/com/woojugoing/part_0/Example.kt:1:1: File 'Example.kt' contains a single class and possibly also extension functions for that class and should be named same after that class 'Cal.kt' (standard:filename)
/Users/skw/Documents/LIKELION/StudyAlone/Part_0/app/src/main/java/com/woojugoing/part_0/Example.kt:3:33: Expected a single white space before start of function body (standard:function-start-of-body-spacing)

Summary error count (descending) by rule:
  standard:package-name: 3
  standard:no-wildcard-imports: 2
  standard:filename: 1
  standard:function-start-of-body-spacing: 1

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:ktlintFormat'.
> Process 'command '/Users/skw/Library/Java/JavaVirtualMachines/openjdk-20.0.1/Contents/Home/bin/java'' finished with non-zero exit value 1

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

BUILD FAILED in 1s
1 actionable task: 1 executed

 

해당 과정과 함께 자동으로 컨벤션에 맞게 코드가 정리되는 모습을 볼 수 있습니다.

저는 해당 자동 맞춤 기능을 애용하고 있는데, 나쁘지 않게 잘 정리되는 거 같아서 편했습니다.

 

class Cal {

    fun add(a: Int, b: Int): Int {
        return a + b
    }

    fun sub(a: Int, b: Int): Int = a - b
}