남이 쓴 커밋 로그를 열었는데 수정, ㅇㅇ, 버그 고침, 또 수정이 줄줄이 있으면 진짜 막막하죠. 대체 뭘 왜 고쳤는지 하나도 모르겠고, 나중에 "이 기능 어느 커밋에서 들어왔지?" 찾을 때 지옥이 펼쳐져요. 그래서 팀들이 커밋 메시지를 쓰는 약속을 정해둬요. 이걸 커밋 컨벤션(commit convention, 커밋 메시지 작성 규칙)이라고 해요. 그중 가장 널리 쓰이는 게 컨벤셔널 커밋(Conventional Commits)이에요.
82.1 커밋 메시지, 왜 그렇게 신경 써요?
커밋 메시지는 미래의 나와 동료에게 보내는 편지거든요. 코드는 "무엇을 했는지"는 보여주지만 "왜 했는지"는 안 보여줘요. 석 달 뒤에 이 코드를 보고 "이걸 왜 이렇게 짰지?" 싶을 때, 잘 쓴 커밋 메시지 하나가 그 답을 줘요. 게다가 팀 규모가 커질수록 로그는 그 프로젝트의 역사책이 돼요. 역사책이 수정, ㅇㅇ으로 가득하면 아무도 안 읽죠. 반대로 규칙이 잡혀 있으면 로그만 쭉 훑어도 프로젝트가 어떻게 자라왔는지 한눈에 들어와요.
나쁜 예와 좋은 예를 나란히 볼게요. 수정이라고만 적힌 커밋은 무엇을 왜 고쳤는지 0의 정보를 줘요. 반면 fix: 비밀번호 재설정 메일이 안 가던 문제 수정은 열어보지 않아도 내용이 그려지죠. 두 커밋을 쓰는 데 걸리는 시간 차이는 고작 10초인데, 나중에 이걸 읽는 사람이 아끼는 시간은 몇 분에서 몇 시간이에요. 그래서 커밋 메시지는 "귀찮은 절차"가 아니라 "미래에 대한 투자"라고 생각하는 게 맞아요.
82.2 좋은 커밋 메시지는 어떤 모양이죠?
컨벤셔널 커밋의 뼈대는 아주 단순해요. 타입: 제목 형태예요.
feat: 회원가입 폼 추가
앞의 feat이 이 커밋의 성격을 나타내는 타입(type)이고, 콜론 뒤에 무엇을 했는지를 한 줄로 써요. 제목을 쓸 땐 요령이 몇 개 있어요. 첫째, 50자 안쪽으로 짧게. 둘째, "추가함", "수정했음"보다 "추가", "수정"처럼 간결하게 끊어요. 셋째, 마침표는 안 찍고요. 짧고 명령하듯 쓰는 게 관례예요. "이 커밋을 적용하면 무슨 일이 일어나는가"를 한 줄로 요약한다고 생각하면 편해요.
82.3 타입에는 어떤 것들이 있나요?
자주 쓰는 타입은 예닐곱 개예요. 이것만 익혀도 충분해요. feat은 새 기능, fix는 버그 수정이에요. 이 둘이 제일 많이 쓰여요. 그 밖에 docs는 문서 작업, refactor는 동작은 그대로 두고 코드 구조만 다듬는 것, style은 들여쓰기나 세미콜론처럼 동작과 무관한 서식 정리, test는 테스트 코드, chore는 라이브러리 버전 올리기 같은 자잘한 잡일이에요. 실제 로그로 보면 이런 느낌이에요.
$ git log --oneline
5d00ead chore: 의존성 버전 업데이트
f647d04 refactor: 결제 로직 함수 분리
bb8a16d docs: README 설치 방법 보완
e85237b fix: 로그인 버튼 클릭 안되던 문제 수정
0d89a60 feat: 회원가입 폼 추가
어때요, 타입만 봐도 각 커밋이 무슨 종류의 작업인지 딱 오죠. 어느 부분을 건드렸는지 더 알려주고 싶으면 타입 뒤 괄호에 범위(scope, 작업한 영역)를 넣기도 해요.
feat(auth): 소셜 로그인 버튼 추가
여기서 auth는 "인증 쪽을 손봤다"는 표시예요. 필수는 아니지만, 큰 프로젝트에서는 이 괄호 하나가 로그 읽는 속도를 크게 높여줘요.
여기서 자주 헷갈리는 게 fix와 refactor의 차이예요. 겉보기엔 둘 다 "코드를 고친다"지만 결이 완전히 달라요. fix는 동작이 바뀌어요. 안 되던 게 되게 만드는 거죠. 반면 refactor는 동작은 그대로 두고 코드 생김새만 깔끔하게 다듬는 거예요. 사용자 입장에선 아무 변화가 없어야 정상이고요. 이 둘을 구분해서 적어두면, 나중에 "이 커밋이 사용자에게 영향을 줬나?"를 로그만 보고 판단할 수 있어요. 마찬가지로 style은 세미콜론이나 들여쓰기처럼 눈에 보이는 서식만 만지는 거라, 코드 로직을 바꾸는 refactor와는 또 달라요.
82.4 제목만 쓰나요, 아니면 설명도 붙이나요?
대부분은 제목 한 줄이면 충분해요. 근데 "왜 이렇게 고쳤는지"를 남겨야 하는 커밋이 있죠. 그럴 땐 제목 아래 빈 줄을 하나 두고 본문(body)에 배경을 적어요.
fix: 장바구니 수량 0 허용되던 버그 수정
수량이 0 이하일 때 서버에서 400을 반환하도록 검증 추가.
제목과 본문 사이의 빈 줄이 중요해요. 이게 없으면 깃이 제목과 본문을 구분하지 못하거든요. 본문에는 "왜 이 문제가 생겼고, 어떻게 접근했는지"를 적어요. 명령창에서는 git commit -m "제목" -m "본문"처럼 -m을 두 번 주면 제목과 본문이 나뉘어 들어가요. 코드만 봐선 이해 안 될 결정을 내렸다면, 이 본문이 미래의 동료를 구해줘요. 특히 "왜 더 쉬운 방법 대신 이 방법을 골랐는지" 같은 건 코드 어디에도 안 남으니, 본문에 한 줄 적어두면 나중에 "이거 왜 이렇게 했냐"는 질문을 미리 막아줘요. 반대로 오타 하나 고친 커밋에까지 본문을 억지로 채울 필요는 없어요. 설명이 필요한 커밋에만 붙이면 돼요.
82.5 이렇게 쓰면 실제로 뭐가 좋아지죠?
가장 체감되는 건 찾기예요. 타입이 정해져 있으니 "지금까지 고친 버그만 쭉 보자" 하면 한 줄로 뽑혀요.
$ git log --oneline --grep="^fix"
48a86bb fix: 장바구니 수량 0 허용되던 버그 수정
e85237b fix: 로그인 버튼 클릭 안되던 문제 수정
--grep은 메시지에서 특정 글자를 찾는 옵션인데, 타입이 앞에 딱 붙어 있으니 fix 커밋만 골라내기가 이렇게 쉬워져요. 게다가 요즘 도구들은 이 규칙을 읽어서 변경 이력 문서(changelog)를 자동으로 만들어주기도 해요. feat은 새 기능 목록에, fix는 버그 수정 목록에 알아서 정리되는 식이죠. 사람이 손으로 정리 안 해도 되니 시간이 확 절약돼요. 릴리스 노트를 매번 손으로 쓰던 팀이 이 규칙 하나로 그 일을 통째로 없애는 걸 저도 여러 번 봤어요.
82.6 오늘 정리
정리할게요. 커밋 메시지는 타입: 제목 형태로 쓰고, 타입은 feat(기능), fix(버그), docs, refactor, style, test, chore 정도만 알면 돼요. 제목은 50자 안쪽으로 간결하게, 마침표 없이 명령하듯 짧게 끊어 써요. 배경 설명이 필요한 커밋에만 빈 줄 뒤에 본문을 붙이고요. 이렇게 해두면 git log --grep으로 원하는 커밋을 쏙 찾고, 변경 이력 문서도 저절로 만들어져요. 완벽하게 지키려 부담 갖기보다, 오늘 커밋부터 feat과 fix 두 개만 붙여봐도 로그가 확 달라지는 게 보일 거예요. 그리고 이건 혼자 지키는 것보다 팀이 같이 지킬 때 힘이 몇 배가 돼요. 나만 규칙대로 써도 남들이 수정만 남기면 로그는 결국 뒤죽박죽이 되니까요. 팀에 이런 약속이 아직 없다면, 오늘 이 글을 슬쩍 공유하면서 "우리도 이렇게 써볼까요?" 한마디 던져보는 것도 좋은 시작이에요. 규칙은 거창하게 시작할 필요 없어요. 작게 시작해서 팀에 스며들게 하는 게 오래가는 비결이에요.