최근 컨벤션에 대한 논의를 진행하면서, 협업용 도구에 대해서 관심이 많아지게 되었다.
코딩 컨벤션을 준수하는 것은 코드의 가독성 증진에 기여하고 일관된 코드 스타일을 보장한다는 점에서 중요하다.
최근 주로 사용하는 언어인 Kotlin은 공식 코딩 컨벤션이 존재한다.
그럼, 이걸 프로젝트에 강제성을 띄는 방향으로 적용해야 하는데..
강제성을 띄게 하는 방법은 간단하다.
린터를 도입해서 자동으로 코드를 검사하고, 코딩 컨벤션에 위배되는 코드는 깃에 반영하지 못하게 막는 것.
Github Action과 연동해서 Pull-Request에 대한 검증을 수행하는 방식이 제일 먼저 떠올랐다.
진행하다보니 단순하면서 치명적인 문제에 직면했다.
두 자리수가 넘는 커밋을 포함하는 PR을 올렸는데 만약..
린팅 실패가 난다면??
그럼 모든 커밋과 PR을 rebase하여 수정하여야 하나??
그래서, 각 커밋을 작성하는 단계에서 린터를 동작시킬 수 있는 방법을 생각해보았다.
첫번째 시도: Ktlint 적용
프로젝트에 적용할 방법도 찾았겠다, 이제 린터만 정하면 되는데...
찾아보니 Kotlin 코딩 스타일 체크에는 Ktlint라는 린터가 제일 무난하다고 하더라.
그래서 Ktlint를 프로젝트에 적용해보기로 했다.
Gradle에 Ktlint 추가
가장 먼저 프로젝트에 Ktlint를 추가해주어야 한다.
내가 사용하는 빌드 시스템은 Gradle인데, 아래와 같이 Ktlint를 build.gradle.kts에 추가해주면 된다.
plugins {
id("org.jlleitschuh.gradle.ktlint") version "x.x.x"
}
위의 설정이 끝이다. 이것만 해도 ./gradlew ktlintCheck 명령어를 통해 Ktlint를 동작하는데는 문제가 없다.
Ktlint를 pre-commit으로 Git Hook에 추가해야, 우리가 당초 원하던 강제성을 부여할 수 있다.
다행히, 자동으로 Git Hook을 추가하는 ./gradlew addKtlintCheckGitPreCommitHook 명령어가 존재했다.
Running ktlint over these files:
src/main/kotlin/com/jdh213/test/TestEntity.kt
companion object {
fun test(test: Test) = TestEntity()
}
코틀린 공식 코딩 컨벤션에 위배되게끔, 아래와 같이 수정하고 커밋해보자.
companion object { fun test(test: Test) =
TestEntity()
}
FAILURE: Build failed with an exception.
- What went wrong:
Execution failed for task ':ktlintMainSourceSetCheck'.
A failure occurred while executing org.jlleitschuh.gradle.ktlint.worker.ConsoleReportWorkAction
KtLint found code style violations. Please see the following reports:
- /Users/jdh213/lint-test/build/reports/ktlint/ktlintMainSourceSetCheck/Test.tx
Ktlint에서 Detekt로 변경
Detekt도 적용 자체는 그리 어렵지 않다.
plugins {
id("io.gitlab.arturbosch.detekt") version "x.x.x"
}
문제는 정적 도구 분석에 대한 옵션값 설정에 따라 작동이 다르다는점이다.
yml 파일안의 설정부분은,
build:
maxIssues: 0
config:
validation: true
warningsAsErrors: true
- build/maxIssue: 0이 아닌 경우, 설정된 개수 이상의 문제가 발생되면 더 이상 분석을 하지 않고 빌드를 캔슬한다.1
- config/warningsAsErrors: 경고도 전부 에러로 간주하여 빌드를 실패시킬지 여부를 결정한다.
complexity:
active: true
TooManyFunctions:
active: true
thresholdInFiles: 30
thresholdInClasses: 20
thresholdInInterfaces: 10
thresholdInObjects: 20
thresholdInEnums: 5
ignoreDeprecated: true
ignorePrivate: true
ignoreOverridden: false
- complexity/active: 코드 복잡도 분석 룰을 켤지를 결정한다.
- Complexity/TooManyFunctions: 함수 개수에 대한 코드 복잡도 분석 룰을 설정한다.
- active: 해당 룰을 사용할지 여부를 결정한다.
- thresholdInFiles: 한 파일에 몇 개까지의 함수를 허용할지 결정한다.
- thresholdInClasses: 클래스에 몇 개까지의 함수를 허용할지 결정한다.
- thresholdInInterfaces: 인터페이스에 몇 개까지의 함수를 허용할지 결정한다.
- thresholdInObjects: 오브젝트에 몇 개까지의 함수를 허용할지 결정한다.
- thresholdInEnums: 열거형에 몇 개까지의 함수를 허용할지 결정한다.
- ignoreDeprecated: @Deprecated 어노테이션이 설정된 함수를 카운트에서 제외할지 결정한다.
- ignorePrivate: Priavte 접근 제어자가 설정된 함수를 카운트에서 제외할지 결정한다.
- ignoreOverridden: 상위 클래스의 함수를 오버라이드한 함수를 카운트에서 제외할지 결정한다.
이런 식으로 룰 별로 상세하게 원하는 동작을 설정할 수 있다.
다른 룰들에 대해서는, Detekt 공식 가이드 문서에 자세히 설명되어 있다.