이번에 Java로 새로운 프로젝트를 진행하면서 코드 컨벤션을 맞추는 것에 대해서 고민을 많이 했습니다. 코드 컨벤션을 맞춰주는 도구를 Lint 혹은 Linter라고 칭하는 것을 알게 되었습니다. 코틀린은 ktlint가 국룰로 잡혀있기 때문에 린트 선정에 고민이 없지만, 자바는 국룰이 없는 것 같았습니다. 그래서 내가 어떤 Lint을 원하는가 고민을 많이 했습니다.
- 린트 적용이 강제여야한다.
- 린트 적용이 안되어 있는 부분이 있다면 쉽게 알려주어야한다.
- 린트가 적용이 안되어 있는 부분이 있다면, 간단하게 단축키 혹은 gradle task로 수정되어야 한다.
크게 다음과 같은 세가지 요구사항이 있다는 것을 알게되었습니다. 그래서 찾아보던 중 sonarLint와 checkStyle이 있다는 것을 알게되었습니다.
SonarLint와 CheckStyle 비교
위에서 말한 세가지 기준을 중점적으로 비교해보겠습니다.
SonarLint
단점
SonarLint는 너무 아쉽게도 IDE 플러그인으로써, 린트 적용을 강제화할 순 없다는 것을 알게되었습니다. 도중에 알게된 SonarCloud라는 깃허브 CI 분석 도구로 강제로 머지를 막을 순 있다고 생각했지만, 깃허브로 푸쉬를하고 코드 컨벤션에 맞지 않는 부분이 있다는 것을 알게 되면, 수정을 하는 플로우 자체가 부담으로 느껴졌습니다. 개발 환경에서 바로바로 알 수 있는 것이 제가 원하는 거였으니까요.
장점
그러나 장점이 꽤나 많아서 고민을 계속하게 되었습니다. 코드 컨벤션 뿐만 아니라 코드 스멜, 즉 코드가 제대로 짜여져 있지 않는 부분을 잡아주기도 하고, 코드에 보안에 취약한 부분을 잡아주기도 하는 등 좋은 점이 매우 많았습니다. 그리고 플러그인이라서 적용도 매우 간단하다는 점이 있기도 하였습니다.
CheckStyle
장점
CheckStyle은 코드 컨벤션에 어긋나는 부분을 html 문서로 보기 매우 쉽게 제공해줍니다.
그리고 gradle에 의존성을 추가할 수 있는 라이브러리로써 gradle 테스트를 추가해서, 빌드를 실패하게 만들 수 있습니다.
단점
린트가 적용이 안되어 있는 부분이 있다면, 단축키로 바로 변경할 수 있다면 좋겠지만 그건 힘들어서, IntelliJ의 힘을 빌려야 한다는 점이 있습니다. 그리고 사실 개발할 때만 사용되는 라이브러리가 gradle에 의존성으로 들어가 있는 것도 조금 부담으로 느껴지긴 했습니다.
저는 CheckStyle을 선택했습니다. 약간은 투박하게 느껴질 수 있는 도구이지만, 제가 원하는 일은 정말로 잘 수행하는 라이브러리라고 판단한 것이 이유입니다.
CheckStyle 적용해보기
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.1'
id 'io.spring.dependency-management' version '1.1.4'
//checkstyle
id 'checkstyle'
}
먼저 플러그인에 checkstyle을 추가합니다.
//checkstyle
compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'
tasks.withType(Checkstyle) {
reports {
xml.required = true
html.required = true
}
}
checkstyle {
maxWarnings = 0
configFile = file("checkstyle/naver-checkstyle-rules.xml")
configProperties = ["suppressionFile": "checkstyle/naver-checkstyle-suppressions.xml"]
}
그리고 다음과 같이 task를 커스텀 할 수 있습니다. 약간을 설명을 하자면, 위에서부터 main 코드와 test 코드의 인코딩을 UTF-8로 설정하고, 위에서 본 테스크 결과를 xml과 html로 받겠다고 하는 것입니다. 그리고 밑에는 허용되는 최대 warning의 수는 0개입니다. 즉 warning이 1개라도 있다면 task가 실패한다는 것이죠. 그리고 밑엔 convention이 정의된 파일을 설정할 수 있습니다.
저는 컨벤션 파일로 네이버 핵데이 코드 컨벤션을 사용했습니다. https://github.com/naver/hackday-conventions-java 여기서 컨벤션 파일을 볼 수 있고, 다음과 같이 루트 경로 밑에 checkstyle 경로 밑에 두면 잘 인식이 되게 사용할 수 있습니다.
다음과 같이 설정하면, gradle task에
다음과 같이 checkstyleMain과 checkstyleTest가 추가된 것을 볼 수 있습니다. checkstyleMain은 main에 있는 코드에 관해서 검토를 진행하는 것이고, 각각의 테스크를 진행하면,
다음과 같이 html 파일이 생긴 것을 확인할 수 있습니다.
코드컨벤션 강제화
그러면 이제, 어떻게 커밋 전에 그리고 CI시에 코드컨벤션을 강제했는지 알아보겠습니다.
커밋 전
커밋 전에는 git hook의 pre-commit을 사용해서 강제화 하였습니다.
다음의 경로의 파일에 접근하여서, 다음과 같이 gradle task를 실행하게 설정하였습니다.
#/bin/bash
CWD=$(pwd)
MAIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd $MAIN_DIR/../.. # Modified the path to navigate up two directories
# set gradlew executable
chmod +x ./gradlew
# Run clean task
./gradlew clean > /dev/null
# Run checkstyleMain task
./gradlew checkstyleMain > /dev/null
# Check if the checkstyleMain task failed
if [ $? -ne 0 ]; then
echo
echo "-----------------description-----------------"
echo "==> Error while checkstyleMain"
cd $CWD
exit 1
fi
# Run checkstyleTest task
./gradlew checkstyleTest > /dev/null
# Check if the checkstyleTest task failed
if [ $? -ne 0 ]; then
echo
escho "-----------------description-----------------"
echo "==> Error while checkstyleTest"
cd $CWD
exit 1
fi
cd $CWD
여러 gardle 테스크를 실행하다보니 커밋을 하고, 5초정도 기다려야한다는 단점이 있긴 하지만, push하기 전에 개발환경에서 문제가 있는지 알 수 있다는 장점이 있습니다.
CI 시
저는 github action을 사용했습니다. build시에 코드 컨벤션에 문제가 있다면, 바로 failed되기 때문에 github action에서 gradle build하는 것으로 검증을 할 수 있습니다. 그리고 빌드할 때에 코드 컨벤션 확인을 분리했다면, 다음과 같이 코드를 작성하면 CI시 검증을 할 수 있습니다.
name: Java CI with Gradle
on:
pull_request:
branches: [ "master" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew clean build
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Execute checkstyleMain
run: ./gradlew clean checkstyleMain
- name: Execute checkstyleTest
run: ./gradlew clean checkstyleTest
끝내며
코드리뷰시에 컨벤션으로 지적하지 않아도 되는 것은 혁명적입니다. 매우 귀찮은 작업이었나봅니다. 사실 우리는 우리 주변에서 존재하는 여러가지 비효율을 느끼지 못하며 살아가고 있습니다. 그런 것들을 하나하나 해결해보고 싶습니다 ㅎ. 글 읽어주셔서 감사합니다! bye~
'기술고민' 카테고리의 다른 글
???: 빌더 패턴은 필수 값을 받지 못하잖아요. (3) | 2024.03.26 |
---|---|
정팩메, 생성자 고를 때 무엇을 고려해야 할까? (2) | 2024.03.25 |
낙관적 락으로 동시성 이슈 해결하기 (0) | 2024.03.21 |
Repository없이 EntityManager 사용하기 (1) | 2023.12.20 |
비즈니스의 흐름이 잘 보이는 코드와 설계 (0) | 2023.12.05 |