SpringBoot - Kotlin 프로젝트에 Klint 적용 및 disable 설정

원태연·2024년 7월 23일
2
post-thumbnail

Kotlin 프로젝트를 진행하던 중, Klint라는 걸 적용한 내용을 간단하게 정리하고자 합니다.

Lint

KtLint는 Kotlin 프로그래밍 언어의 코드 스타일 규칙을 검사하고 적용하는 Linter 도구인데요.

Linter는, 소스 코드를 분석하여 프로그램 오류, 버그, 스타일 오류, 의심스러운 구조체에 표시(flag)를 달아놓기 위한 도구들을 가르킨다
출처: wikipedia

JavaScript를 위한 ESLinter, Python환경에서의 Pylint 등 다양한 환경에서 linter는 존재 하고, kotlin에서는 ktLint 라는 Linter를 사용할 수 있습니다.

Lint는 스웨터의 보푸라기 것을 부를 때 사용 한다고 합니다. 기능에 영향을 주요하게 주진 않지만, 지켜야 할 컨벤션이 잘 지켜지지 않는 프로그램을 마치 손질이 필요한 보푸라기가 많은 스웨터로 비유한 것이 아닐까 싶습니다. Linter가 그런 보푸라기를 제거해주는 거죠.

프로젝트를 시작 하면 Convention이 정말 중요하다고 느껴질 때가 많은데요. 초기 단계에서 사소한 공백이나 줄바꿈으로 많은 의사소통 비용이 들더라고요. 이후에 기능을 개발 하기에도 급급하다 보니 컨벤션을 꼼꼼하게 지키기도 어려웠고, 공동 작업자가 놓친 컨벤션에 일일이 리뷰를 달기도 주저하게 되더라고요.

Linter를 사용 해서 Convetion의 기준을 명확하게 정하고, buildCI 단계에서 항상 보푸라기를 정리해준다면 매우 유용할겁니다.

KtLint

저는 kotlin 진영에서 자주 사용 되는 pinterest에서 만든 ktlint를 사용 하였습니다.

repo: https://github.com/pinterest/ktlint
docs: https://pinterest.github.io/ktlint/latest/

Kotlin 공식 컨벤션을 지키고 있다는 점과 .editorconfig를 지원하여 rule를 custom 할 수 있고, 필요에 의해 몇몇 Rule을 비활성화 할 수 있습니다. 기본적으로 컨벤션을 지원하고 프로젝트에 맞게 설정 할 수 있다는 점이 큰 장점인 것 같습니다.

Quick Start에서는 ktlint를 설치하여 CLI 를 통해 formatting을 수행하는 방법이 소개되어 있지만, gradle 빌드 도구를 사용 하는 프로젝트에 적용하기 위해 wrapping 프로젝트를 사용 하였습니다.

Provides a convenient wrapper plugin over the ktlint project.
JLLeitschuh/ktlint-gradle

star수도 많고 최신 release가 두 달도 안된 살아있는 프로젝트라 사용 하였습니다.
해당 plugin을 사용하면, ktlint를 직접 설치할 필요 없이 gradle을 통해서 ktlint를 간편하게 사용할 수 있습니다.

KtLint-gradle

간단하게 플러그인을 추가할 수 있습니다.
build.gradle.kts

plugins {
    id("org.jlleitschuh.gradle.ktlint").version("12.1.1")
}

repositories {
  mavenCentral()
}

이후 새로 고침을 하면 formatting/ktlintFormat, verification/ktlintCheck 등 여러 태스트가 추가된 걸 확인할 수 있습니다.

이후, build를 하거나

plugin을 추가하면 자동으로 build task에 포함됩니다.

$ ./gradlew check --dry-run

ref: https://github.com/JLLeitschuh/ktlint-gradle/tree/main?tab=readme-ov-file#tasks-added

ktlintCheck를 수행하면 코드에 대한 rule을 체크하고 이에 대한 report(보고서)도 만들어줍니다.

$ ./gradlew ktlintCheck

결과

이렇게 FAILED와 함께 어느 파일이 어떤 rule을 어겼는지 나옵니다. (좀 많네요...)

그리고 ./build/report/ktlint 경로에 세부적인 보고서가 나옵니다.
저는 보고서 형식을 json으로 변경하였습니다.

build.gradle.kts

ktlint {
    reporters {
        reporter(ReporterType.JSON)
    }
}

리포트를 보고 해당 파일로 찾아가서 직접 수정 할 수 있습니다.
다른 방법으로는 formatting/ktlintFormat을 통해 자동 수정도 가능합니다.

$ ./gradlew ktlintFormat

사소하지만 잘 지켜지지 않았던 부분들은 자동으로 수정 되었습니다.

하지만, ktlintFormat을 사용할 때는 모든 컨벤션이 자동 수정 되지 않는다는 점과 의도하지 않은 방향으로 수정 될 수도 있는다는 점을 인지하고 있어야 합니다.

editorconfig

Ktlint를 적용하여 놓치고 있던 여러 부분을 검증 받았는데요. 하지만, 몇가지 예외사항이 존재하였습니다.
현재 프로젝트에서는 port-adapter패턴을 사용중이라 in이라는 이름의 패키지를 사용 하고 있었습니다.
kotlin에서는 in 이라는 예약어가 존재하여 백틱(`)을 사용하여 아래 처럼 선언 되는데,

import com.celuveat.member.application.port.`in`.command.SocialLoginCommand

이 부분이 패키지 rule에 어긋나서 빌드가 실패 되는 문제가 있었습니다.
패키지 이름도 중요한 컨벤션이지만, 코드에 비해 상대적으로 양이 적고 쉽게 발견 할 수 있기 때문에 rule 검사에선 제외하기로 하였습니다.
이외에도 Ko-test 환경에서의 indent 스타일, ktlint에서 정의한 multiline-expression-wrapping을 제거하였습니다.

특정 rule 검사를 무시하려면 .editorconfig 파일에 정의하여야 합니다.

kotlin dsl에서 정의하는 방법은 0.48 버전 이후로 deprecated 되었습니다.
disabledRules.set(setOf("final-newline")) // not supported with ktlint 0.48+

Ktlint에서는 Intellij와의 충돌을 최대한 방지하고자 기본적인 intellij idea configuration을 제공합니다.
https://pinterest.github.io/ktlint/latest/rules/configuration-intellij-idea/

root = true

[*]
insert_final_newline = true

[{*.kt,*.kts}]
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL

#  Disable wildcard imports entirely
ij_kotlin_name_count_to_use_star_import = 2147483647
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
ij_kotlin_packages_to_use_import_on_demand = unset

이후 비활성화 하려는 rule을 추가하였습니다.

root = true

[*]
insert_final_newline = true

[{*.kt,*.kts}]
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL

#  Disable wildcard imports entirely
ij_kotlin_name_count_to_use_star_import = 2147483647
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
ij_kotlin_packages_to_use_import_on_demand = unset

# 패키지 네이밍 비활성화
ktlint_standard_package-name = disabled

# mutliline-expresion-wrapping 비활성화
ktlint_standard_string-template-indent = disabled
ktlint_standard_multiline-expression-wrapping = disabled

# Ko-test 에서 indent 스타일 체크 비활성화
[src/test/kotlin/**.{kt,kts}]
ktlint_standard_indent = disabled
ktlint_standard_string-template-indent = disabled

editorconfig가 아닌, 코드내에서 비활성화를 결정 시키고 싶은 경우 아래 어노테이션을 추가하여 구성할 수도 있습니다.

@Suppress("ktlint:standard:multiline-expression-wrapping")

공식문서에서 각각 rule에 대한 구성 방법에 대해 자세하게 나와있으니 참고하면 좋을 것 같습니다.

이를 적용하고 다시 수행해보면

$ ./gradlew ktlintCheck


잘 통과 하는 것을 확인할 수 있습니다!

PreCommit, PrePush hook에 등록

매번 수행하는 것이 귀찮거나 까먹을 것 같다면,

  • help/addKtlintCheckGitPreCommitHook
  • help/addKtlintFormatGitPreCommitHook
    위 태스크를 활용하여 커밋 전에 수행 되도록 설정 할 수 있습니다.
$ ./gradlew addKtlintCheckGitPreCommitHook

$ ./gradlew addKtlintFormatGitPreCommitHook

이후, git 루트 경로에서 ./.git/hooks에 pre-commit 파일이 추가되는데요.

아래와 같은 script가 생성 됩니다.

plugin에서는 precommit에 등록되는 task만 제공하는 데요. 위에서 생성된 스크립트를 pre-push 파일에 적용(hook)하면 git push단위로 ktlint를 실행 시킬 수 있습니다.


kotlin official 컨벤션을 따르는 것도 중요하지만, 팀내에서 잘 공유 되고 합의된 규칙을 지치는 것이 더 중요하다고 생각한다. official convention을 기반으로 적절히 설정하여 팀내 컨벤션이 잘 지켜지도록 사용 한다면,생산성 있게 프로젝트가 진행되는데 도움이 될 것이다!!


출처

profile
앞으로 넘어지기

0개의 댓글