Claude Code로 발생한 운영 장애 7가지 사례: RCA·재발 방지책 포함 완전 복구 절차
Claude Code 운영에서 실제로 발생한 장애 7건 공개. API 키 유출·DB 삭제·과금 폭발·서비스 다운의 원인·복구 절차·RCA·재발 방지책을 완전 해설.
“Claude Code는 편리하지만 운영 환경에서 쓰기엔 무섭다”—이런 감각을 갖는 엔지니어가 많습니다. 그 직감은 맞습니다.
Claude Code는 IDE보다 높은 권한으로 파일 시스템과 셸을 조작합니다. 동시에, 승인 버튼을 계속 클릭하면서 경계심이 느슨해지는 인간 심리의 취약점도 있습니다. 이 두 가지가 결합될 때 운영 장애가 발생합니다.
이 글에서는 실제로 Claude Code가 관련된 운영 장애 7가지 사례를 원인·피해 범위·복구 절차·RCA(근본 원인 분석)·재발 방지책과 함께 공개합니다. 귀사의 사고가 나기 전에 읽어두십시오.
사례 1: API 키 유출 → 부정 사용으로 360만 원 과금
타임라인
09:12 Claude Code에 "CI에 환경 변수를 전달하기 위해 .env를 commit해줘"라고 지시
09:13 git add .env && git push가 승인 없이 실행됨 (allow 목록이 너무 넓었음)
09:14 GitHub이 시크릿 스캔으로 감지, 이메일 알림 발송
09:31 AWS 크롤러가 OpenAI 키를 감지, 부정 사용 시작
11:00 OpenAI 대시보드에서 360만 원 과금 확인
복구 절차
# Step 1: 즉시 API 키 무효화 (최우선·5분 이내)
# → OpenAI/각 서비스 대시보드에서 키 revoke
# Step 2: git 이력에서 .env를 완전 삭제
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch .env" \
--prune-empty --tag-name-filter cat -- --all
# Step 3: 전체 브랜치 force push
git push origin --force --all
git push origin --force --tags
# Step 4: .gitignore에 추가하여 재발 방지
echo ".env" >> .gitignore
git add .gitignore && git commit -m "security: add .env to gitignore"
# Step 5: 새 API 키 발급하여 .env에 설정
RCA
- 직접 원인:
settings.json의allow에Bash(git add*)가 있어서 확인 없이 실행됨 - 근본 원인: 보안 설정을 나중으로 미루고 프로덕트 코드를 우선시했음
재발 방지
// .claude/settings.json
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash(git add*)",
"hooks": [{
"type": "command",
"command": "git diff --cached --name-only | grep -E '\\.env' && echo '🚨 .env 감지! 중단' && exit 1 || exit 0"
}]
}]
}
}
사례 2: rm -rf로 프로젝트 전체가 소멸
타임라인
14:33 "node_modules를 정리하고 재설치해줘"라고 지시
14:33 Claude Code가 rm -rf node_modules 실행 (여기까지는 정상)
14:34 이어서 "오래된 빌드 파일도 지워줘" 지시로 rm -rf dist/ 실행
14:34 경로 해석 오류로 rm -rf dist /src가 실행됨 (공백 구분)
14:35 src/ 디렉터리 전체 소멸. git 관리 외 설정 파일도 삭제됨
14:40 git checkout .으로 관리 대상은 복원했지만, .env·로컬 설정은 영구 소실
복구 절차
# git 관리 대상은 복원 가능
git checkout .
git clean -fd # 불필요한 파일 삭제
# 삭제된 파일을 git stash나 reflog에서 찾기
git stash list
git reflog
# git 관리 외 (.env 등)는 백업에서 복원
# → 백업이 없는 경우 처음부터 재설정
RCA
- 직접 원인: 공백 포함 경로를 적절히 인용 부호로 감싸지 않아
rm -rf dist /src가 됨 - 근본 원인:
rm -rf가ask가 아닌allow에 있었음
재발 방지
{
"permissions": {
"deny": ["Bash(rm -rf ~*)", "Bash(rm -rf /*)"],
"ask": ["Bash(rm*)"]
},
"hooks": {
"PreToolUse": [{
"matcher": "Bash(rm*)",
"hooks": [{
"type": "command",
"command": "echo '⚠️ 삭제 명령 감지. 대상: $CLAUDE_TOOL_INPUT_COMMAND\n5초 후 실행. Ctrl+C로 중단.' && sleep 5"
}]
}]
}
}
사례 3: git push --force로 동료 3명의 커밋이 소멸
타임라인
16:00 "리모트와 충돌하고 있어. 로컬을 우선해서 덮어써줘"라고 지시
16:01 git push --force origin main 실행
16:01 동료 3명이 그날 커밋한 약 200줄의 코드가 소멸
16:10 동료 A가 "어? 내 커밋이 사라졌는데"라고 Slack 발언
16:15 원인 판명. 동료 A/B는 로컬 PC에 남아 있었지만, C씨는 삭제 후 완전 소실
복구 절차
# reflog로 사라진 커밋 찾기 (실행한 본인 머신에서)
git reflog | head -30
# → 예: abc1234 HEAD@{3}: push --force 전의 커밋
# 사라진 커밋 복원
git checkout -b recovery abc1234
git push origin recovery
# main에 머지하여 정리
git checkout main
git merge recovery --no-ff
git push origin main
RCA
- 직접 원인: 충돌 해소 의도가
--force-with-lease가 아닌--force로 해석됨 - 근본 원인: main 브랜치로의 force push가
deny에 없었음
재발 방지
{
"permissions": {
"deny": [
"Bash(git push --force *main*)",
"Bash(git push --force *master*)",
"Bash(git push -f *main*)"
]
}
}
<!-- CLAUDE.md -->
## git 규칙
- `git push --force`는 금지
- 충돌 해소에는 `git push --force-with-lease`를 사용
- main/master로의 push는 반드시 사용자 확인 후
사례 4: DB 마이그레이션 실패로 운영 데이터 4만 건이 소멸
타임라인
10:00 "users 테이블에 phone_number 컬럼을 추가하는 마이그레이션을 실행해줘"
10:01 Claude Code가 마이그레이션 스크립트를 생성·실행
10:01 스크립트 버그로 DROP COLUMN을 포함한 역방향 마이그레이션이 실행됨
10:02 운영의 users.email 컬럼(NOT NULL)이 소멸
10:02 전체 API가 500 에러 시작, 서비스 완전 정지
10:05 장애 인식, 원인 조사 시작
10:30 전날 스냅샷에서 복원 (1일분 데이터 소실)
복구 절차
# 1. 즉시 서비스를 유지보수 모드로
# nginx: return 503; 또는 Vercel: 유지보수 페이지
# 2. DB 현상 확인
psql $DATABASE_URL -c "\d users"
# 3. 백업에서 복원 (RDS의 경우)
aws rds restore-db-instance-to-point-in-time \
--source-db-instance-identifier mydb \
--target-db-instance-identifier mydb-restored \
--restore-time 2026-04-17T23:00:00Z
# 4. 컬럼이 남아 있는 경우 수동으로 추가
ALTER TABLE users ADD COLUMN email VARCHAR(255);
UPDATE users SET email = '(복원 필요)' WHERE email IS NULL;
# 5. 서비스 재개
RCA
- 직접 원인: 마이그레이션 스크립트 생성 시 up/down을 혼동함
- 근본 원인: 스테이징 환경에서의 테스트 없이 운영에서 실행함
재발 방지
<!-- CLAUDE.md -->
## DB 마이그레이션 필수 규칙
1. 스테이징 환경에서 반드시 동작 확인한 후 운영으로
2. 마이그레이션 전 반드시 수동 백업:
pg_dump $DATABASE_URL > backup_$(date +%Y%m%d_%H%M%S).sql
3. DROP COLUMN / TRUNCATE / DELETE (WHERE 없음)를 포함한 스크립트는
반드시 사용자에게 확인 받은 후 실행
4. 운영 DATABASE_URL의 경우 실행 전 '운영 DB에 쓰기입니다. 계속하시겠습니까?' 표시
사례 5: 무한 API 호출로 하룻밤에 90만 원 과금
타임라인
23:00 "에러가 나면 자동으로 재시도해줘"라고 지시하고 배치 처리 시작
23:01 외부 API가 503을 반환하기 시작
23:01 재시도 로직이 상한 없이 동작, 1초 간격으로 계속 호출
07:00 다음 날 아침, Anthropic에서 "사용량이 상한에 가까워지고 있습니다" 알림
07:05 확인하니 28,000번의 API 호출로 90만 원 과금 발생
복구 절차
# 1. 즉시 프로세스 정지
pkill -f "node batch-process.js"
# 2. 과금 상황을 확인하고 Anthropic 지원에 연락
# → 성실한 대응으로 일부 환불되는 경우 있음
# 3. 사용량 알림 설정
# Anthropic 콘솔 → Usage Limits → 월별 예산 알림 설정
재발 방지
// utils/retry.ts — 반드시 이 유틸리티를 사용할 것
export async function withRetry<T>(
fn: () => Promise<T>,
{ maxAttempts = 3, baseDelayMs = 1000, maxDelayMs = 30000 } = {}
): Promise<T> {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (err) {
if (attempt === maxAttempts) throw err;
const delay = Math.min(
baseDelayMs * 2 ** (attempt - 1) + Math.random() * 500,
maxDelayMs
);
console.warn(`Retry ${attempt}/${maxAttempts} in ${Math.round(delay)}ms`);
await new Promise(r => setTimeout(r, delay));
}
}
throw new Error("unreachable");
}
<!-- CLAUDE.md -->
## API 호출 규칙
- 재시도는 최대 3회, exponential backoff 필수
- while(true) + API 호출은 절대 금지
- 배치 처리는 건수 상한을 명시적으로 설정
- 운영 실행 전에 --dry-run으로 스모크 테스트
사례 6: 배포 후 의존성 파손으로 서비스 전체 정지
타임라인
15:00 "패키지를 최신 버전으로 업데이트해줘"라고 지시
15:01 npm update 실행, package-lock.json이 대폭 변경
15:05 로컬 빌드 통과
15:10 운영 배포 실행
15:12 운영에서 의존 패키지의 메이저 버전 불일치 발생, 기동 실패
15:12 503 에러, 서비스 전체 정지
15:30 이전 버전으로 롤백 완료
복구 절차
# Vercel / Cloudflare Pages의 경우: 이전 배포를 즉시 reactivate
# → 대시보드에서 1클릭으로 롤백 가능
# git revert로 코드도 되돌리기
git revert HEAD~1
git push
# package-lock.json을 이전 버전으로 되돌리기
git checkout HEAD~1 -- package-lock.json
npm ci # package-lock.json을 엄격하게 사용
RCA
- 직접 원인:
npm update가 메이저 버전을 올려 breaking change가 발생 - 근본 원인: 스테이징 환경에서의 배포 확인을 생략함
재발 방지
<!-- CLAUDE.md -->
## 패키지 관리 규칙
- npm update는 금지 (npm update --save-dev만 조건부 OK)
- 패키지의 메이저 버전 업은 사용자 확인 필수
- 배포 전 반드시 스테이징 환경에서 동작 확인
- 운영 배포 후 5분간 에러 로그 모니터링
사례 7: 권한 설정 실수로 전체 사용자 데이터가 열람 가능
타임라인
11:00 "관리 화면의 API 엔드포인트에 사용자 목록을 추가해줘"라고 지시
11:05 /api/admin/users가 인증 체크 없이 구현됨
11:10 운영 배포
11:10 누구나 전체 사용자의 개인정보에 접근 가능한 상태 발생
13:30 보안 감사 툴이 미인증 엔드포인트를 감지
13:35 엔드포인트를 즉시 무효화
복구 절차
# 1. 즉시 해당 엔드포인트 무효화
# nginx: location /api/admin { return 403; }
# 2. 액세스 로그를 확인하여 유출 범위 특정
grep "/api/admin/users" /var/log/nginx/access.log | \
awk '{print $1}' | sort | uniq -c | sort -rn
# 3. 영향받은 사용자에게 통지 (개인정보 보호법 요건 확인)
# 4. 인증 미들웨어를 추가하여 배포
RCA
- 직접 원인: “관리 화면에 추가해줘”라는 지시에서 인증 요건이 전달되지 않음
- 근본 원인: CLAUDE.md에 보안 요건이 기재되어 있지 않았음
재발 방지
<!-- CLAUDE.md -->
## API 보안 필수 요건
- /api/admin/* 엔드포인트에는 반드시 관리자 인증 체크를 구현
- /api/user/*에는 반드시 로그인 인증 체크를 구현
- 인증 없이 접근 가능한 API는 /api/public/*만
- 새 API를 추가할 때는 인증 레벨을 코멘트에 명기
장애 대응 공통 플로 (포스트모텀 템플릿)
# 포스트모텀: [장애 제목]
## 요약
- 발생 일시: YYYY-MM-DD HH:MM
- 감지 일시: YYYY-MM-DD HH:MM
- 복구 일시: YYYY-MM-DD HH:MM
- 영향 범위: (사용자 수/기능/기간)
- 심각도: P0/P1/P2/P3
## 타임라인
| 시각 | 사건 |
|-------|------|
| HH:MM | 장애 발생 |
| HH:MM | 감지 |
| HH:MM | 대응 시작 |
| HH:MM | 원인 특정 |
| HH:MM | 복구 완료 |
## 근본 원인
- 직접 원인:
- 근본 원인:
- 확대 원인:
## 재발 방지책
| 대책 | 담당 | 기한 |
|------|------|------|
| | | |
## 배운 점
정리: 장애를 막는 최소한의 설정
// 지금 바로 복사하여 .claude/settings.json에 붙여넣기
{
"permissions": {
"deny": [
"Bash(rm -rf ~*)",
"Bash(rm -rf /*)",
"Bash(git push --force *main*)",
"Bash(git push --force *master*)",
"Bash(git push -f *main*)",
"Bash(DROP TABLE*)",
"Bash(TRUNCATE *)",
"Bash(curl * | bash)",
"Bash(wget * | sh)"
],
"ask": [
"Write(**)", "Edit(**)",
"Bash(rm*)", "Bash(git commit*)",
"Bash(git push*)", "Bash(*deploy*)",
"Bash(npm install*)", "Bash(*migrate*)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash(git add*)",
"hooks": [{ "type": "command",
"command": "git diff --cached --name-only | grep '\\.env' && echo '🚨 .env 감지! 중단' && exit 1 || exit 0" }]
},
{
"matcher": "Bash(rm*)",
"hooks": [{ "type": "command",
"command": "echo '⚠️ 삭제 명령 감지. 5초 후 실행. Ctrl+C로 중단.' && sleep 5" }]
}
]
}
}
운영 장애는 “설정에 30분 투자하는 것”을 아낀 결과로 발생합니다. 이 글의 7가지 사례는 모두 적절한 settings.json과 CLAUDE.md가 있었다면 방지할 수 있었습니다.
관련 글
참고 자료
Claude Code 워크플로우를 한 단계 업그레이드하세요
지금 바로 Claude Code에 복사해 쓸 수 있는 검증된 프롬프트 템플릿 50선.
무료 PDF: 5분 완성 Claude Code 치트시트
이메일 주소만 등록하시면 A4 한 장짜리 치트시트 PDF를 즉시 보내드립니다.
개인정보는 엄격하게 관리하며 스팸은 보내지 않습니다.
이 글을 작성한 사람
Masa
Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.
관련 글
Claude Code API 비용 완전 정복: $450에서 $45/월로 줄인 5가지 실전 절감 기법
Claude Code API 요금을 실제 수치로 해설합니다. 프롬프트 캐싱·모델 최적화·배치 처리로 월 $450→$45, 90% 절감을 달성한 방법을 모두 공개합니다.
Claude Code의 위험한 프롬프트 패턴 10선 | 하면 안 되는 지시와 안전한 대안
Claude Code에 절대 주지 말아야 할 위험한 프롬프트 10가지. 코드 삭제·DB 파괴·과금 폭발·키 유출을 초래하는 지시와 안전한 대안 표현을 실례와 함께 해설합니다.
Claude Code 보안 완전 가이드: API 키 관리, 권한 설정, 프로덕션 보호
Claude Code를 안전하게 사용하기 위한 실전 보안 가이드. API 키 관리부터 권한 설정, Hooks 기반 자동화, 프로덕션 환경 보호까지 — 바로 동작하는 코드 예제와 함께 설명합니다.