Kotlin 프로젝트에 정적 분석 도구 적용하기

콜트·2022년 1월 19일
0
post-thumbnail

정적 분석이란?

소스 코드를 실제로 실행하지 않고 코드를 분석하는 디버깅 방법을 말한다. 반대로 동적 분석은 소스 코드를 실제로 실행하여 코드를 분석하는 디버깅 방법을 말한다.

정적 분석 vs 동적 분석

BASIS FOR COMPARISONSTATIC TESTINGDYNAMIC TESTING
BasicDoes not execute the softwareExecution of the software is necessary
CostLowHigh
Statement coverage100%50%
Time consumptionLessMore
UncoversLarge variety of bugsLimited types of bugs
PerformedBefore compilationOnly when executables are available

사용 효과

Speed

개발자가 리뷰를 할 수 있는 속도엔 한계가 있다. 일정을 진행하기에 바쁜데 개발된 코드들을 전부 직접 검토한다면 소요되는 시간이 많을 것이다. 이러한 문제를 자동화 도구로 빠르게 해결할 수 있으며, 어디에 문제가 있는지 표시를 해주기 때문에 에러를 빠르게 고칠 수 있다.

Depth

개발자가 모든 실행 경로를 직접 확인하기는 힘들 것이다. 하지만 분석 도구는 그러한 심층 분석을 손쉽게 할 수 있다.

Accuracy

코드 리뷰는 사람의 착오가 생길 수가 있다. 분석 도구를 이용하면 정해진 규칙과 기준에 따라 분석을 하기 때문에 착오가 발생하지는 않는다.

Security

애플리케이션 보안을 강화하여 DevOps의 보안을 강화할 수 있다.


코틀린 정적 분석 도구 (Kotlin Static Analysis Tool)

  • Kotlin 언어에 대한 정적 분석 도구로는 대표적으로 Android Lint, Detekt, ktlint의 세가지가 있다.

  • Java 언어에 대한 정적 분석 도구로 주로 사용되지만, SonarQube에서도 설정을 통해 Kotlin 언어에 대한 정적 분석을 수행할 수 있다.

기능 비교

  • ktlint의 경우, 공식 Kotlin 스타일을 따르지 않는 모든 스타일, 들여쓰기 또는 줄 바꿈에 대해 코드를 자동으로 스캔하는 정적 분석기다. 코드 컨벤션을 정의하기에 유용할 수 있다.
  • Detekt의 경우 코드 복잡성, 잠재적 버그, code smell, 처리되지 않은 예외 및 기타 여러 가지 분석을 포함하여 좀 더 복합적인 분석을 제공해준다. 다만, ktlint에 비해 구성하기가 더 복잡하다.
    • 원한다면 gradle 설정에 ktlint의 formatting rule에 대한 의존성을 추가해서 detekt가 제공해주는 ktlint wrapper를 사용할 수도 있다.
  • ktlint, detekt 모두 확장 가능하며 CI Gradle 스크립트로 구현해서 검사를 자동화 할 수 있다.

한계

정적 분석 도구에도 아래와 같이 한계가 존재한다. 이는 팀원과의 코드 리뷰를 통해서 해결해야한다.

  • 개발자의 의도를 완벽히 이해하지는 못한다.
  • 네이밍의 의미까지 파악하지는 못한다.

코틀린 정적 분석 도구 Detekt 적용 가이드

본 가이드는 Kotlin DSL로 작성된 Spring Boot + Gradle 프로젝트를 기준으로 한다.

build.gradle.kts

root 디렉토리에 존재하는 build.gradle.kts 에 아래의 종속성을 추가한다.

자세한 설명이 궁금하다면 Run detekt using the Detekt Gradle Plugin | A static code analyzer for Kotlin 를 참고하도록 한다.

plugins {
    id("io.gitlab.arturbosch.detekt").version("1.19.0")
}

repositories {
    mavenCentral()
    // 중요! 정적 분석 이후 report로 출력하기 위해서는 아래의 repository 주소를 추가로 등록해주어야 한다. // 등록해주지 않은 경우 아래에 후술할 detekt 작업이 동작하지 않는다.
    // 이슈에 대해 상세한 원인이 궁금하다면 https://github.com/detekt/detekt/issues/3461 를 참고하도록 한다.
    maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven")
} 

이 때 버전은 Compatibility Table | A static code analyzer for Kotlin 을 참고하여 지정하도록 한다.

위 과정을 완료하고 gradle reload까지 진행하면, 인텔리제이 기준 우측 gradle - Tasks - verification 탭에서 아래 사진과 같이 detekt 작업을 볼 수 있다.

만약 커맨드로 실행하고 싶다면 아래와 같이 터미널에 입력하면 된다(물론 실행권한이 부여되어 있어야 한다).

./gradlew detekt

코드 분석

detekt 작업을 실행했을 때, code style, performance 등등.. 문제의 소지가 발생할 수 있는 지점들이 존재하면 아래 사진과 같이 실패한다.

분석에 대한 결과 리포트는 기본적으로 build/reports/detekt 디렉토리에 존재한다. 확장자의 종류로는 html, txt, xml, sarif 가 있다.

분석 결과

분석 결과로는 콘솔에 출력되는 로그와 분석 결과 리포트로 생성되는 파일이 있다. 기본적으로 지정되는 분석 결과 생성 디렉토리인 build/reports/detekt 의 detekt.html 파일을 열어보면 아래와
같이 나오며, Metrics(측정 기준), Complexity Report(복잡성 보고서), Findings(발견한 취약점의 수) 을 확인할 수 있으며, 이유와 함께 표시해준다.

이때 표시되는 취약점의 원인들에 대해서, 설정 파일을 통해 제한 조건을 지정해줄 수 있다.
설정 항목에 대해 자세한 내용은 detekt의 메뉴중, Rule Sets를 참고하도록 한다.

검사 비활성화 설정

detekt 작업은 gradle의 check task 실행시에 기본적으로 수행되도록 설정되어 있는 검사 작업이다. 하지만 상황에 따라서는 점진적으로 코드 품질을 높이고 싶을 수가 있다. 이럴 경우에 아래
script를 build.gradle.kts에 작성하면 detekt 작업을 gradle이 수행하는 check task 실행시에 실행되지 않도록 비활성화 할 수 있다.

tasks.named("check").configure {
    this.setDependsOn(this.dependsOn.filterNot {
        it is TaskProvider <*> && it.name == "detekt"
    })
}

하지만 만약 비활성화 대신, 약하지만 제한을 건다던지 설정을 해주고 싶다면 설정 파일(yml로 작성되는)에서 빌드시 실패하도록 만들 임계값을 설정해줄 수 있다. 이 외에도 검사 규칙과 같은 여러 가지 것들에 대한
설정들을 직접 지정할 수 있도록 지원하고 있는데, 자세한 내용은 Detekt Configuration File 를 참고하도록 한다.

Git pre-commit hook

버전 관리 시스템으로 Git을 사용하고 있다면, Commit 시에 hook을 발생시키도록 하여 검사를 수행할 수 있다. Detekt 에서는 Git Commit 시에 사용자 지정 스크립트를 자동으로 실행하는 방식으로
이를 지원해준다.

아래는 Run detekt using a Git pre-commit hook | A static code analyzer for Kotlin 에서 제공해주는, Git Commit 시에 실행될 스크립트 예제이다.

# !/usr/bin/env bash

echo "Running detekt check..."
OUTPUT="/tmp/detekt-$(date +%s)"
./gradlew detekt > $OUTPUT 
EXIT_CODE=$? 
if [ $EXIT_CODE -ne 0 ]; then 
  cat $OUTPUT rm $OUTPUT
  echo "***********************************************"
  echo "                 Detekt failed                 "
  echo " Please fix the above issues before committing "
  echo "***********************************************"
  exit $EXIT_CODE 
fi 
rm $OUTPUT 

가령, 위 스크립트를 복사해서 <<your-repo>>/.git/hooks/pre-commit 디렉토리에 설치하여 사용할 수 있다. 단, 이 스크립트는 실행가능한 파일이어야 하므로 사용 권한의
변경(chmod +x pre-commit)이 필요하다.

Git Hooks 에 대해서 더 알아보고 싶다면 Git Hooks | Atlassian Git Tutorial 를 참고하도록 한다.

플러그인

Detekt는 인텔리제이 플러그인도 지원하고 있다.

우선, Preferences → Plugins 에서 Detekt 플러그인을 설치한다.

Preferences → Tools → Detekt 항목에서 플러그인에 대한 설정을 진행할 수 있으며, 원한다면 설정 파일을 직접 지정해줄 수 있다.

각각의 옵션들에 대한 설명은 아래와 같다.

  • Enable Detekt → 파일 수정시 Detekt가 실행되도록 한다.
  • Enable rules upon the default configuration → 안정적인 규칙을 기본 구성으로 사용하는 기본 detekt 구성을 사용한다. 제공된 추가 구성은 속성을 재정의할 수 있다.
  • Enbale formatting (ktlint) rules → 코드 서식과 관련된 규칙에 ktlint 규칙을 추가한다.
  • ktlint 는 Kotlin의 공식 가이드의 규칙을 포함하여 코드 스타일을 검사하고, 맞춰주는 도구이다.
  • Enable all rules → 실험적인 규칙을 고려하더라도 deteckt의 모든 기존 규칙을 활성화한다. 제공된 추가 구성은 여전히 활성 속성을 재정의할 수 있다.
  • Treat detekt findings as errors → 탐지 결과를 경고 대신 오류로 표시한다.

코틀린 정적 분석 도구 ktlint 적용 가이드(작성 예정)


참고자료 및 출처

profile
개발 블로그이지만 꼭 개발 이야기만 쓰라는 법은 없으니, 그냥 쓰고 싶은 내용이면 뭐든 쓰려고 합니다. 코드는 깃허브에다 작성할 수도 있으니까요.

0개의 댓글