Claude Code Hooks로 Git Commit 자동화하기 — AI가 코딩 끝내면 알아서 저장
AI 도구

Claude Code Hooks로 Git Commit 자동화하기 — AI가 코딩 끝내면 알아서 저장


⚡ 한눈에 보는 핵심 요약 (TL;DR)

  • Stop Hook이 정답에 가장 가깝습니다. 파일 하나 고칠 때마다 커밋하면 히스토리가 난잡해지고, AI 턴이 끝난 뒤 한 번만 하면 깔끔하게 유지됩니다.
  • 변경 사항 없으면 그냥 넘기세요. 스크립트 앞에 한 줄만 넣으면 빈 커밋이 반복되는 무한 루프를 막을 수 있습니다.
  • main·develop 브랜치는 반드시 차단하세요. 자동화는 실수가 원격 서버까지 곧장 올라가는 구조라, 안전 브랜치는 명시적으로 막아두는 게 낫습니다.
  • ⚠️흔한 실수 3가지: 빈 커밋 루프, .env 자동 업로드, .git/index.lock 오류 — 이 세 가지는 Hook 도입 첫 주에 거의 누구나 겪습니다.
  • post-task·AfterAgent는 없는 이벤트입니다. AI가 블로그 글에서 지어낸 이름이 많습니다. 공식 이벤트는 9가지뿐입니다.

Claude Code로 하루에 20~30번씩 코드를 수정하다 보면, 커밋을 빼먹고 그냥 다음 작업으로 넘어가는 경우가 많습니다. 나중에 git status를 열어보면 빨간 파일이 40개씩 쌓여 있고, “이걸 어떻게 나눠서 커밋하지?” 고민하느라 시간을 쓰게 됩니다.

이 글은 Claude Code의 Hook 시스템으로 그 빈틈을 메우는 방법을 정리합니다. AI의 작업이 끝나면 자동으로 커밋이 쌓이되, 실수는 막는 구조를 만드는 것이 목표입니다. 공식 문서에 없는 실패 사례와 안전장치 위주로 썼습니다.

Claude Code Hook이 뭔가요? — 개념부터 쉽게

Claude Code에는 특정 순간에 내가 만든 스크립트를 자동으로 실행해주는 기능이 있는데, 이것을 Hook 이라고 부릅니다. 쉽게 말하면 “AI가 이 행동을 하면, 내 스크립트도 같이 실행해줘”라고 설정하는 것입니다.

Hook은 총 9가지 이벤트를 지원합니다. 각 이벤트는 다른 시점에 발동합니다.

  • PreToolUse: 도구를 실행하기 직전. 허용할지 말지 판단할 때 씁니다.
  • PostToolUse: 도구 실행이 끝난 직후. 파일 편집 후 바로 처리할 때 씁니다.
  • Stop: AI가 한 번의 응답을 마무리할 때. git commit 자동화에 가장 적합합니다.
  • SubagentStop: 내부 서브에이전트가 끝날 때.
  • UserPromptSubmit: 사용자가 프롬프트를 제출할 때. 컨텍스트를 추가할 때 씁니다.
  • Notification · PreCompact · SessionStart · SessionEnd: 세션 생명주기 관련.
Claude Code Hook 이벤트 라이프사이클 - 세션 시작부터 종료까지 9가지 이벤트 흐름 인포그래픽

git commit 자동화를 위해 실질적으로 고민해야 할 이벤트는 두 개입니다. PostToolUse는 파일 하나를 고칠 때마다 커밋을 생성하고, Stop은 AI의 한 턴이 끝날 때 한 번만 실행됩니다. 대부분의 경우 Stop hook이 git log를 사람이 읽을 수 있는 상태로 유지하는 더 나은 선택입니다.

Stop Hook으로 Git Commit 자동화 구현하기

설정 파일 구조

Hook 설정은 settings.json에 넣습니다. 파일 위치는 두 가지입니다.

  • 전체 적용: ~/.claude/settings.json (내 컴퓨터의 모든 프로젝트에 적용)
  • 프로젝트별 적용: .claude/settings.json (해당 프로젝트에만 적용)
  • 로컬 전용: .claude/settings.local.json (팀원과 공유하지 않을 때 권장)
{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/auto-commit.sh",
            "timeout": 30000
          }
        ]
      }
    ]
  }
}

Stop 이벤트에서는 matcher가 의미가 없으므로 빈 문자열로 두면 됩니다. timeout은 밀리초 단위이고 30초(30000ms)면 대부분의 저장소에서 충분합니다.

안전한 자동 커밋 스크립트

아래 스크립트는 첫 주에 만나는 3대 실수를 예방하기 위한 안전장치가 모두 들어간 버전입니다.

안전한 Hook 스크립트 7단계 체크리스트 - stdin 읽기부터 커밋 저장까지
#!/usr/bin/env bash
# .claude/hooks/auto-commit.sh
set -euo pipefail

# 1. Claude가 전달하는 정보 읽기
payload=$(cat)

# 2. 브랜치 가드 — main·develop에서는 자동 커밋 금지
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
case "$branch" in
  main|master|develop|release/*) exit 0 ;;
esac

# 3. 변경 사항 체크 — 없으면 조용히 종료 (무한 루프 방지)
if git diff --quiet && git diff --cached --quiet; then
  exit 0
fi

# 4. 민감 파일 안전장치 — .env, *.key, credentials 차단
staged=$(git diff --name-only --cached)
if echo "$staged" | grep -E '(^|/)\.env($|\.)|\.(key|pem)$|credentials' >/dev/null; then
  echo "민감 파일이 스테이징됨. 수동 확인 필요." >&2
  exit 2
fi

# 5. 추적 중인 파일만 스테이징 (git add . 대신 git add -u 사용)
for i in 1 2 3; do
  if git add -u 2>/dev/null; then break; fi
  sleep 0.5
done

# 6. 커밋 유형 자동 분류
types=""
echo "$staged" | grep -qE '\.md$|^docs/' && types+="docs,"
echo "$staged" | grep -qE 'test|spec' && types+="test,"
echo "$staged" | grep -qE '^src/' && types+="feat,"
prefix=$(echo "${types:-chore}" | cut -d, -f1)

# 7. 커밋 — Conventional Commits 형식으로 저장
summary=$(git diff --cached --stat | tail -1)
git commit -m "$prefix: Claude Code 자동 커밋

$summary

🤖 Stop hook 자동 생성" --quiet

핵심은 3번(변경 사항 없으면 바로 종료)과 4번(민감 파일 차단)입니다. 이 두 줄만 제대로 있으면 흔한 실수 대부분이 예방됩니다.

[실패 사례] Hook 도입 첫 주에 만나는 3가지 문제

Hook 도입 첫 주에 흔히 겪는 3가지 실수 - 빈 커밋 루프, .env 업로드, index.lock 충돌

자동화를 처음 도입하면 아래 세 가지 중 하나를 거의 반드시 겪게 됩니다. 실제로 저도 다 겪었고, 각 케이스마다 스크립트에 붙은 안전장치가 위의 7단계 안에 그대로 반영돼 있습니다.

1. 빈 커밋 무한 루프

Stop hook 안에서 git commit만 실행하면, 변경 사항이 없어도 커밋을 하거나 에러를 냅니다. 더 나쁜 경우는 Claude가 에러 메시지를 보고 “커밋이 안 됐으니 다시 해야겠다”고 판단해서 턴이 끝나지 않는 상황입니다.

[!WARNING] Pro·Max 한도가 순식간에 소진될 수 있습니다. Claude Code는 Stop hook이 에이전트를 다시 호출하게 만들 수 있도록 설계되어 있어, 스크립트가 무한 루프를 만들면 요금이 순식간에 쌓입니다. 스크립트의 3번 단계(git diff --quiet 체크)가 이 루프를 막는 역할입니다.

2. .env 파일이 원격에 자동 업로드

git add . 는 Hook 초창기에 자주 나오는 실수입니다. .gitignore.env가 있어도 이미 한 번이라도 추적된 파일이라면 그대로 커밋됩니다. 특히 AI가 .env.example을 만들다가 실수로 .env를 생성한 경우, Hook이 비밀번호를 원격까지 밀어 보낼 수 있습니다.

해결책은 두 층입니다. 첫째, git add -u(추적된 파일 중 수정된 것만)로 스테이징 범위를 좁힙니다. 둘째, 민감 파일 패턴을 grep으로 탐지해 exit 2로 차단합니다. exit 2는 Claude에게 “이 커밋은 차단됐다”고 알리므로, AI가 왜 안 됐는지 파악할 수 있습니다.

3. .git/index.lock 오류로 커밋 실패

Stop hook이 실행되는 동안 다른 터미널에서 git add를 돌리거나, VS Code GitLens 같은 에디터 플러그인이 백그라운드에서 git을 돌리면 .git/index.lock 파일이 남아서 충돌이 납니다.

# .git/index.lock 충돌 대비 재시도 루프
for i in 1 2 3; do
  if git add -u 2>/dev/null; then break; fi
  sleep 0.5
done

3회·0.5초 간격이면 대부분의 에디터 충돌은 넘어갑니다. 재시도를 너무 많이 넣으면 오히려 병목이 생기니 이 정도로 충분합니다.

PostToolUse vs Stop — 어떤 방식이 더 나을까요?

PostToolUse 방식과 Stop Hook 방식 비교 인포그래픽 - 한 작업에 10개 커밋 vs AI 턴당 1개 커밋

두 방식의 차이를 명확히 정리하면 다음과 같습니다.

항목PostToolUseStop Hook
발동 시점파일을 고칠 때마다AI 한 턴이 끝날 때 한 번
커밋 수파일 10개 수정 = 커밋 10개파일 10개 수정 = 커밋 1개
git log 가독성지저분해짐깔끔하게 유지됨
복잡도비교적 간단안전장치 설계 필요
추천 대상워크인프로그레스(WIP) 전용일반 상황 모두

처음 도입한다면 Stop Hook 단독 사용을 권합니다. PostToolUse로 WIP 커밋을 만들고 Stop에서 squash하는 2단 구조도 가능하지만, 실패 빈도가 올라가므로 익숙해진 뒤에 시도하는 편이 낫습니다.

커밋 메시지 품질을 높이는 팁

단순히 “chore: auto commit”만 찍히면 나중에 git log가 아무 의미가 없어집니다. 품질을 올리는 세 가지 방법입니다.

(1) 변경 경로로 타입 자동 분류 — 스크립트의 6번 단계가 이 역할을 합니다. docs/ 밑이면 docs:, 테스트 파일이 섞이면 test:, src/ 안이면 feat:로 자동으로 분류됩니다.

(2) stdin 페이로드 활용 — Stop hook은 JSON 데이터를 stdin으로 받으며, 안에 transcript_path(AI 대화 내용 파일 경로)가 들어 있습니다. 이걸 읽어서 커밋 메시지에 AI의 마지막 응답 요약을 넣을 수도 있습니다.

transcript=$(echo "$payload" | jq -r '.transcript_path')
if [ -f "$transcript" ]; then
  last_summary=$(tail -200 "$transcript" | jq -rs 'map(select(.role=="assistant")) | last | .content' 2>/dev/null | head -c 100)
fi

(3) Jira 티켓 번호 자동 삽입 — 브랜치명이 feature/ABC-123-xxx 형식이라면 티켓 번호를 파싱해서 커밋 메시지에 넣을 수 있습니다.

ticket=$(echo "$branch" | grep -oE '[A-Z]+-[0-9]+' | head -1)
[ -n "$ticket" ] && prefix="$prefix($ticket)"

로컬 Hook과 CI/CD의 역할 구분

Claude Code Hook은 편집 직후 즉시 피드백에 강하고, GitHub Actions 같은 CI는 원격 푸시 후 느린 검증에 강합니다. 둘을 함께 쓰는 것이 현실적인 구성입니다.

  • 로컬 (Claude Hook): git commit, 가벼운 린트, 타입 체크 경고
  • pre-commit 훅 (Husky 등): 커밋 시점 린트·포맷. Claude Hook이 git commit을 호출할 때 자동으로 실행됩니다.
  • CI (GitHub Actions): 전체 테스트, 빌드, 보안 스캔, 배포

주의할 점은 Husky와 Claude Hook이 모두 git commit 경로를 건드린다는 것입니다. Claude Hook에서 --no-verify를 쓰면 pre-commit이 건너뛰어지고 CI가 검증 부담을 다 떠안게 됩니다. 가능하면 --no-verify는 쓰지 말고, Husky 자체를 가볍게 유지하는 쪽이 낫습니다.

자주 묻는 질문

Q1. post-taskAfterAgent 같은 hook을 설명하는 글이 있던데, 왜 이 글은 Stop을 쓰나요?

Claude Code 공식 Hook 이벤트에는 post-task·AfterAgent가 없습니다. 이런 이름은 다른 도구(Aider 등)의 용어이거나, LLM이 직접 만들어낸 가상 이벤트명인 경우가 대부분입니다. 공식 문서(docs.claude.com/en/docs/claude-code/hooks) 기준 9종 이벤트만 실제로 트리거됩니다.

Q2. PostToolUse에 Edit·Write matcher를 걸면 왜 안 되나요?

기술적으로는 됩니다. 다만 연속 편집이 10번 있으면 커밋이 10개 생깁니다. git log가 사람이 읽기 어려워지므로, Stop 단독 또는 PostToolUse(WIP)+Stop(squash) 2단 구조를 권합니다.

Q3. Hook 스크립트가 실행이 안 됩니다. 어디를 확인해야 하나요?

세 가지 순서로 점검합니다. (1) chmod +x .claude/hooks/auto-commit.sh로 실행 권한이 있는지, (2) settings.jsoncommand 경로가 올바른지, (3) Claude Code CLI의 --debug 모드로 실행했을 때 Hook 관련 에러 메시지가 나오는지. 세 번째가 가장 빠른 방법입니다.

Q4. Jira 티켓 번호를 자동으로 커밋 메시지에 넣고 싶습니다.

브랜치명에서 파싱하는 방식이 가장 안정적입니다. 브랜치명이 feature/ABC-123-xxx 형식이면:

ticket=$(echo "$branch" | grep -oE '[A-Z]+-[0-9]+' | head -1)
[ -n "$ticket" ] && prefix="$prefix($ticket)"

Q5. 대규모 리팩토링 중 Hook이 30초를 초과합니다.

timeout을 올리기 전에 병목부터 확인하는 편이 낫습니다. 대부분의 경우 git add . 같은 광범위한 스테이징이나 git status가 수천 파일을 처리하는 케이스입니다. .claudignore.gitignore로 빌드 결과물을 제외하면 대부분 10초 이내로 내려옵니다. 토큰 절감 관련해서는 Claude Code .claudignore 설정 가이드에 자세히 정리했습니다.

Q6. main 브랜치에서도 자동 커밋을 허용하면 안 될까요?

권하지 않습니다. 자동 커밋은 잘못된 Hook 동작(예: .env 스테이징)이 바로 원격으로 밀려 나갈 수 있습니다. main은 수동 커밋 + Pull Request 플로우를 유지하고, 자동 커밋은 feature 브랜치에만 허용하는 편이 복구 비용이 훨씬 낮습니다.

마무리 한 줄: Hook 자동 커밋의 진짜 가치는 “커밋 횟수 증가”가 아니라 “생각의 흐름을 끊지 않는 것”입니다. “이거 지금 커밋해야 하나”라는 판단 자체를 스크립트에 위임하면, 그 몇 초가 쌓여 하루에 10~20번의 컨텍스트 스위칭이 사라집니다. 어떤 이벤트를 쓰든 브랜치 가드 + diff 체크 + 민감 파일 차단 세 가지는 반드시 넣어두세요.