-
AI가 한 일을 어떻게 믿나 - PostToolUse 훅으로 만드는 Audit 시스템IT 2026. 3. 13. 21:00
AI가 코드를 짰다. 근데 제대로 됐나?
AI 코딩 에이전트를 쓰다 보면 불안한 순간이 온다. 에이전트가 파일 10개를 수정하고 명령어 5개를 실행했다. 겉보기엔 잘 된 것 같다. 빌드도 통과했다.
그런데 이런 의문이 든다:
- 어떤 파일이 어떻게 바뀐 거지?
- 테스트 커버리지가 떨어진 건 아닌가?
- 우리 팀 컨벤션을 지켰나?
- 만약 나중에 문제가 생기면 AI가 뭘 했는지 추적할 수 있나?
이 불안을 해소하는 것이 Audit(감사) 시스템이다. 그리고 AI 코딩 에이전트에서 이것을 구현하는 핵심 도구가 PostToolUse 훅이다.
PostToolUse 훅이란
Hook은 AI 에이전트가 특정 행동을 할 때 자동으로 실행되는 스크립트다. 이전 포스팅에서 Hook의 종류를 소개했는데, 그 중 PostToolUse는 도구 실행이 성공한 직후에 발화한다.
Hook 발화 시점 주요 용도 PreToolUse 실행 전 위험 명령 차단, 사전 검증 PostToolUse 실행 후 결과 기록, 품질 검증, Audit PostToolUseFailure 실행 실패 시 오류 로깅, 알림 PostToolUse가 Audit에 특히 적합한 이유가 있다.
"실행 후 발화하기 때문에 '실제로 일어난 사실'을 기록하는 데 적합하다."
그리고 한 가지 역설적인 특성이 있다. 실행을 되돌릴 수 없다는 것. 이 제약이 오히려 신뢰성을 만든다. PostToolUse가 기록한 것은 "실제로 일어났다"는 사실이 보장된다. 나중에 누군가 "그때 AI가 뭘 했는지" 물으면, 그 기록이 반박할 수 없는 증거가 된다.
Audit의 두 가지 레이어
PostToolUse로 구현하는 Audit는 두 개의 레이어로 나뉜다. 많은 팀이 첫 번째 레이어만 하고 멈추는데, 진짜 가치는 두 번째에 있다.
레이어 1 - Observability: "무슨 일이 있었나"
에이전트가 실행한 모든 행동을 기록하는 것이다.
- 어떤 도구를 썼는가 (Write, Bash, Read...)
- 대상이 무엇이었는가 (파일 경로, 실행 명령어)
- 언제 일어났는가 (타임스탬프)
- 어느 세션에서 일어났는가 (세션 ID)
이것을 JSONL(JSON Lines) 포맷으로 쌓는다. JSONL은 한 줄이 하나의 JSON 레코드인 포맷이다. 대용량 로그에 적합하고, 스트리밍으로 읽을 수 있다.
{"ts": "2026-03-12T14:20:00Z", "session": "abc-123", "tool": "Write", "target": "src/auth.py", "lines_changed": 42} {"ts": "2026-03-12T14:20:05Z", "session": "abc-123", "tool": "Bash", "command": "pytest tests/", "exit_code": 0} {"ts": "2026-03-12T14:20:08Z", "session": "abc-123", "tool": "Write", "target": "src/utils.py", "lines_changed": 7}이 로그를 HTTP 웹훅으로 팀 단위 중앙 서버에 보낼 수도 있다. Claude Code는 PostToolUse에서 HTTP 훅 타입을 지원하기 때문에, 각자의 로컬 에이전트가 하는 일을 팀 전체가 실시간으로 모니터링하는 대시보드를 만들 수 있다.
{ "hooks": { "PostToolUse": [ { "type": "http", "url": "https://our-team-dashboard.internal/agent-audit", "matcher": "Write|Bash" } ] } }레이어 2 - Compliance Check: "의도한 대로 됐나"
Observability가 "사실 기록"이라면, Compliance Check는 "기준 대비 판정"이다.
AI가 파일을 수정한 직후, 자동으로 이런 것들을 검사한다:
- 테스트 커버리지: 커버리지가 기준(예: 80%) 아래로 떨어졌는가
- 코드 복잡도: 함수 복잡도가 임계값을 넘었는가 (Cyclomatic Complexity)
- 컨벤션 준수: 린터 룰을 위반했는가, 파일명 규칙을 지켰는가
- SLA: 응답 시간, 빌드 시간 등 성능 지표가 기준 이내인가
중요한 것이 있다. 판정 결과 자체도 로그로 남겨야 한다.
{"ts": "2026-03-12T14:20:09Z", "session": "abc-123", "tool": "Write", "target": "src/auth.py", "compliance": { "coverage": {"value": 76.3, "threshold": 80, "pass": false}, "complexity": {"value": 8, "threshold": 10, "pass": true}, "lint": {"violations": 0, "pass": true} }, "verdict": "WARN" }이 레코드가 있어야 나중에 "그때 커버리지가 떨어졌는데 왜 아무도 몰랐지?"라는 질문에 답할 수 있다. 로그 + 판정 결과 = 완전한 audit trail.
실제 구현 예시
Claude Code 기준으로 PostToolUse 훅에 audit 스크립트를 연결하는 방법이다.
.claude/hooks.json 설정
{ "hooks": { "PostToolUse": [ { "matcher": "Write", "command": "bash .claude/hooks/post-write-audit.sh" }, { "matcher": "Bash", "command": "bash .claude/hooks/post-bash-audit.sh" } ] } }.claude/hooks/post-write-audit.sh
#!/bin/bash # PostToolUse: Write 이후 실행 # 환경변수로 Claude Code가 컨텍스트를 전달함 FILE="$CLAUDE_TOOL_INPUT_PATH" SESSION="$CLAUDE_SESSION_ID" TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ") # 1. Observability 로그 echo "{\"ts\":\"$TS\",\"session\":\"$SESSION\",\"tool\":\"Write\",\"target\":\"$FILE\"}" \ >> ~/.claude/audit.jsonl # 2. Compliance Check - 린터 LINT_RESULT=$(eslint "$FILE" --format json 2>/dev/null | jq '.[0].errorCount // 0') LINT_PASS=$([ "$LINT_RESULT" -eq 0 ] && echo true || echo false) # 3. 판정 결과도 로그에 기록 echo "{\"ts\":\"$TS\",\"session\":\"$SESSION\",\"compliance\":{\"lint\":{\"violations\":$LINT_RESULT,\"pass\":$LINT_PASS}}}" \ >> ~/.claude/audit.jsonl # 4. 위반 시 LLM 컨텍스트에 주입 (AI에게 알림) if [ "$LINT_PASS" = "false" ]; then echo "{\"contextModification\": \"WARNING: $FILE has $LINT_RESULT lint violations. Please fix before proceeding.\"}" fi마지막 줄이 중요하다. 린터 위반이 있으면 단순히 로그만 쌓는 게 아니라 AI에게도 알린다.
contextModification을 반환하면 AI의 다음 응답 컨텍스트에 이 메시지가 주입되어, AI가 스스로 수정하도록 유도한다.
Audit이 SUPERVISE 루프와 연결되는 방식
이 구조가 Harness Engineering의 어디에 위치하는지 짚고 가자.
Harness Engineering에서 개발자가 하는 일은 세 단계다: PREPARE(환경 설계) → SUPERVISE(루프 감시) → IMPROVE(개선).
PostToolUse Audit은 SUPERVISE 단계의 자동화다.
전통적 SUPERVISE PostToolUse Audit 개발자가 AI의 변경을 직접 보고 판단 훅이 자동으로 기준 대비 판정 매번 수동 검토 필요 기준 위반 시에만 알림 기록이 남지 않음 JSONL로 모든 판정이 기록됨 팀 규모에 제한됨 HTTP 훅으로 팀 전체 중앙 수집 여기서 핵심 관점 하나를 다시 강조한다:
"진짜 audit의 본질은 '기준 대비 현실의 비교'다."
로그는 수단이고, 메트릭 충족 여부 판정이 목적에 더 가깝다.단순히 "AI가 이 파일을 수정했다"는 것을 아는 것(Observability)으로는 부족하다. "AI가 이 파일을 수정했고, 그 결과가 우리 기준을 충족했는가"(Compliance)까지 자동으로 판정해야 진짜 Audit이다.
어디서부터 시작할까
Audit 시스템을 한 번에 완성하려 하면 복잡해진다. 단계별로 쌓는 것을 추천한다.
- 로깅부터 (Observability)
PostToolUse에 JSONL 로그만 쌓는 스크립트를 붙인다. 내용을 보지 않아도 된다. 일단 쌓기 시작하는 것이 중요하다. - 가장 중요한 기준 하나 (Compliance)
팀에서 가장 자주 위반되는 규칙 하나만 골라서 자동 검사를 붙인다. 린터, 파일명 규칙, 커버리지 중 하나. - 컨텍스트 주입으로 AI 셀프 수정 유도
위반이 감지되면contextModification으로 AI에게 알린다. AI가 스스로 고치는 루프가 만들어진다. - 팀 대시보드 (HTTP 훅)
팀이 충분히 커지면 HTTP 훅으로 중앙 수집 시스템을 붙인다.
마치며
AI 에이전트를 믿는 방법은 두 가지다. 첫 번째는 AI를 무조건 믿는 것. 두 번째는 AI가 한 일을 자동으로 검증하는 시스템을 만드는 것.
PostToolUse 훅 + JSONL 로그 + Compliance Check의 조합이 바로 그 두 번째 방법이다. AI를 믿는 것이 아니라, 기준이 통과됐다는 사실을 믿는 것. Proof of Work와 같은 철학이다.
이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.
'IT' 카테고리의 다른 글
AI 에이전트에게 자율권을 얼마나 줄 것인가 - HITL Policy 설계 (0) 2026.03.14 Claude Code가 PR을 만들 수 있는 이유 - GitHub CLI와 API의 구조부터 이해하기 (0) 2026.03.14 LLM tool calling, '지원'한다면서요? — 스펙과 현실 사이의 간극 (1) 2026.03.14 NVIDIA DGX부터 ASUS Ascent GX10, MSI EdgeXpert까지 - AI 서버 시장이 바뀌고 있다 (0) 2026.03.13 텔레그램 봇을 열어두면 생기는 일 - AI 봇 보안 위협과 OpenClaw 차단 설정 (0) 2026.03.13 AI 코딩 에이전트의 Hook - 에이전트를 길들이는 가드레일 (2026년 3월 초 조사) (1) 2026.03.12 Tailscale로 모바일에서 데스크탑 개발 환경 원격 접속하기 (0) 2026.03.12 Acer 노트북 + 우분투 16.04 이슈해결기 (2) 2018.03.07 [人터뷰] 개발자의 길에서 벗어나 영업으로 떠난 С군 (0) 2017.02.28 [알고리즘] 유클리드 호제법, 간단하게 증명하기 (0) 2017.02.22