Spotless
Spotless는 코드 컨벤션을 지정한 규칙에 맞게 설정해 주는 툴입니다.
간단하게 build.gradle에 스크립트를 추가한 후 터미널에서 명령어를 입력하면 적용할 수 있습니다.
1. build.gradle의 최상단에 존재하는 plugins에 코드를 추가합니다.
plugins {
id 'java'
...
id "com.diffplug.spotless" version "6.20.0"
}
2. 공식문서의 Java 코드 부분을 build.gradle에 추가합니다.
spotless {
java {
// Use the default importOrder configuration
importOrder()
// optional: you can specify import groups directly
// note: you can use an empty string for all the imports you didn't specify explicitly, '|' to join group without blank line, and '\\#` prefix for static imports
importOrder('java|javax', 'com.acme', '', '\\#com.acme', '\\#')
// optional: instead of specifying import groups directly you can specify a config file
// export config file: https://github.com/diffplug/spotless/blob/main/ECLIPSE_SCREENSHOTS.md#creating-spotlessimportorder
importOrderFile('eclipse-import-order.txt') // import order file as exported from eclipse
removeUnusedImports()
// Cleanthat will refactor your code, but it may break your style: apply it before your formatter
cleanthat() // has its own section below
// Choose one of these formatters.
googleJavaFormat() // has its own section below
eclipse() // has its own section below
prettier() // has its own section below
clangFormat() // has its own section below
formatAnnotations() // fixes formatting of type annotations, see below
licenseHeader '/* (C) $YEAR */' // or licenseHeaderFile
}
}
여기서, 제가 쓸 부분만 추리고 다 삭제해보겠습니다.
build.gradle
plugins {
...
id "com.diffplug.spotless" version "6.20.0"
}
...
ext.googleJavaFormatVersion = "1.10.0"
dependencies {
...
}
...
spotless {
java {
importOrder()
removeUnusedImports()
googleJavaFormat("$googleJavaFormatVersion")
}
}
import 순서, 사용하지 않는 import문 정리, 자바 컨벤션 이 세 가지의 컨벤션을 적용하도록 설정하였습니다.
3. 실행
설정이 끝난 후 gradle 업데이트를 진행하고, 다음과 같은 명령어를 터미널에 입력합니다.
./gradlew spotlessApply
그럼 프로젝트 전체의 컨벤션을 구글 포맷팅에 맞게 수정합니다.
4. 불편한 점?
이렇게 설정하고 끝난다면, 불편한 점이 발생합니다.
그것은 매번 위의 명령어를 입력해야 하는 것입니다.
실제로 spotless 명령어 사용을 잊은 채, commit을 진행하게 되어 나중에 spotless를 적용하게 되면 다른 내용과 커밋이 섞여버리는 상황이 발생할 수 있습니다.
(예시로 controller 관련 커밋을 진행하고 있는데 컨벤션으로 인해 service, repository까지 모두 커밋 내역에 들어가게 되면 불편하겠죠?)
이러한 문제를 해결하기 위해 pre-commit까지 적용해 보도록 하겠습니다.
Pre commit
Pre commit이란 커밋을 진행하기 전, 개발자가 정해놓은 설정을 수행하도록 하는 것입니다.
위의 Spotless 뿐 아니라 원하는 행동을 스크립트로 작성하면 해당 내용을 커밋 수행 전 실행하게 됩니다.
1. pre-commit 파일 생성
프로젝트 패키지에 scripts
폴더를 만들고, 그 안에 pre-commit
파일을 생성합니다. (확장자가 필요하지 않습니다.)
그 후 아래 명령어를 작성하고 저장합니다.
#!/bin/sh
PROJECT_ROOT=$(git rev-parse --show-toplevel)
# Part 1
stagedFiles=$(git diff --staged --name-only)
# Part 2
echo "Running spotlessApply. Formatting code..."
cd $PROJECT_ROOT && ./gradlew spotlessApply
if [ $? -ne 0 ]; then
echo "Spotless apply failed!"
exit 1
fi
# Part 3
for file in $stagedFiles; do
if test -f "$file"; then
git add $file
fi
done
# 최초 적용시 ./.git/hooks/pre-commit 파일을 생성하고 위의 내용을 복사합니다.
# 스크립트가 변경되면 ./gradlew compileJava를 실행합니다.
명령어를 확인하면, 프로젝트 루트(모듈)가 어디인지 확인하고, stagedFile을 따로 저장합니다.
이후 루트(패키지 메인, 모듈 등..)에서 spotless 명령어를 수행합니다.
만약 spotless가 실패 한다면 멈추고, 성공한다면 stagedFiles에 있던 파일들을 바로 git add 합니다.
처음에는 spotless 할 때, 코드 변경사항이 있으면 바로 커밋하지 않고 다시 커밋버튼을 누를 수 있도록 스크립트를 구성하였는데, 생각보다 번거롭게 느껴져서 바로 커밋될 수 있도록 스크립트를 구성하였습니다.
2. build.gradle 스크립트 추가
build.gradle 하단에 다음 스크립트를 추가합니다. (꼭 하단일 필요는 없습니다)
tasks.register('updateGitHooks', Copy) {
from './scripts/pre-commit'
into './.git/hooks'
}
tasks.register('makeGitHooksExecutable', Exec) {
commandLine 'chmod', '+x', './.git/hooks/pre-commit'
dependsOn updateGitHooks
}
compileJava.dependsOn makeGitHooksExecutable
scripts/pre-commit 파일을. git/hooks에 저장합니다.
추가로 한 번 컴파일하여 설정해놓으면, 따로 커맨드에 별도의 명령어를 입력하지 않도록 build.gradle에서 처리되도록 하는 명령어입니다.
3. compile 실행
./gradlew compileJava
Gradle Task의 updateGitHooks를 하면 된다는 글들을 보았지만, 개인적으로 해당 명령어가 수행되지 않았기 때문에 위의 컴파일 명령어를 입력했습니다.
위의 명령어는 아래의 상황일 때 사용하면 됩니다.
- 최초 사용
- 스크립트 변경
두 가지 상황일 때, compile 명령어를 사용하면 됩니다.
이제 커밋을 진행하면 자동으로 spotless가 적용됩니다.
예시
참조
'Server > Spring&Spring Boot' 카테고리의 다른 글
[Spring] @Controller, @Service, @Repository 어노테이션 차이점. 나만의 @Component 어노테이션 생성 (4) | 2023.04.18 |
---|---|
[Spring Security] UserDetails를 User에 구현하여 사용하기. (0) | 2023.04.14 |
[Refactor] boolean을 사용하여 메서드 정리 (0) | 2023.04.01 |
[Spring] @Builder 사용시, 초기화해야할 필드가 존재할 때 발생하는 에러. @Builder will ignore the initializing expression entirely (0) | 2023.03.25 |
[Docs] Spring Rest Docs HTML 출력하는 법. (0) | 2023.03.22 |