← 글

그날 AI가 “정리”를 결정했고, 프로젝트는 사라졌습니다

· 18 분 읽기
AI 보조 개발 Hooks 안전장치 Context 관리 관측 가능성 오픈소스
AI 엔지니어링 실전

그날 AI는 “정리”하기로 했습니다

프로젝트가 사라졌습니다

동료의 AI CLI 도구가 프로젝트 폴더 전체를 삭제해 복구하지 못할 뻔했습니다. 이제 모두가 AI에 모든 권한을 주고 사용합니다. 안전장치 없는 완전 자동화는 자동화된 재앙입니다.

사건

그날 AI가 폴더를 삭제했습니다

실제 사건, 두 동료, 서로 다른 두 CLI 도구

두 동료 모두 AI 때문에 파일이 삭제되는 일을 겪었습니다. 한 명은 OpenAI Codex CLI, 다른 한 명은 Google Gemini CLI를 사용했습니다. 두 도구 모두 강력하지만, 일이 벌어진 뒤에야 어느 쪽에도 hook을 설정하지 않았다는 사실을 알았습니다.

첫 번째 동료는 AI에게 임시 파일을 정리해 달라고 했는데, AI가 잘못 판단해 프로젝트 폴더 전체를 삭제했습니다.

$ rm -rf ./project-name
# AI는 이것을 “정리”의 일부로 판단했습니다
# 이 명령을 막는 장치는 아무것도 없었습니다

git history와 Time Machine 백업을 이용해 복구하는 데 상당한 시간이 걸렸습니다. 두 번째 동료에게도 사고가 났습니다. AI가 파일을 정리하다 경로를 잘못 판단해 삭제해서는 안 될 디렉터리를 지웠습니다.

두 사건의 공통점은 CLI 도구 자체에 문제가 있었던 게 아니라 안전장치가 없었다는 것입니다.

AI는 확률적 시스템입니다.prompt에 “중요한 파일을 삭제하지 마”라고 적어도 모델은 어느 순간 다른 판단을 내릴 수 있습니다. Hooks는 결정론적입니다.rm -rf / AI가 무슨 생각을 하든 항상 차단됩니다.

현실

아무도 하나씩 Allow를 누르지 않습니다

실제로는 아무도 매번 allow를 누르지 않습니다. 이론상 AI CLI 도구에는 명령 실행 전에 allow 또는 deny를 묻는 권한 확인 장치가 있지만, 하루에 수백 번 누르면 개발 효율이 바닥납니다. 그래서 모두가 최고 권한을 켭니다.

# Claude Code — 모든 권한 확인 건너뛰기
claude --dangerously-skip-permissions

# OpenAI Codex CLI — YOLO 모드, 모든 작업 자동 승인
codex --approval-mode full-auto

# Google Gemini CLI — 동일한 YOLO 모드
gemini --sandbox=false

이렇게 하는 사람은 소수가 아닙니다.이미 일반적입니다. 모든 권한을 주는 것이 AI agent의 효율을 제대로 끌어내는 유일한 방법이지만, 그 대가로 AI는 파일 삭제, force push, 시스템 디렉터리 쓰기, 비밀 키가 포함된 commit 푸시까지 어떤 명령이든 실행할 수 있습니다. 안전장치 없는 완전 자동화는 “대부분 똑똑하지만 가끔 실수하는” 시스템에 개발 환경을 맡기는 것과 같습니다.

문제는 “모든 권한을 줄 것인가”가 아니라 “권한을 준 뒤, 그 가끔의 실수를 누가 막을 것인가”입니다.

문제

흩어진 Shell Scripts는 통제하기 어려워집니다

그다음 Claude Code에 hooks를 추가하기 시작했습니다. 처음에는 위험한 명령을 막는 shell script 몇 개뿐이었지만, 세 달 뒤 우리 쪽에서 먼저 문제가 터졌습니다. 스크립트는 여기저기 흩어졌고, 한 번의 crash로 모두 멈췄으며, 끌 수 있는 스위치도 없었습니다.

1

스크립트가 곳곳에 흩어져 있습니다

열몇 개의 .sh 파일이 hooks 디렉터리, scripts 디렉터리, home 디렉터리에 나뉘어 있었습니다. 각 파일은 JSON 파싱과 오류 처리를 중복 구현하고 있었습니다.

2

한 번의 crash로 모두 멈춥니다

어떤 hook이 잘못된 JSON 형식 때문에 crash했습니다. Claude Code의 hooks는 동기식입니다.hook 하나가 무너지면 pipeline 전체가 중단됩니다.

3

on/off 스위치가 없습니다

특정 hook을 잠시 끄고 싶다면 settings.json 에서 수동으로 수정해야 합니다. 테스트를 마친 뒤 원래대로 돌리는 것을 자주 잊었습니다.

해법

하나의 Dispatcher로 모든 Hooks 관리

v0.1.0에서 모든 hooks를 하나의 모듈형 프레임워크로 통합했습니다. 하나의 dispatcher가 모든 이벤트를 받아 설정 파일에 따라 각 handler로 보냅니다.

34
Handler 모듈
10
모든 이벤트 유형 지원
1
YAML 설정 파일
0
외부 의존성
settings.json
Claude Code의 기본 설정입니다. 10개 이벤트 유형이 모두 같은 dispatcher를 가리킵니다
Claude Code
dispatcher.py
이벤트 유형과 JSON 입력을 읽고 handler registry를 호출하는 얇은 shim입니다
진입점
Two-Phase
Critical handlers를 먼저 실행하고(시간 예산 제한 없음), Deferrable handlers는 5초 예산 안에서 순서대로 실행합니다
디스패치 전략
config.yaml
각 handler를 독립적으로 켜고 끕니다. YAML 한 줄만 바꾸면 되고 settings.json은 건드릴 필요가 없습니다
설정 계층

Two-Phase Dispatch가 핵심 설계입니다.안전 관련 handler(예를 들어 rm -rf차단)는 critical로 표시해 항상 먼저 실행하며 시간 예산의 제한을 받지 않습니다. 다른 handler들은 5초 예산을 공유합니다. 시간이 초과되면 건너뛰지만 AI의 작업 흐름을 막지는 않습니다.

안전장치

AI의 위험한 작업 차단

첫 번째 동료처럼 프로젝트 폴더 전체를 잘못 삭제하는 사고는 이 세 handler로 막을 수 있습니다. 이들은 critical로 표시되어 조건 없이 우선 실행되며 가장 중요한 안전 문제를 처리합니다.

bash_safety
PreToolUse · Bash

차단 대상은 rm -rf /,sudo,git push --force,chmod 777 같은 위험한 명령입니다. quote-aware shell parser로 명령을 분석하므로 따옴표나 sub shell로 우회할 수 없습니다.

secret_scan
PreToolUse · Bash

작동 시점은 git push 전입니다. diff를 검사해 AWS Key, GitHub Token, PEM 개인 키 등을 차단합니다. 푸시할 때만 작동해 로컬 개발을 방해하지 않습니다. # nosec 을 이용한 줄별 예외를 지원합니다.

skill_security
PreToolUse · Write/Edit

prompt injection 공격을 차단합니다. “ignore previous instructions” 같은 주입 문구와dangerouslyDisableSandbox 같은 권한 상승 작업,curl 을 통한 데이터 유출 같은 탈취 행위를 감지합니다. 세 종류의 위협을 각각 검사합니다.

“그때 bash_safety가 있었다면 첫 번째 동료의 폴더는 삭제되지 않았을 겁니다. 우리가 처음 만든 handler였고, 이 프로젝트의 출발점이기도 합니다.”

품질 게이트

AI가 자기 작업을 직접 검증하게 합니다

commit 전에 반드시 검증을 끝내도록 하는 것이 품질 게이트의 네 handler가 세운 규칙입니다. 그전에는 검증 여부가 온전히 AI에게 달려 있었습니다. 코드를 작성하자마자 검증을 건너뛰고 commit하는 일이 잦았습니다.

verify_commit
PreToolUse · Bash

가로채는 대상은 git commit 와 gh pr create입니다. AI가 먼저 검증(lint, typecheck)을 마치고, 통과 후 marker 파일을 작성해야 commit을 허용합니다.일회용 토큰이며 사용하면 소멸합니다.

verify_completion
SubagentStop

Sub-agent가 작업을 마치면 lint와 typecheck를 자동 실행합니다. 통과하지 못하면 agent를 돌려보내 수정하게 합니다. 최대 5회 반복, 30분 제한이며, 이를 넘으면 막히지 않도록 통과시킵니다.

auto_format
PostToolUse · Edit/Write

AI가 파일을 편집한 뒤 ruff(Python) 또는 biome(JS/TS)으로 자동 포맷합니다. formatter가 crash해도 조용히 통과시키며 절대 차단하지 않습니다. 형식을 통일하는 것은 좋지만 작업 흐름을 끊을 만큼 중요한 일은 아닙니다.

review_gate
Stop

Claude Code session이 끝날 때 commit되지 않은 코드 변경 사항이 있는지 확인합니다. 기본값은 알림만 보내고 차단하지 않으며, 실제로 block하려면 수동으로 opt-in해야 합니다. AI가 일을 끝낸 뒤 변경 사항을 working tree에 남겨 놓고 떠나는 일을 막기 위해서입니다.

Context 관리

AI가 기억을 잃지 않도록

sub-agent는 생성될 때 아무런 맥락도 없습니다. 메인 대화의 맥락이 자동으로 전달되지 않습니다. Context 관리의 세 handler가 바로 이 빈틈을 메웁니다. AI Agent의 context window도 유한하므로, 제대로 기억하지 못하면 매번 처음부터 다시 시작하는 셈입니다.

context_inject
SubagentStart

Sub-agent가 시작할 때 프로젝트 문서를 자동으로 주입합니다. .context/ 디렉터리의 JSONL 설정 파일을 읽으며, agent type마다 서로 다른 내용을 주입할 수 있습니다. context를 가득 채우지 않도록 8000자까지만 넣습니다.

context_relay
SessionStart · PreCompact

Session 사이의 기억을 이어 줍니다. 이전 session이 끝나기 전에 작성한 인계 문서를 새 session 시작 시 자동으로 주입합니다. TTL은 5분으로, 오래된 데이터가 방해하지 않도록 합니다.

plan_impl_gate
PostToolUse · ExitPlanMode

AI가 Plan Mode에서 나갈 때 구현을 시작하기 전에 계획을 기억에 저장하라고 알려 줍니다. Plan Mode 자체가 context를 많이 쓰므로 저장하지 않으면 낭비입니다.한 번만 알리고 반복하지 않습니다.

관리와 관측

AI가 무엇을 하는지 확인합니다

sub-agent가 많아지면 뒤에서 무엇을 하는지 보이지 않습니다. 여기서는 네 handler로 가시성을 되찾습니다.

agent_naming
PreToolUse · Agent

모든 sub-agent에 설명적인 이름을 강제합니다. 이름이 없으면 차단합니다. 또한 prompt 내용을 바탕으로 적절한 agent type을 제안합니다. 코드 작성은 worker, 자료 조사는 explorer, 문서 작성은 writer입니다.

observability
모든 이벤트

각 hook 이벤트를 JSONL 한 줄로 spool 파일에 기록합니다. 지연 시간은 <1ms로 작업에 영향을 주지 않습니다. 백그라운드 프로그램이 2초마다 묶어서 데이터베이스에 저장하며, dashboard에서 조회할 수 있습니다.전체 경로에서 네트워크 지연은 0입니다.

sentinel_notify
PreToolUse · PostToolUse · Bash

AI가 service restart 또는 docker 작업을 실행할 때 모니터링 시스템에 유지보수 시간임을 알립니다. 작업이 끝나면 자동으로 해제합니다. 모니터링 시스템이 “이 중단은 예상된 것”이라고 알 수 있어 잘못된 경보를 내지 않습니다.

voice_notify
Stop · AskUserQuestion

AI가 작업을 마치면 TTS 음성으로 알려 줍니다. 화면을 보며 기다릴 필요 없이 커피를 마시러 가도 됩니다. 끝나면 AI가 불러 줍니다. 답변이 필요한 질문이 있을 때도 음성으로 알려 줍니다. 일곱 단계의 guard가 sub-agent의 중간 상태를 걸러, 정말 끝났을 때만 음성을 냅니다.

설계 철학

모든 Handler에 적용되는 세 가지 원칙

Block-at-Submit, Block-at-Write가 아닙니다

AI가 파일을 쓸 때는 막지 않습니다. commit 또는 push하려 할 때만 막습니다.

이유:AI의 작업 흐름은 이어져 있습니다. Edit 단계에서 block하면 AI는 맥락을 잃고 어디까지 작성했는지 알지 못합니다. 결과물을 제출하려 할 때 검토하는 편이 더 합리적입니다.

언제나 Fail-Open

모든 handler는 try/except 로 감쌉니다. Handler가 crash했나요? 통과시킵니다. Config를 읽지 못했나요? 통과시킵니다.

이유:안전장치가 보호 대상보다 더 위험해서는 안 됩니다. handler 하나가 crash했다고 AI의 작업 흐름 전체가 멈춰서는 안 됩니다.

모든 규칙에는 탈출구가 있습니다

secret_scan에는 # nosec이 있고, verify_commit에는 marker 장치가 있으며, bash_safety에는 환경 변수 스위치가 있습니다.

이유:탈출구가 없는 규칙은 결국 통째로 꺼집니다. 사용자가 우회할 수 있는 방법을 제공해야 오히려 규칙을 더 오래 유지할 수 있습니다.

의도적으로 하지 않는 것

한 일보다 더 중요한 것은 하지 않은 일입니다

  • Edit / Write를 가로채지 않습니다 AI가 파일을 쓸 때 개입하지 않습니다. 작성 도중 흐름을 끊으면 파일이 불완전한 상태로 남을 수 있습니다. 모든 검토는 commit 또는 push 시점까지 미룹니다.
  • 게이트에서 테스트를 자동 실행하지 않습니다 verify_completion은 lint와 typecheck만 실행하고 테스트는 의도적으로 제외합니다. 테스트는 너무 느리고 부작용이 있으며 데이터베이스 상태를 바꿀 수도 있습니다. 자동 게이트에 넣으면 이득보다 위험이 큽니다.
  • 자동 수정 없이 차단하고 이유를 설명합니다 bash_safety는 AI 대신 “더 안전한 명령”으로 고쳐 쓰지 않습니다. “이 명령은 X 때문에 차단됐습니다”라고만 알립니다. 다음 행동은 AI가 결정하게 합니다. 자동 수정은 더 기묘한 bug를 낳기 쉽습니다.
  • context budget 점수를 매기지 않습니다 context 사용량을 계산하고 압축 시점을 제안하려고 context_supervisor handler(800줄)를 만든 적이 있습니다. 발상은 좋았지만 점수 모델이 정확하지 않았습니다. context의 “중요성”은 휴리스틱 규칙으로 판단할 수 없었습니다. 현재는 비활성화했습니다.
  • Fail-Closed로 처리하지 않습니다 모든 handler가 crash하면 통과시킵니다. “위험하지 않나요?”라고 묻는 사람도 있겠지만, 안전장치 때문에 시스템 전체가 멈추는 일이 더 위험합니다. AI가 움직이지 않는데 사용자도 이유를 모르게 됩니다.
오픈 소스 프로젝트

hook-observatory

MIT 라이선스, 외부 의존성 없음, 설치 즉시 사용 가능합니다.
설치 방법은 Homebrew, Git Clone, 데스크톱 설치 프로그램 세 가지입니다.

★ GitHub ↓ Installer 다운로드

Homebrew

macOS에 권장합니다. 두 줄이면 끝나며 clone할 필요가 없습니다.

brew tap operonlab/tap
brew install hook-observatory

# Claude Code에 설치
hook-observatory
Git Clone

모든 플랫폼에서 사용할 수 있습니다. 설치 스크립트를 바로 실행합니다.

git clone https://github.com/operonlab/hook-observatory.git
cd hook-observatory
python3 install.py
설치 프로그램

GUI 안내에 따라 handler를 선택하고 도구 경로를 설정합니다.

macOS에서 처음 열 때 다음을 실행해야 합니다:xattr -cr 安裝器.app

.dmg Apple Silicon .dmg Intel Mac .exe Windows

도구

명령 몇 줄을 붙여 어떤 줄이 차단되는지 확인하세요

bash_safety 카드에는 네 종류의 명령이 나옵니다.rm -rf,sudo,git push --force,chmod 777.직접 실행해 본 명령 몇 줄을 붙여 넣으면 여기서 그 네 종류와 줄마다 비교하고, verify_commit 또는 secret_scan에 걸릴 줄도 표시합니다. 모두 브라우저 안에서 처리하며 아무것도 외부로 보내거나 실행하지 않습니다. 전체 history는 필요 없고 몇 줄이면 됩니다. 비밀 키, 내부 호스트 이름, 비공개 경로가 들어간 줄은 붙여 넣지 마세요.

판정에 쓰인 근거는 모두 이 글에 있습니다. 네 종류의 위험한 명령과 “quote-aware shell parser로 명령을 분석하므로 따옴표나 sub shell로 우회할 수 없다”는 설명은 bash_safety 카드에서 가져왔습니다.git commit / gh pr create 먼저 lint와 typecheck를 마쳐야 통과한다는 내용은 verify_commit 카드에서 가져왔습니다.git push 전에 diff를 검사한다는 내용은 secret_scan 카드에서 가져왔습니다. 여기서 할 수 없는 일은 네 가지입니다. ① 붙여 넣은 문자열을 판정할 뿐 실제로 컴퓨터에서 어떤 일이 일어날지는 알 수 없습니다. 아무것도 외부로 보내거나 실행하지 않습니다. ② 이 글에서 언급한 네 종류를 기준으로 대략 판단하며, hook-observatory의 실제 규칙이 아닙니다. 글에서 “같은 위험한 명령”이라고 했듯 목록 자체가 완전하지 않으므로 “빨갛게 표시되지 않았다”고 안전한 것은 아닙니다. ③ 이 글의 세 번째 critical handler인 skill_security는 PreToolUse · Write/Edit에 연결되어 명령줄이 아닌 파일 내용을 살피므로 여기서는 검사할 수 없습니다. ④ 이 글의 “의도적으로 하지 않는 것”에서 설명했듯, 차단되면 이유만 알려 주고 “더 안전하게 작성하는 방법”을 제시하지 않습니다.

이 부분을 가져가세요

AI Agent에게 복사해 전달하세요

문제는 “모든 권한을 줄 것인가”가 아니라 “권한을 준 뒤 그 가끔의 실수를 누가 막을 것인가”입니다. prompt에 “중요한 파일을 삭제하지 마”라고 써도 소용없습니다. AI는 확률적이라 어느 순간 다른 판단을 내릴 수 있지만, hook은 결정론적이므로 rm -rf /를 항상 차단합니다. 이 글은 첫 번째 동료가 AI 때문에 프로젝트 폴더 전체를 잃은 일에서 시작됐습니다.

아래 내용을 AI에 붙여 넣어 먼저 안전장치 없이 사용 중인지 확인하고, 이 글의 안전장치 목록에 따라 보완하게 하세요.

저는 〈그날 AI는 “정리”하기로 했고, 프로젝트가 사라졌습니다〉라는 글을 읽고 있습니다. (https://blog.joneshong.com/zh/blog/hook-observatory) 저자의 동료는 AI가 실행한 rm -rf 한 줄로 프로젝트 전체를 잃었고, 이를 계기로 hook 안전장치를 만들었습니다. 하나의 dispatcher + 유형별 handler로 구성하고, 안전 관련 handler를 critical로 표시해 항상 먼저 실행합니다(Two-Phase). 먼저 제 현재 상태를 실제로 확인한 다음, 이 방식에 따라 보완해 주세요. 일반적인 목록만 주지 마세요. 먼저 제가 안전장치 없이 사용 중인지 확인해 주세요. 1. 어떤 CLI를 사용하나요? 모든 권한을 주고 실행하고 있나요? (claude --dangerously-skip-permissions / codex full-auto / gemini --sandbox=false 같은 방식) 2. 현재 hook / pre-commit이 있나요? settings.json, hooks 디렉터리, .git/hooks를 확인해 주세요. 3. 최근 shell history에 위험한 명령이 차단되지 않고 실행된 적이 있나요? 그다음 이 글의 handler 분류를 기준으로 무엇부터 보완해야 할지 판단해 주세요. - bash_safety: rm -rf /, sudo, git push --force, chmod 777 차단(quote-aware로 처리해 따옴표나 sub shell로 우회하지 못하게) - secret_scan: push 전에 diff에서 AWS Key / GitHub Token / PEM 개인 키 검사 - skill_security: prompt injection, dangerouslyDisableSandbox, curl을 통한 외부 전송 차단 - verify_commit: commit 전에 lint / typecheck를 통과하도록 강제 답변에는 “사고가 나면 얼마나 아픈가”를 기준으로 우선 작성할 handler 3개와 각 탈출구(예: 줄별 예외)의 설계 방법을 알려 주세요. 제가 애초에 모든 권한을 주지 않았고 모든 명령에서 직접 allow를 누른다면, 아직 이 장치가 필요하지 않을 수도 있다고 분명히 말해 주세요.
참고 자료

더 읽을거리

Blake Crosley의 〈Why Each of My 95 Hooks Exists〉는 제게 깨달음을 주었습니다. 각 handler는 실제 사고 하나에 대응해야 합니다.

자료 중요한 이유
operonlab/hook-observatory 이 글에서 다룬 오픈 소스 프로젝트
Blake Crosley: Why Each of My 95 Hooks Exists 사고를 바탕으로 hook을 구축하는 접근 방식입니다. 각 hook 뒤에는 사고가 하나씩 있습니다.
GitButler: Automate Your AI Workflows with Claude Code Hooks 초기 hooks 실전 사례 중 하나로, 우리의 첫 설계에 영감을 주었습니다
aiorg.dev: Claude Code Hooks Complete Guide “결정론적 vs 확률적”이라는 틀로 hooks의 가치를 정확히 설명합니다
Jackle: Claude Code Voice Mode hooks로 음성 알림을 구현한 참고 사례로, voice_notify handler 설계에 영감을 주었습니다
✦ 프롬프트 복사