본문 바로가기
Tools2026년 8월 14일6분 읽기

Git 훅(hooks) — 커밋·푸시 순간에 끼어드는 자동화

YS
김영삼
조회 6
Git 훅(hooks) — 커밋·푸시 순간에 끼어드는 자동화

Git 훅(hooks)은 커밋·푸시·머지 같은 특정 순간에 Git이 자동으로 실행해 주는 스크립트입니다. 저장소의 .git/hooks/ 폴더에 실행 가능한 파일로 놓아두면, 그 이름에 해당하는 이벤트가 일어날 때마다 Git이 알아서 불러줍니다. 한마디로 "이 동작 직전(혹은 직후)에 내 스크립트를 끼워 넣는" 확장 지점이죠.

나는 훅을 "팀의 규칙을 사람 대신 지켜주는 문지기"라고 부릅니다. 린트 안 돌린 코드, 콘솔 로그가 잔뜩 남은 코드, 커밋 메시지 규칙을 안 지킨 커밋 — 이런 걸 리뷰 단계에서 잡으면 이미 늦습니다. 훅은 그걸 커밋이 만들어지는 순간에 막아줍니다.

훅은 어디에 있고 언제 실행되나

모든 저장소에는 .git/hooks/ 폴더가 있고, 처음엔 .sample이 붙은 예제 파일만 들어 있습니다. 확장자를 떼고 실행 권한을 주면 활성화됩니다. 실행 파일이기만 하면 되니 셸이든 파이썬이든 상관없습니다.

가장 많이 쓰는 훅은 몇 개 안 됩니다. 실행 시점과 "실패하면 동작이 막히는가"를 기준으로 나눠 보면 이해가 빠릅니다.

훅 이름실행 시점0이 아닌 종료코드면?
pre-commit커밋 메시지 입력 전커밋 취소
commit-msg메시지 작성 직후커밋 취소
pre-pushpush 전송 직전push 취소
post-merge머지 완료 후막지 못함(정보용)

핵심은 종료 코드입니다. 훅이 0을 반환하면 통과, 0이 아니면 그 동작을 막습니다. 이 단순한 규칙이 훅의 전부라고 봐도 됩니다.

pre-commit 훅 하나 직접 만들어 보기

스테이징된 JS 파일에 console.log가 남아 있으면 커밋을 막는 훅입니다. 실무에서 가장 요긴한 종류죠.

#!/bin/sh
# .git/hooks/pre-commit
# 스테이징된 JS/TS 파일만 검사
files=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts)$')
[ -z "$files" ] && exit 0
if echo "$files" | xargs grep -nH 'console\.log' ; then
  echo "✋ console.log가 남아 있습니다. 지우고 다시 커밋하세요."
  exit 1
fi
exit 0

여기서 --diff-filter=ACM은 추가·복사·수정된 파일만 본다는 뜻입니다. 삭제된 파일까지 검사하면 없는 파일을 grep하다 엉뚱하게 실패하거든요. 이런 걸 처음엔 몰라서 삭제 커밋마다 훅이 터지는 걸 한참 헤맸습니다.

훅은 공유되지 않는다 — 이게 제일 중요한 함정

.git/ 폴더는 Git이 추적하지 않습니다. 즉 내가 만든 훅은 나만 갖고 있고 동료에게 전달되지 않습니다. "규칙을 훅으로 정했는데 왜 나만 지키지?"의 원인이 이겁니다. 해결책은 두 가지입니다.

  • core.hooksPath 지정 — 저장소에 .githooks/ 폴더를 만들어 커밋으로 공유하고, git config core.hooksPath .githooks로 훅 경로를 그쪽으로 돌립니다.
  • 훅 관리 도구 — pre-commit, husky, lefthook 같은 도구를 쓰면 설정 파일 하나로 팀 전체가 같은 훅을 install 한 번에 맞춥니다. 언어·다중 훅 관리가 편해서 나는 규모가 조금만 커지면 이쪽을 씁니다.
개인적으로 훅에 3초 넘게 걸리는 무거운 검사(전체 테스트 스위트 같은 것)를 넣는 건 말립니다. 커밋마다 몇 초씩 기다리면 사람들은 결국 --no-verify로 훅을 꺼버립니다. 무거운 건 CI로, 빠르고 국소적인 것만 훅으로 — 이 경계가 훅을 오래 살립니다.

pre-push로 마지막 방어선 치기

pre-commit이 너무 느려지는 게 싫다면, 빠른 포맷 검사만 커밋에 걸고 테스트는 push 단계로 미루는 전략도 좋습니다. pre-push는 원격에 나가기 직전, 즉 "이제 남들이 본다"는 마지막 순간이라 검문 지점으로 적절합니다.

#!/bin/sh
# .git/hooks/pre-push
if ! npm test --silent; then
  echo "테스트 실패 — push를 중단합니다."
  exit 1
fi

자주 묻는 질문

훅을 잠깐만 건너뛰고 싶으면요?

git commit --no-verify(줄여서 -n)를 쓰면 pre-commit과 commit-msg 훅을 건너뜁니다. push는 git push --no-verify입니다. 급할 때 유용하지만 남용하면 훅이 있으나 마나 해지니, 나는 "정말 예외적인 핫픽스"에만 씁니다.

core.hooksPath와 husky 중 뭘 써야 하나요?

순수 셸 훅 한두 개면 core.hooksPath + 커밋된 .githooks/로 충분합니다. 훅이 여러 개이고 언어별 도구(prettier, eslint, black 등)를 스테이징된 파일에만 돌리고 싶다면 pre-commit이나 lefthook 같은 관리 도구가 훨씬 편합니다. 판단 기준은 "훅이 몇 개고 얼마나 자주 바뀌느냐"입니다.

훅이 실행이 안 돼요.

십중팔구 실행 권한 문제입니다. chmod +x .git/hooks/pre-commit을 확인하세요. 그리고 파일 이름에 확장자가 붙어 있으면(pre-commit.sh) Git이 인식하지 못합니다. 이름은 정확히 훅 이름이어야 합니다. 셔뱅(#!/bin/sh) 첫 줄도 빠지지 않았는지 보세요.

서버 쪽에서도 훅을 걸 수 있나요?

네. 원격 저장소(베어 리포)에는 pre-receive, update, post-receive 훅이 있어 push를 받는 서버가 직접 검증·배포를 트리거할 수 있습니다. 다만 GitHub·GitLab 같은 호스팅은 이 훅을 직접 열어주지 않고, 대신 브랜치 보호 규칙이나 CI 파이프라인으로 같은 목적을 달성합니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록