
장애 대응 프로세스: 포스트모템 작성과 인시던트 관리
장애는 언젠가 반드시 일어난다. 중요한 건 얼마나 빨리 복구하고 무엇을 배우냐다. 인시던트 심각도 분류부터 포스트모템 작성, 실행되는 액션 아이템까지 SRE 실무를 정리했다.

장애는 언젠가 반드시 일어난다. 중요한 건 얼마나 빨리 복구하고 무엇을 배우냐다. 인시던트 심각도 분류부터 포스트모템 작성, 실행되는 액션 아이템까지 SRE 실무를 정리했다.
왜 넷플릭스는 멀쩡한 서버를 랜덤하게 꺼버릴까요? 시스템의 약점을 찾기 위해 고의로 장애를 주입하는 카오스 엔지니어링의 철학과 실천 방법(GameDay)을 소개합니다.

서비스를 운영하다 보면 장애는 피할 수 없다. 구글의 SRE 책을 읽으면서 '운영'이 단순 노가다가 아니라 고도의 엔지니어링 문제임을 이해했다. SLI, SLO, Error Budget 개념을 통해 소방관에서 건축가로 사고방식이 바뀌는 과정을 정리해본다.

서비스가 느리다는 항의를 받았는데, 로그파일만 뒤적거리다가 원인을 못 찾았던 경험이 있나요? Prometheus와 Grafana를 도입하여 '눈을 감고 운전하던' 상태에서 벗어난 경험과, 구글이 정의한 모니터링 4대 골든 시그널(Golden Signals)을 공유합니다.

편리함을 위해 시작한 스마트 홈 구축, 하지만 보안을 놓치면 집이 털릴 수도 있습니다. 와이파이 전구부터 스마트 도어락까지, IoT 기기들을 안전하게 연결하고 '로컬 제어' 시스템을 구축한 저의 삽질 기록을 공유합니다.

CRITICAL: API error rate 45% — P1 incident
PagerDuty 알림음에 잠에서 깼다. 절반 가까운 요청이 실패하고 있었다. 눈은 반쯤 뜨고, 손은 떨리고, 뭐부터 해야 할지 머릿속이 하얘진다.
그때 팀에 명확한 인시던트 프로세스가 없었다. 슬랙에 "저 에러 나요" 메시지들이 쏟아졌다. 누가 뭘 확인하고 있는지, 누가 고객에게 알려야 하는지, 지금 어느 단계인지 아무도 몰랐다. 각자 대시보드를 열어서 다른 걸 보고 있었다.
결국 4시간 만에 복구했다. 원인은 단순했다 — 잘못된 DB 인덱스 쿼리 하나. 그런데 복구에 4시간이 걸렸다. 프로세스가 없었기 때문에.
그 이후 우리 팀은 인시던트 프로세스를 처음부터 만들었다. 그 과정에서 배운 것들을 정리한다.
모든 알림이 같은 급박함을 갖는 건 아니다. 체계적인 대응을 위해 심각도(Severity)를 정의해야 한다.
| 레벨 | 이름 | 정의 | 대응 시간 |
|---|---|---|---|
| SEV-1 (P1) | Critical | 서비스 전체 다운, 데이터 손실 위험 | 즉시 (24/7) |
| SEV-2 (P2) | Major | 핵심 기능 장애, 대다수 사용자 영향 | 30분 내 (업무 시간 외 포함) |
| SEV-3 (P3) | Minor | 일부 기능 장애, 소수 사용자 영향 | 업무 시간 내 |
| SEV-4 (P4) | Low | 사소한 버그, 우회 방법 있음 | 일반 티켓으로 처리 |
SEV-1:
- 에러율 > 10% (전체 요청)
- 결제/인증 기능 다운
- 데이터 손실/노출 가능성
- 전체 서비스 접근 불가
SEV-2:
- 에러율 > 5% (핵심 기능)
- 레이턴시 > 정상의 3배 이상
- 특정 지역/세그먼트 사용자 100% 영향
SEV-3:
- 에러율 > 1% (비핵심 기능)
- 우회 방법 존재
- 소수 사용자만 영향
SEV-4:
- 사용성 저하
- 모니터링/내부 도구 이슈
인시던트 중 혼란의 가장 큰 원인은 "누가 뭘 할지 모르는 것"이다. 명확한 역할 정의가 해결책이다.
역할: 전체 대응을 지휘한다. 기술적 문제 해결이 아니라 조율이 주 임무다.
책임:
중요: IC는 직접 디버깅하지 않는다. 다른 사람이 디버깅하도록 조율한다.
역할: 인시던트 외부 커뮤니케이션 전담.
책임:
IC가 내부 기술 대화에 집중할 수 있도록, Comms가 외부 커뮤니케이션을 전담한다.
역할: 실제 기술적 문제를 진단하고 해결한다.
역할: 모든 것을 기록한다.
[03:15] IC가 SEV-1 선언. Kim이 IC, Lee가 SME 백엔드.
[03:17] 에러율 확인: 45%, 주로 /api/payments 엔드포인트
[03:22] Lee: 결제 DB 쿼리 슬로우 쿼리 로그 확인 중
[03:31] Lee: 특정 인덱스 미사용으로 풀 테이블 스캔 발생 확인
[03:35] 임시 조치: 결제 기능 점검 모드로 전환
[03:40] 에러율 2%로 감소
[04:10] 인덱스 재생성 완료, 에러율 0.1% 정상화
[04:15] IC: SEV-1 인시던트 종료 선언
이 기록이 나중에 포스트모템의 근거가 된다.
#incidents → 실시간 대응 스레드 (기술 논의)
#incidents-updates → 정제된 상태 업데이트 (경영진, 다른 팀)
#on-call-alerts → PagerDuty/알림 수신
#incidents는 외부에 공유하지 않는다. 원인 분석 과정의 가설들이 오해를 살 수 있다.
인시던트 중 5~15분마다 업데이트를 올린다:
**[03:20] SEV-1 업데이트 #2**
**현재 상태**: 대응 중 (Investigating)
**영향 범위**: 결제 API, 전체 사용자
**에러율**: 45% → 38% (소폭 감소)
**현재 작업**: DB 슬로우 쿼리 분석 중
**다음 업데이트**: 03:35
**IC**: Kim / **SME**: Lee
상태는 5가지로 관리:
Investigating - 원인 파악 중Identified - 원인 파악 완료, 해결 중Monitoring - 임시 조치 완료, 안정화 모니터링Resolved - 복구 완료Post-Incident - 포스트모템 준비 중상태 페이지(StatusPage) 업데이트 원칙:
## Incident: Payment API Degradation
**Status**: Investigating
**Started**: March 20, 2026 03:15 UTC
We are currently investigating increased error rates on our payment processing API.
Some users may experience failures when attempting to complete purchases.
We will provide updates every 15 minutes.
---
**Update 03:35 UTC**: We have identified the root cause and are implementing a fix.
Current error rate has decreased from 45% to 15%.
---
**Update 04:20 UTC**: The issue has been resolved. All systems are operating normally.
A full postmortem will be published within 48 hours.
사전에 플레이북을 작성해 두면 새벽 3시에도 명확하게 행동할 수 있다.
# 고에러율 플레이북
## 트리거
- 에러율 > 5% 5분 지속
## 즉시 확인 사항 (5분 내)
1. [ ] 어떤 엔드포인트에서 에러 발생?
- Datadog: `sum:trace.web.request.errors{*} by {resource_name}`
2. [ ] 최근 배포가 있었나?
- GitHub: `/compare/HEAD~1..HEAD`
- ArgoCD 배포 히스토리
3. [ ] 외부 의존성 상태?
- Stripe, Supabase, AWS 상태 페이지
## 진단 트리
에러율 > 10%?
├── YES → SEV-1 선언, 즉시 롤백 고려
└── NO → SEV-2 선언, 원인 파악 후 대응
최근 배포 있음?
├── YES → 배포 롤백 먼저 시도 (빠른 복구 우선)
└── NO → 인프라/외부 서비스 점검
## 롤백 절차
1. GitHub Actions: 이전 배포 태그로 재실행
2. ArgoCD: Previous version으로 롤백
3. Feature Flag: 관련 플래그 즉시 OFF
## 에스컬레이션 기준
- 15분 내 원인 불명 → 시니어 엔지니어 호출
- 30분 내 미해결 → CTO 알림
인시던트가 끝나면 포스트모템을 작성한다. 목적은 비난이 아니라 학습이다.
"Kim이 잘못된 배포를 했다" → 누구의 실수인가 "배포 파이프라인이 스테이징에서 해당 패턴을 감지하지 못했다" → 시스템의 문제
개인을 비난하면:
시스템을 개선하면:
# 포스트모템: [인시던트 제목]
**날짜**: 2026-03-20
**심각도**: SEV-1
**대응 시간**: 03:15 ~ 04:15 (1시간)
**작성자**: Kim
**검토자**: Lee, Park
---
## 요약 (Executive Summary)
2026년 3월 20일 03:15~04:15 (UTC), 결제 API 에러율이 45%까지 상승했다.
원인은 최근 배포된 쿼리에서 인덱스를 사용하지 않아 발생한 풀 테이블 스캔이었다.
추정 영향: 약 2,300건의 결제 시도 실패.
---
## 타임라인
| 시각 | 이벤트 |
|------|--------|
| 03:00 | v2.14.0 배포 완료 (결제 쿼리 최적화 포함) |
| 03:12 | Datadog 에러율 알림 발생 (5% 임계값) |
| 03:15 | PagerDuty on-call 호출. Kim이 IC 지정 |
| 03:17 | Lee: /api/payments 에러율 45% 확인 |
| 03:22 | Lee: DB 슬로우 쿼리 로그 분석 시작 |
| 03:31 | Lee: `user_payment_methods` 테이블 풀 스캔 확인 |
| 03:35 | 임시 조치: 결제 기능 점검 모드 전환 → 에러율 2%로 감소 |
| 03:40 | 인덱스 생성 작업 시작 |
| 04:10 | 인덱스 생성 완료, 점검 모드 해제 |
| 04:15 | 에러율 0.1% 정상화, SEV-1 종료 |
---
## 근본 원인 분석 (5 Whys)
**Q: 왜 에러율이 45%까지 올랐나?**
A: 결제 API 쿼리가 타임아웃 발생
**Q: 왜 쿼리가 타임아웃됐나?**
A: `user_payment_methods` 테이블에 풀 테이블 스캔이 발생함
**Q: 왜 풀 테이블 스캔이 발생했나?**
A: v2.14.0에서 수정된 쿼리가 기존 인덱스를 사용하지 않았음
**Q: 왜 인덱스를 사용하지 않았나?**
A: 쿼리 조건절 순서가 바뀌면서 복합 인덱스의 leftmost prefix 규칙에서 벗어남
**Q: 왜 배포 전에 발견하지 못했나?**
A: 스테이징 환경의 데이터가 100만 건인 프로덕션과 달리 1,000건뿐이어서
슬로우 쿼리로 드러나지 않았음. CI에 쿼리 실행 계획 검증이 없었음.
**근본 원인**: 스테이징 데이터가 프로덕션과 규모가 다르고, 배포 파이프라인에
쿼리 실행 계획 검증이 없었음.
---
## 영향
- **사용자**: 2,300건 결제 시도 실패 (03:15~03:35, 20분간)
- **매출**: 약 $46,000 추정 피해
- **고객 문의**: 87건 접수
---
## 잘 된 것 (What Went Well)
1. PagerDuty 알림이 에러 발생 3분 내 울렸다
2. 인시던트 채널이 명확하게 관리되었다
3. 임시 조치(점검 모드)로 빠르게 피해를 줄였다
4. 기록자가 타임라인을 정확히 기록해 분석이 쉬웠다
---
## 개선 필요 (What Went Wrong)
1. 스테이징과 프로덕션 데이터 규모 차이로 문제 미감지
2. 쿼리 실행 계획 자동 검증 없음
3. 결제 기능 점검 모드 전환에 수동 작업 필요 (10분 소요)
4. 고객 상태 페이지 업데이트가 첫 30분간 없었음
---
## 액션 아이템
| 항목 | 담당자 | 기한 | 우선순위 |
|------|--------|------|----------|
| CI에 EXPLAIN ANALYZE 기반 쿼리 실행 계획 검증 추가 | Lee | 2026-03-27 | P1 |
| 스테이징 DB 데이터를 프로덕션 대비 10% 이상으로 유지하는 스크립트 작성 | Park | 2026-03-31 | P1 |
| 결제 킬스위치 Feature Flag 구현 | Kim | 2026-03-28 | P2 |
| 인시던트 발생 5분 내 상태 페이지 업데이트 자동화 | Park | 2026-04-07 | P2 |
| DB 쿼리 성능 이상 감지 알림 추가 | Lee | 2026-04-07 | P2 |
포스트모템에서 근본 원인을 파고드는 가장 강력한 도구다. 도요타에서 시작된 방법론.
"왜"를 다섯 번 물어보되, 각 대답이 다음 "왜"의 주어가 되어야 한다.
나쁜 예:Q: 왜 서비스가 죽었나?
A: Kim이 잘못된 코드를 배포해서.
Q: 왜 Kim이 잘못된 코드를 배포했나?
A: 실수로.
→ 여기서 막힘. "실수하지 마라"는 액션 아이템이 되어버림.
좋은 예:
Q: 왜 서비스가 죽었나?
A: 배포된 코드에 메모리 누수가 있었다.
Q: 왜 메모리 누수가 있었나?
A: PR 리뷰에서 발견되지 않았다.
Q: 왜 PR 리뷰에서 발견되지 않았나?
A: 메모리 프로파일링이 리뷰 기준에 없었다.
Q: 왜 메모리 프로파일링이 기준에 없었나?
A: 프론트엔드 PR 리뷰 체크리스트가 없었다.
Q: 왜 체크리스트가 없었나?
A: 문서화된 리뷰 프로세스가 없었다.
→ 액션 아이템: PR 리뷰 체크리스트 작성
포스트모템 액션 아이템의 흔한 문제: 아무것도 실행되지 않는다.
나쁜 액션 아이템:
- "모니터링 개선하기" → 너무 모호
- "팀 전체 인식 개선" → 담당자 없음
- "코드 리뷰 더 잘하기" → 측정 불가
좋은 액션 아이템:
- "Datadog에 결제 에러율 5% 알림 추가 (담당: Lee, 기한: 3/27)"
- "CI 파이프라인에 Lighthouse 성능 점수 80점 미만 빌드 실패 추가 (담당: Kim, 기한: 3/31)"
- "배포 플레이북에 DB 마이그레이션 단계 추가 (담당: Park, 기한: 4/07)"
| 기준 | 설명 |
|---|---|
| Specific | 구체적: 무엇을? |
| Measurable | 측정 가능: 완료 기준은? |
| Assignable | 담당자: 누가? |
| Realistic | 실현 가능: 기한 내 가능한가? |
| Time-bound | 기한: 언제까지? |
## 액션 아이템 트래커
| ID | 항목 | 담당 | 기한 | 상태 | 관련 이슈 |
|----|------|------|------|------|----------|
| A1 | CI에 EXPLAIN ANALYZE 추가 | Lee | 3/27 | 완료 | #1234 |
| A2 | 스테이징 데이터 스크립트 | Park | 3/31 | 진행중 | #1235 |
| A3 | 결제 킬스위치 구현 | Kim | 3/28 | 진행중 | #1236 |
매주 엔지니어링 미팅에서 액션 아이템 진행 상황 체크. 기한이 지난 것은 그냥 넘어가지 말고 재조정.
나쁜 알림:
- CPU 사용율 70% 이상
- 메모리 70% 이상
- 응답 시간 500ms 이상
→ 하루에 수십 개 알림 → 모두 무시
좋은 알림:
- 사용자에게 직접 영향: 에러율, P99 레이턴시, 결제 실패율
- 임박한 자원 고갈: 디스크 4시간 내 가득 찰 예측
원칙: 모든 알림은 액션이 필요해야 한다. 액션 없이 무시해도 되는 알림은 삭제하거나 낮춰라.
# PagerDuty 로테이션 예시
rotation:
type: weekly # 주간 교대
team:
- Kim (week 1)
- Lee (week 2)
- Park (week 3)
escalation:
level1: on-call 담당자 (5분 내 응답)
level2: 팀 리드 (15분 내 미응답 시 에스컬레이션)
level3: VP Engineering (30분 내 미해결 시)
On-call은 벌이 아니다. 팀이 해야 할 일이다.
얼마나 잘 대응하는지 측정해야 개선할 수 있다.
MTTD (Mean Time to Detect)
: 장애 발생 → 알림까지 시간
MTTA (Mean Time to Acknowledge)
: 알림 발생 → IC 지정까지 시간
MTTR (Mean Time to Resolve)
: IC 지정 → 인시던트 종료까지 시간
MTTM (Mean Time to Mitigate)
: IC 지정 → 임시 조치 완료까지 시간
# 인시던트 메트릭 계산
from datetime import datetime, timedelta
class IncidentMetrics:
def __init__(self):
self.incidents = []
def add_incident(
self,
incident_id: str,
occurred_at: datetime,
detected_at: datetime,
acknowledged_at: datetime,
mitigated_at: datetime,
resolved_at: datetime,
):
self.incidents.append({
'id': incident_id,
'mttd': (detected_at - occurred_at).seconds / 60,
'mtta': (acknowledged_at - detected_at).seconds / 60,
'mttm': (mitigated_at - acknowledged_at).seconds / 60,
'mttr': (resolved_at - acknowledged_at).seconds / 60,
})
def summary(self):
import statistics
mttrs = [i['mttr'] for i in self.incidents]
return {
'total_incidents': len(self.incidents),
'avg_mttr_minutes': statistics.mean(mttrs),
'p95_mttr_minutes': statistics.quantiles(mttrs, n=20)[18],
}
들으면 황당하지만 — 잘 대응된 장애는 팀에게 선물이다.
시스템의 취약점을 드러낸다. 모니터링의 사각지대를 보여준다. 문서화되지 않은 암묵적 지식을 수면 위로 올린다. 그리고 팀이 압박 상황에서 어떻게 협력하는지 보여준다.
블레임리스 포스트모템을 제대로 쓰면, 장애 하나가 여러 개의 시스템 개선으로 이어진다. 나쁜 운이 좋은 투자가 되는 것이다.
물론 그러려면 프로세스가 있어야 한다. 새벽 3시에 아무것도 생각 안 날 때 플레이북이 있어야 하고, 역할이 명확해야 하고, 포스트모템을 쓸 에너지가 남아 있어야 한다.
그게 SRE 문화의 핵심이다. 장애를 두려워하지 말고, 장애로부터 배워라.