Claude Code 운영 장애 대응: 탐지, 롤백, RCA, 재발 방지
Claude Code 운영 장애 대응 가이드: 비밀키, 삭제, DB, 비용, 롤백, RCA, 재발 방지까지 정리.
Claude Code는 파일을 읽고, 코드를 고치고, 명령을 실행할 수 있어서 운영 저장소에서도 빠르게 움직입니다. 하지만 빠른 승인 하나가 비밀키 유출, 파일 삭제, main 덮어쓰기, 위험한 DB 마이그레이션, API 호출 폭증으로 이어질 수 있습니다.
이 글은 특정 회사의 실제 장애 보고서가 아닙니다. ClaudeCodeLab에서 진행한 장애 연습, 저장소 리뷰, 콘텐츠 운영 경험을 바탕으로 만든 합성 사례입니다. 시간과 금액은 예시지만, 패턴은 운영 전에 연습할 만큼 현실적입니다.
장애는 사용자, 데이터, 보안, 비용, 가용성에 영향을 주는 사건입니다. 봉쇄는 피해가 더 커지지 않게 멈추는 일입니다. RCA는 근본 원인 분석입니다. 롤백은 마지막으로 안전했던 버전으로 되돌리는 일입니다.
정확한 설정 문법은 공식 Claude Code settings와 hooks guide를 확인하세요. 여기서는 탐지, 봉쇄, 진단, 롤백, 커뮤니케이션, 사후 분석, 재발 방지를 하나의 흐름으로 정리합니다.
대응 흐름
| 단계 | 목적 | Claude Code에 시킬 일 |
|---|---|---|
| 탐지 | 무엇이 바뀌고 누가 영향을 받는지 확인 | 알림, 로그, diff, 배포, 최근 명령 요약 |
| 봉쇄 | 피해 확산 중지 | 키 폐기, 작업 중지, feature flag OFF, endpoint 차단 제안 |
| 진단 | 직접 원인 좁히기 | 마지막 정상 배포와 실패 변경 비교 |
| 롤백 | 안전한 상태로 복귀 | 대상 버전, 데이터 위험, 검증 명령 정리 |
| 커뮤니케이션 | 이해관계자 불안 줄이기 | 상태, 영향, 다음 업데이트 시각, 담당자 작성 |
| 사후 분석 | 사고를 학습으로 전환 | RCA, 타임라인, 탐지 누락, 액션 아이템 작성 |
| 예방 | 같은 사고를 어렵게 만들기 | permissions, hooks, CI, 알림, 리뷰 게이트 추가 |
비밀키, 과금, 개인정보, DB 쓰기와 관련된 사고는 먼저 봉쇄하고 나중에 조사합니다.
일곱 가지 사고 패턴
| 패턴 | 발생 상황 | 첫 조치 | 흔한 실패 |
|---|---|---|---|
| 비밀키 유출 | .env, 로그, 스크린샷에 API key 노출 | 키 폐기, 교체, 로그 확인 | git만 정리하고 CI 로그를 놓침 |
| 위험한 삭제 | rm -rf가 필요한 파일 삭제 | 작업 중지, 백업 확인, 미추적 파일 목록화 | git checkout .은 미추적 파일을 복구하지 못함 |
| force push | main이 팀원의 커밋을 덮어씀 | push 중지, reflog 확인, 복구 브랜치 생성 | --force-with-lease와 --force 혼동 |
| DB 마이그레이션 | drop, 대량 update, lock으로 운영 장애 | 쓰기 중지, 상태 보존, 복구 계획 | 검증 안 된 SQL을 운영에서 실행 |
| API 무한 재시도 | 실패할수록 호출과 비용 증가 | 프로세스 종료, 큐 중지, 한도 확인 | ”재시도”가 무한 루프가 됨 |
| 의존성 배포 실패 | 로컬은 통과, 운영은 503 | 이전 배포 재활성화, lockfile 확인 | npm update가 major 버전을 올림 |
| 인증 누락 | admin endpoint가 공개됨 | endpoint 차단, 접근 로그 확인 | ”admin”이라고만 쓰고 인증 요구를 안 씀 |
세 가지 실전 사례
API key 유출은 GitHub secret scanning, 클라우드 알림, 과금 화면에서 발견될 수 있습니다. 첫 조치는 원인 분석이 아니라 키 폐기입니다. 이후 공개 저장소, PR, CI 로그, 채팅, 모니터링을 확인합니다.
git status --short
git diff --cached --name-only
git log --all -- .env .env.local
git grep -n "sk-" -- ':!node_modules' ':!dist'
DB 마이그레이션 실패는 쓰기를 멈춘 뒤 조사합니다. 코드는 빠르게 롤백할 수 있지만 삭제된 데이터는 백업, WAL, 감사 로그, 외부 재동기화가 필요합니다.
psql "$DATABASE_URL" -c "select now();"
psql "$DATABASE_URL" -c "\d users"
pg_dump "$DATABASE_URL" --schema-only > schema_before_repair.sql
API 재시도에는 명확한 한도가 있어야 합니다. 아래 파일을 incident-budget-runner.mjs로 저장해 배치 작업을 감싸세요.
#!/usr/bin/env node
import { spawn } from "node:child_process";
const command = process.argv.slice(2);
const maxAttempts = Number(process.env.MAX_ATTEMPTS || 3);
const maxCostCents = Number(process.env.MAX_COST_CENTS || 200);
const costPerAttempt = Number(process.env.COST_PER_ATTEMPT_CENTS || 0);
if (command.length === 0) {
console.error("사용법: node incident-budget-runner.mjs <명령> [...인자]");
process.exit(2);
}
let estimatedCost = 0;
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
const child = spawn(command[0], command.slice(1), {
stdio: "inherit",
shell: process.platform === "win32"
});
const exitCode = await new Promise((resolve) => {
child.on("exit", (code) => resolve(code ?? 1));
});
estimatedCost += costPerAttempt;
if (exitCode === 0) process.exit(0);
if (estimatedCost >= maxCostCents) {
console.error(`중지: 예상 비용 ${estimatedCost} 센트 도달`);
process.exit(1);
}
const delayMs = Math.min(1000 * 2 ** (attempt - 1), 10_000);
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
console.error(`${maxAttempts}회 시도 후 실패`);
process.exit(1);
Claude Code 방어 설정
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Bash(git push --force *main*)",
"Bash(git push -f *main*)",
"Bash(rm -rf /*)",
"Bash(rm -rf ~*)"
],
"ask": [
"Bash(git push*)",
"Bash(rm*)",
"Bash(npm install*)",
"Bash(*migrate*)",
"Bash(*deploy*)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-danger.sh"
}
]
}
]
}
}
#!/usr/bin/env bash
set -euo pipefail
payload="$(cat)"
command="$(node -e 'const fs = require("fs"); const raw = fs.readFileSync(0, "utf8") || "{}"; const json = JSON.parse(raw); console.log(json.tool_input?.command || "");' <<< "$payload")"
blocked='(rm[[:space:]]+-rf[[:space:]]+(/|~)|git[[:space:]]+push[[:space:]].*(-f|--force)([[:space:]]|$)|DROP[[:space:]]+TABLE|TRUNCATE[[:space:]])'
if [[ "$command" =~ $blocked ]]; then
echo "위험한 명령을 차단했습니다: $command" >&2
exit 2
fi
exit 0
공지와 사후 분석 템플릿
## 장애 업데이트
- 상태: 조사 중 / 봉쇄 완료 / 복구 검증 중
- 영향: 기능, 사용자, 시작 시각
- 현재 조치: 작업 중지, 배포 롤백, 로그 확인
- 다음 업데이트: YYYY-MM-DD HH:mm
- 담당자:
# 사후 분석: [제목]
## 요약
- 시작:
- 탐지:
- 복구:
- 영향:
- 심각도: P0/P1/P2/P3
## 타임라인
| 시각 | 사건 |
| --- | --- |
| HH:mm | |
## 원인
- 직접 원인:
- 근본 원인:
- 탐지가 늦은 이유:
## 재발 방지
| 조치 | 담당자 | 기한 |
| --- | --- | --- |
| | | |
외부 참고 자료로는 Google SRE의 Postmortem Culture가 좋습니다.
관련 글과 CTA
Claude Code 보안 모범 사례, 권한 설정 가이드, API 비용 가이드, 검증 기록 워크플로도 함께 읽어 보세요.
개인 사용자는 무료 치트시트로 위험 명령과 확인 습관부터 고정하면 됩니다. 재사용 템플릿은 ClaudeCodeLab 제품에서, 팀 도입은 Claude Code 교육과 상담에서 CLAUDE.md, 권한, hooks, 리뷰, 장애 연습을 함께 정리할 수 있습니다.
ClaudeCodeLab 연습에서 가장 효과가 컸던 변화는 진단보다 봉쇄를 먼저 쓰는 것이었습니다. 게시 전 JSON, Bash, Node 구문을 확인한 것도 단순 오류를 줄였습니다. 20분짜리 리허설만 해도 빠진 알림, 검증되지 않은 백업, 너무 넓은 Claude Code 권한이 눈에 들어옵니다.
무료 PDF: Claude Code 치트시트
이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.
개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.
작성자 소개
Masa
Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.
관련 글
Claude Code 권한 세이프티 래더: 통제력을 잃지 않고 allow 넓히기
read-only에서 제한 편집, 검증 명령, deploy 확인까지 권한을 단계적으로 넓히는 방법.
Claude Code Small PR Proof Pack: 작은 PR을 리뷰 가능한 상태로 만드는 증거 세트
Claude Code의 작은 PR에 diff, 검증, 공개 URL, CTA 경로, rollback을 붙이는 실무 체크리스트.
Claude Code 커밋 전 리뷰 게이트: diff, 테스트, 공개 URL, CTA 확인
Claude Code 작업을 커밋하기 전에 diff 범위, build, 공개 URL, Gumroad 링크, 상담 CTA, 테스트 누락과 무관한 파일을 확인하는 방법입니다.