1. 들어가며
소프트웨어 개발에서 코드 스타일을 일관되게 유지하는 것은 매우 중요합니다. 코드 스타일이 통일되지 않으면 협업이 어려워지고, 유지보수가 복잡해질 수 있습니다.
이번 디프만 프로젝트에서 Checkstyle을 사용하고 네이버 자바 코딩 컨벤션을 적용하려 했으나, 팀원 모두가 IntelliJ에서 세팅을 해야 하고 매번 확인해야 하는 단점이 있었습니다.
따라서 Spotless 플러그인을 사용하고, Git Pre-commit 훅을 통해 코드 포맷팅을 자동화하는 방식을 선택하게 되었습니다.
https://naver.github.io/hackday-conventions-java/
캠퍼스 핵데이 Java 코딩 컨벤션
중괄호({,}) 는 클래스, 메서드, 제어문의 블럭을 구분한다. 5.1. K&R 스타일로 중괄호 선언 클래스 선언, 메서드 선언, 조건/반복문 등의 코드 블럭을 감싸는 중괄호에 적용되는 규칙이다. 중괄호
naver.github.io
2. 코드 포맷팅(Code Formatting)이란?
코드 포맷팅이란 코드의 가독성을 높이고 일관된 스타일을 유지하기 위해 코드의 들여쓰기, 공백, 줄 정리, 중괄호 위치 등의 규칙을 정리하는 것입니다.
🔎 코드 포맷팅 전
public class Example {public static void main(String[]args){System.out.println("Hello, World!");}}
🔎 코드 포맷팅 후
public class Example {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
📍 포맷팅 요소
- 들여쓰기(Indentation) → 코드 블록을 들여써서 계층을 명확히 함
- 공백(Spacing) → 연산자 주변, 함수 호출 등에서 적절한 공백 추가
- 중괄호 위치(Bracing Style) → 한 줄에 배치할지, 줄 바꿈할지 설정
- 줄 길이(Line Length) → 너무 긴 줄을 적절히 줄바꿈하여 가독성 향상
- 불필요한 코드 정리 → 사용되지 않는 import 제거 등
팀원 간의 코드 스타일이 다르면 유지보수가 어려워지므로, 통일된 코드 스타일을 유지하는 것이 중요합니다.
하지만 개발자가 매번 직접 포맷팅 규칙을 지키는 것은 쉽지 않기 때문에 Spotless, Checkstyle 같은 Formatter를 사용하는 것이 효율적입니다.
3. Spotless란?
Spotless는 코드 스타일을 자동으로 정리해주는 Gradle 플러그인입니다. Java, Kotlin, Python 등 다양한 언어에서 사용할 수 있으며, 일관된 코드 포맷팅을 유지하는 데 도움을 줍니다.
https://github.com/diffplug/spotless
GitHub - diffplug/spotless: Keep your code spotless
Keep your code spotless. Contribute to diffplug/spotless development by creating an account on GitHub.
github.com
1️⃣ Spotless 적용
먼저, build.gradle의 plugins 섹션에 Spotless 플러그인을 추가합니다.
plugins {
id 'com.diffplug.spotless' version '6.20.0'
}
그 다음 코드 스타일을 정리하기 위해 build.gradle에 아래 설정을 추가합니다.
spotless {
java {
// Import 순서
importOrder("java", "javax", "jakarta", "org", "com")
// 사용하지 않는 import 자동 제거
removeUnusedImports()
//구글 자바 포맷 적용
googleJavaFormat()
// 파일 끝에 개행 추가
endWithNewline()
// 각 라인 끝에 공백 제거
trimTrailingWhitespace()
// 애노테이션 포맷팅 정리 (한 줄에 한 개씩 배치)
formatAnnotations()
}
}
저는 Google Java Format을 적용했고, 아래와 같은 요소들을 추가했습니다.
- Import 순서 정리
- 불필요한 import 자동 제거
- 파일 끝 개행 추
- 각 라인 끝에 공백 제거
이렇게 설정한 후 ./gradlew spotlessApply를 실행하면 위의 규칙에 맞게 코드가 자동으로 정리됩니다.
2️⃣ Git Pre-commit 훅 추가 (scripts/pre-commit)
Spotless가 적용되었지만, 개발자가 명령어를 실행하는 것을 깜빡하면 스타일이 일관되지 않을 수 있습니다.
이를 방지하기 위해 Git Pre-commit 훅을 추가하여 커밋 전에 자동으로 spotlessApply가 실행되도록 설정했습니다.
프로젝트의 scripts/ 폴더 아래 pre-commit이라는 파일을 생성하고, 아래 내용을 추가합니다.
- 커밋을 시도할 때, spotlessApply를 실행하여 코드 스타일을 자동 정리합니다.
- Spotless 실행이 실패하면, 커밋이 차단됩니다.
- 성공적으로 실행되면, 수정된 파일을 다시 스테이징하여 커밋에 반영합니다
#!/bin/sh
PROJECT_ROOT=$(git rev-parse --show-toplevel)
stagedFiles=$(git diff --staged --name-only)
echo "Running spotlessApply. Formatting code..."
cd $PROJECT_ROOT && ./gradlew spotlessApply
if [ $? -ne 0 ]; then
echo "Spotless apply failed!"
exit 1
fi
for file in $stagedFiles; do
if test -f "$file"; then
git add $file
fi
done
3️⃣ Pre-commit 자동 설정 (build.gradle)
.git 디렉터리는 깃허브에 올릴 수 없고, 모든 사람이 수동으로 자신의 .git/hooks 폴더에 파일을 넣는 것은 번거롭기 때문에 Gradle Task를 활용해 훅을 자동으로 복사하고 실행 권한을 부여하게 설정했습니다.
build.gradle에 아래 설정을 넣어줍니다.
- updateGitHooks Task는 scripts/pre-commit 파일을 .git/hooks/ 폴더로 복사합니다.
- makeGitHooksExecutable Task는 OS에 따라 실행 권한을 부여합니다.
- Java 코드가 컴파일될 때 (JavaCompile Task 실행 시) 자동으로 Pre-commit 훅이 설정됩니다.
tasks.register("updateGitHooks", Copy) {
from(file("${rootProject.rootDir}/scripts/pre-commit"))
into(file("${rootProject.rootDir}/.git/hooks"))
}
tasks.register('makeGitHooksExecutable', Exec) {
if (System.getProperty("os.name").contains("Windows")) {
commandLine("attrib", "+x", "${rootProject.rootDir}/.git/hooks/pre-commit")
} else {
commandLine("chmod", "+x", "${rootProject.rootDir}/.git/hooks/pre-commit")
}
dependsOn("updateGitHooks")
}
tasks.withType(JavaCompile) {
dependsOn("makeGitHooksExecutable")
}
4️⃣ 실행 방법
Spotless 설정이 올바르게 적용되었는지 확인합니다.
./gradlew spotlessApply
아래 명령어를 실행하여 Git Pre-commit 훅을 설정합니다.
./gradlew makeGitHooksExecutable