Git 커밋 메시지 작성법: 50자 제목, 명령형, 일관된 대소문자

깔끔한 Git 커밋 메시지는 50자 안팎의 짧은 제목 줄을 명령형으로 쓰고(“Added”가 아니라 “Add”), 끝에 마침표를 붙이지 않습니다. 그다음에 빈 줄을 하나 두고, 72자에서 줄바꿈한 본문에 변경한 이유를 설명합니다. 영어로 커밋 메시지를 쓴다면 대소문자는 한 가지 관례를 정해 끝까지 지키세요. feat:나 docs: 같은 접두사가 있으면 그 뒤의 단어는 보통 소문자로 쓰고, 접두사가 없으면 첫 단어를 대문자로 쓰라는 것이 고전적인 조언입니다.
이 가운데 Git 자체가 강제하는 것은 하나도 없어서 커밋 기록은 시간이 지날수록 제각각이 됩니다. 아래에서 각 규칙과 그 출처, 그리고 기억하지 않아도 되도록 도구가 대신 검사하게 만드는 방법을 설명합니다.
좋은 커밋 메시지의 구조
fix(editor): keep cursor position after paste
Pasting into an empty textarea moved the cursor to the start, because
the change handler replaced the value before restoring the selection.
Restore the selection after updating the value instead.
Fixes #142구성은 세 부분입니다. 커밋이 무엇을 하는지 말하는 제목, 왜 하는지 말하는 본문, 그리고 참조와 메타데이터를 위한 선택 사항인 꼬리말(footer)입니다. Git 공식 문서는 “a single short (no more than 50 characters) line summarizing the change, followed by a blank line and then a more thorough description”(변경 내용을 요약하는 짧은 한 줄(50자 이하), 그다음 빈 줄, 그리고 더 자세한 설명)으로 시작하라고 권합니다. 첫 번째 빈 줄 앞까지는 모두 커밋의 제목으로 취급되며, git log --oneline, git shortlog, git format-patch가 보여 주는 것이 바로 이 부분입니다.
제목 줄: 짧게, 명령형으로, 마침표 없이
50자는 엄격한 한도가 아니라 권장 기준입니다. Git 프로젝트 자체의 SubmittingPatches 가이드도 이 기준을 소프트 리밋(soft limit)이라고 부르며, 기여자에게 “skip the full stop”(마침표는 생략하세요)이라고 요청합니다. 50자 아래로 줄일 수 없다면 그 커밋이 두 가지 일을 하고 있다는 신호인 경우가 많습니다.
같은 가이드는 “describe your changes in imperative mood”(변경 사항을 명령형으로 서술하세요)를 요구합니다. 코드베이스에 명령을 내리듯 영어 동사의 명령형으로 쓰라는 뜻입니다. “make xyzzy do frotz”이지 “makes xyzzy do frotz”나 “changed xyzzy to do frotz”가 아닙니다. Chris Beams의 커밋 메시지 일곱 가지 규칙에는 쓸 만한 테스트가 있습니다. 좋은 제목은 “If applied, this commit will …”(적용하면 이 커밋은 …합니다)이라는 문장을 완성합니다.
- “If applied, this commit will add CSV export”는 말이 됩니다.
- “If applied, this commit will added CSV export”는 말이 되지 않습니다.
- “If applied, this commit will bug fixes”도 말이 되지 않고, 독자에게 아무 정보도 주지 않습니다.
명령형은 Git이 대신 써 주는 메시지, 예를 들어 Merge branch 'main'이나 Revert "…"와도 맞아서, 직접 쓴 커밋도 로그에서 같은 어조로 읽힙니다.
제목의 대소문자: 두 진영
조언이 실제로 엇갈리는 지점이며, 정답은 제목이 접두사로 시작하느냐에 달려 있습니다.
| 관례 | 예시 | 권장하는 곳 |
|---|---|---|
| 접두사 없음, 첫 글자 대문자 | Add CSV export | 2008년 Tim Pope의 커밋 메시지 노트, Chris Beams의 일곱 가지 규칙 |
| 영역 접두사, 그 뒤는 소문자 | doc: clarify sign-off rules | Git 프로젝트의 SubmittingPatches |
| 타입 접두사, 설명은 소문자 | feat: add CSV export | Angular의 커밋 가이드라인, commitlint의 conventional 설정 |
Git 프로젝트의 방식에는 따라 할 만한 세부 사항이 있습니다. 접두사 뒤의 첫 단어만 대문자를 잃고, 그것도 첫 단어이기 때문일 뿐입니다. 항상 대문자로 쓰는 단어는 그대로 둡니다. Git 프로젝트 자신의 예시가 refs: HEAD is also treated as a ref인데, HEAD는 항상 대문자로 쓰기 때문입니다. 같은 논리로 fix: handle iOS paste와 docs: update README badges가 나옵니다.
저희 의견은 이렇습니다. 영어로 쓴다면, 접두사가 있을 때는 이름이나 약어가 아닌 한 첫 단어를 소문자로 쓰고, 접두사가 없으면 대문자로 쓰세요. 방식을 섞어 쓰는 것만이 진짜 실수입니다. git log --oneline이 제목들을 목록으로 만들어 주는데, 몇 줄마다 대소문자가 바뀌는 목록은 성의 없어 보이기 때문입니다. 같은 논리를 문서에 적용한 내용은 일관된 기술 문서 작성법 가이드를 참고하세요.
Conventional Commits 명세가 실제로 말하는 것
Conventional Commits 1.0.0 명세는 다음 구조를 정의합니다.
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]핵심 규칙을 간단히 정리하면 다음과 같습니다.
feat(새 기능)와fix(버그 수정)는 명세가 정의하는 두 가지 타입이며, 각각 Semantic Versioning의 MINOR와 PATCH 릴리스에 해당하고, 호환성을 깨는 변경은 MAJOR에 해당합니다. 다른 타입도 허용됩니다. 명세는build,chore,ci,docs,style,refactor,perf,test를 언급하는데, 이는 Angular의 관례를 바탕으로 한 commitlint의 conventional 설정이 권장하는 타입입니다.- 호환성을 깨는 변경(breaking change)은 콜론 앞에
!를 붙이거나(feat!: drop Node 16 support)BREAKING CHANGE:꼬리말로 표시하며, 이 꼬리말은 반드시 대문자여야 합니다. - 이 형식을 읽는 도구는
BREAKING CHANGE를 제외하고는 대소문자를 구분하면 안 됩니다. 타입을 소문자로 쓸지 대문자로 쓸지에 대해 명세의 FAQ는 어떤 표기든 쓸 수 있지만 “but it's best to be consistent”(일관되게 쓰는 편이 가장 좋다)라고 말합니다.
명세는 제목 길이를 정하지도, 명령형을 요구하지도 않습니다. 그것들은 Git의 관례에서 나온 것입니다.
commitlint가 명세보다 엄격한 부분
형식을 강제하는 일반적인 방법은 commitlint의 @commitlint/config-conventional이며, 그 기본값은 명세보다 더 나아갑니다. 버전 21에 샘플 제목들을 넣어 돌려 봤습니다.
| 제목 | 결과 |
|---|---|
feat: add CSV export | 통과 |
feat: Add CSV export | 실패: 제목은 문장 형식(sentence case)이면 안 됨 |
Feat: add CSV export | 실패: 타입은 소문자여야 함 |
fix: handle empty textarea. | 실패: 마침표 불가 |
fix: README typo | 실패: 이것도 문장 형식으로 간주됨 |
fix: typo in README | 통과 |
README 행을 눈여겨보세요. fix: README typo나 feat: URLSearchParams support처럼 약어나 클래스 이름으로 시작하는 제목은, Git 프로젝트라면 받아들일 만한 같은 형태의 제목인데도 거부됩니다. 약어가 앞에 오지 않도록 문장을 고쳐 쓰거나, 설정에서 subject-case 규칙을 완화하세요. 기본 헤더 길이 제한은 100자여서 commitlint는 50자도 요구하지 않습니다.
본문: 72자에서 줄바꿈하고 이유를 설명하세요
Git은 메시지를 자동으로 다시 줄바꿈해 주지 않습니다. 2008년 Tim Pope의 설명은 지금도 유효합니다. git log는 메시지를 네 칸 들여써서 보여 주므로, 80열 터미널에서 오른쪽에도 대칭을 위해 네 칸을 비우면 72열이 남고, 줄바꿈된 텍스트는 git format-patch로 커밋을 이메일에 실을 때도 읽기 쉽게 유지됩니다. 편집기가 줄바꿈을 해 주지 않는다면 저희 N자에서 줄바꿈 도구가 한 단락을 단어 경계에서 72자로 줄바꿈해 줍니다.
본문에는 diff가 보여 주지 못하는 것을 적으세요. 문제가 무엇이었는지, 왜 이 수정인지, 시도했지만 통하지 않은 방법입니다. 명령줄에서는 -m 하나하나가 별도의 단락이 되므로 git commit -m "fix: keep cursor after paste" -m "Pasting into…"은 제목, 빈 줄, 본문을 만들어 냅니다.
검사는 도구에 맡기세요
- VS Code:
"git.inputValidation": true를 설정하세요. 그러면 커밋 입력란이 제목이 50자를 넘거나(git.inputValidationSubjectLength) 다른 줄이 72자를 넘으면(git.inputValidationLength) 경고합니다. - Vim: 기본으로 들어 있는 커밋 메시지 설정은 본문을 72자에서 줄바꿈하고(
textwidth=72) 제목의 50자 이후 부분에는 강조 표시를 하지 않습니다. - 커밋 템플릿:
git config commit.template ~/.gitmessage를 설정하면 모든 커밋이 편집기에 미리 적어 둔 알림 문구와 함께 시작됩니다. - commit-msg 훅은 의존성 없이 검사하는 방법입니다. 다음을
.git/hooks/commit-msg로 저장하고 실행 권한을 주세요.
#!/bin/sh
# Checks the subject line of every commit message.
subject=$(head -n 1 "$1")
length=${#subject} # counts bytes in some shells; fine for ASCII subjects
if [ "$length" -gt 72 ]; then
echo "commit-msg: subject is $length characters (limit 72, aim for 50)" >&2
exit 1
fi
if [ "$length" -gt 50 ]; then
echo "commit-msg: note, subject is $length characters (aim for 50)" >&2
fi
case "$subject" in
*.) echo "commit-msg: drop the full stop at the end of the subject" >&2
exit 1 ;;
esac
if [ -n "$(sed -n 2p "$1")" ]; then
echo "commit-msg: leave line 2 blank, between subject and body" >&2
exit 1
fi50자를 넘으면 경고하고, 72자를 넘는 제목, 끝에 붙은 마침표, 빠진 빈 줄은 거부합니다. 정말 필요할 때는 git commit --no-verify로 건너뛸 수 있습니다. 커밋하기 전에 빠르게 글자 수를 세려면 제목을 저희 글자 수 계산기에 붙여넣으세요.
정리하면 체크리스트는 이렇습니다. 명령형 제목, 50자 안팎, 마침표 없음, 저장소 전체에 하나의 대소문자 규칙, 빈 줄 하나, 이유를 설명하는 72자 줄바꿈 본문, 그리고 바쁜 한 주가 지나도 규칙이 살아남도록 하는 훅이나 린터입니다.


