TIL - 20260925

juni·2026년 9월 25일

TIL

목록 보기
462/468

0925 운영 자동화/AI 워크플로우 심화 (19/N): Failure Injection, Chaos Testing과 장애 복구 훈련


✅ 1. 장애 대응 코드는 ‘작성했다’고 끝이 아니다

예를 들어 다음 구조를 만들었다고 하자.

외부 알림톡 API

Timeout
5초

Retry
최대 3회

Circuit Breaker
실패율 증가 시 OPEN

Queue
재처리 가능

Reconciliation
UNKNOWN 상태 복구

코드만 보면 꽤 안전해 보인다.

하지만 실제 장애가 발생하면 전혀 예상하지 못한 문제가 나올 수 있다.

Timeout은 걸리지만 HTTP Connection이 안 닫힘

Retry가 동시에 몰려 Retry Storm 발생

Circuit Breaker가 열렸지만 Queue Worker는 계속 요청

Worker 재시작 후 동일 Job 중복 실행

UNKNOWN Job이 Reconciliation 대상에 포함되지 않음

즉:

장애 대응 로직 역시 장애 상황에서 테스트해야 한다.


✅ 2. 일반 테스트만으로 부족한 이유

일반적인 테스트에서는 대부분 정상 상황을 검증한다.

주문 생성 성공

알림톡 발송 성공

Webhook 처리 성공

Export 완료

실패 테스트를 넣더라도:

provider.send.mockRejectedValue(
  new Error('timeout'),
);

정도에 그치는 경우가 많다.

하지만 실제 장애는 훨씬 복잡하다.

예:

Provider 응답이 30초 동안 오지 않음

500 Error가 간헐적으로 발생

첫 요청은 성공했지만 응답만 유실

DB 연결이 10초 동안 끊김

Worker가 작업 도중 종료

Queue에는 같은 Job이 두 번 전달

Webhook이 순서가 뒤집혀 도착

이런 상황까지 검증해야 운영 신뢰성이 높아진다.


✅ 3. Failure Injection이란?

Failure Injection은 의도적으로 실패 상황을 만들어보는 것이다.

예:

외부 API Timeout 발생시키기

DB Connection 실패시키기

Worker 강제 종료

HTTP 500 반환

Latency 인위적으로 증가

Webhook 중복 전달

Queue Job 중복 실행

목표는 서비스를 망가뜨리는 것이 아니다.

목표는:

실패했을 때
우리가 설계한 방어 장치가
정말 작동하는가?

를 확인하는 것이다.


✅ 4. Chaos Testing과 Failure Injection의 차이

실무에서는 용어가 겹쳐 사용되기도 하지만 개념적으로는 이렇게 이해하면 편하다.

Failure Injection

특정 실패를 의도적으로 발생시켜
특정 방어 로직을 테스트

예:

Notification Provider Timeout

반면:

Chaos Testing

실제 시스템에 예상치 못한 장애를 주입하고
전체 서비스가 얼마나 버티는지 검증

조금 더 넓은 개념이다.

현재 프로젝트에서는 거대한 Chaos Engineering 플랫폼보다 작은 Failure Injection 테스트부터 시작하는 것이 맞다.


✅ 5. 지금 당장 Production에서 Chaos Test를 하면 안 된다

처음부터 운영 서버에서:

DB 끊어보기

Worker 죽이기

Network 차단

Random 500 발생

같은 실험을 하면 위험하다.

우선 순서는:

Unit Test
↓
Integration Test
↓
Local
↓
Staging
↓
Controlled Production Test

가 되어야 한다.

현재 규모에서는 대부분:

Local + Staging

까지만 해도 충분한 효과가 있다.


✅ 6. Chaos Engineering의 핵심은 ‘랜덤 장애 만들기’가 아니다

Chaos Testing을 단순히:

서버를 랜덤하게 죽여본다

라고 생각하기 쉽다.

하지만 중요한 것은 먼저 정상 상태를 정의하는 것이다.

예:

정상 상태

주문 성공률 > 99%

Notification Queue Lag < 30초

UNKNOWN Job < 5건

그리고 장애를 주입한다.

Provider Timeout 발생

그 후 확인한다.

주문 생성은 정상인가?

Queue가 폭주하지 않는가?

Circuit Breaker가 열리는가?

Provider 정상화 후 자동 복구되는가?

✅ 7. Steady State

Chaos Testing에서는 정상 상태를 Steady State라고 생각할 수 있다.

예:

Order API

Success Rate
99.9%

p95
500ms


Notification

Queue Delay
< 10s

Failure Rate
< 1%

실험 전후로 이 기준이 어떻게 변하는지 본다.


✅ 8. Hypothesis부터 정한다

무작정 장애를 넣는 것이 아니다.

먼저 가설을 만든다.

예:

가설

알림톡 Provider가 5분 동안 장애가 발생해도
고객 주문 생성 기능은 정상적으로 동작한다.

그 이유는:

주문 생성
↓
DB Commit
↓
Queue
↓
Notification Worker

로 분리되어 있기 때문이다.


✅ 9. 실험 결과

실험 후:

Order API
정상

Notification Queue
Pending 증가

Retry
Backoff 정상

Circuit Breaker
OPEN

Provider 복구 후
Queue 정상 처리

라면 설계가 제대로 작동한 것이다.

반대로 주문 API까지 느려진다면:

Notification Provider가
Order Request 흐름에 아직 결합되어 있다.

는 문제를 발견할 수 있다.


✅ 10. 투게더몰에서 가장 먼저 테스트할 장애

현재 프로젝트에서는 다음이 현실적인 대상이다.

1. 알림톡 Provider Timeout

2. 알림톡 Provider 500

3. Worker 강제 종료

4. Queue 중복 Job

5. Webhook 중복 수신

6. Webhook 순서 역전

7. PostgreSQL 일시 연결 실패

8. Export 중 S3 업로드 실패

9. AI Run 도중 프로세스 종료

10. 배포 후 Health Check 실패

✅ 11. Provider Timeout 테스트

정상 흐름:

Notification Worker
↓
Provider
↓
200 OK

장애 실험:

Notification Worker
↓
Provider

응답 20초 지연

우리 Timeout이 5초라면:

5초 후 Timeout

↓
UNKNOWN 또는 Retry 판단

↓
Backoff

↓
Circuit Breaker 상태 반영

되어야 한다.


✅ 12. Timeout 테스트에서 확인할 것

단순히:

Timeout 발생 여부

만 보면 부족하다.

확인해야 할 것은:

HTTP Connection이 해제되는가?

Worker Thread가 계속 점유되지 않는가?

Retry가 즉시 몰리지 않는가?

Job 상태가 정확한가?

Correlation ID가 유지되는가?

Error Metric이 증가하는가?

이다.


✅ 13. 500 Error 테스트

예:

Provider

첫 요청
500

두 번째
500

세 번째
200

이 상황에서:

Retry 2회

최종 SUCCESS

가 되어야 한다.

그리고 로그에는:

attempt = 1
FAILED

attempt = 2
FAILED

attempt = 3
SUCCESS

가 남아야 한다.


✅ 14. Retry Backoff 검증

Retry를:

1초

2초

4초

8초

처럼 두었다면 실제로 그 간격을 지키는지 확인한다.

잘못 구현되어:

1초
1초
1초
1초

가 되면 Provider 장애 상황에서 부하를 키울 수 있다.


✅ 15. Jitter도 테스트한다

모든 Worker가 똑같이:

4초 후 Retry

하면 동시에 다시 요청한다.

그래서:

3.7초
4.4초
4.1초
3.9초

처럼 약간의 랜덤성을 준다.

이를 Jitter라고 한다.

테스트에서는 정확한 값이 아니라:

허용 범위 내 분산

을 확인하면 된다.


✅ 16. Circuit Breaker 테스트

예:

최근 20회 요청 중
15회 실패

가 Threshold를 넘었다고 하자.

Circuit 상태:

CLOSED
↓
OPEN

되어야 한다.

그리고 새로운 요청은 Provider에 실제로 전달되지 않아야 한다.


✅ 17. Circuit OPEN 상태 확인

예:

Circuit = OPEN

일 때:

NotificationJob

은 삭제하거나 실패 처리하면 안 된다.

보통:

Retry Pending

또는

Queue 대기

상태로 유지해야 한다.

Provider 장애가 주문 데이터 손실로 이어져서는 안 된다.


✅ 18. Half-Open 테스트

일정 시간이 지나면 Circuit Breaker는 일부 요청만 보내 Provider가 복구됐는지 확인할 수 있다.

OPEN

↓ 일정 시간

HALF_OPEN

↓ 테스트 요청

성공하면:

CLOSED

실패하면:

OPEN

으로 돌아간다.

이 상태 전환 역시 테스트해야 한다.


✅ 19. Circuit Breaker 테스트 시 중요한 것

확인:

OPEN 전환 조건

OPEN 유지 시간

HALF_OPEN 요청 개수

복구 후 CLOSED 전환

Metric 기록

Log 기록

이다.


✅ 20. Worker Crash 테스트

작업 도중 Worker가 죽는 상황도 중요하다.

예:

Job Claim

↓
PROCESSING

↓
외부 API 호출

↓
Worker 강제 종료

DB에는:

PROCESSING

만 남을 수 있다.


✅ 21. Worker Crash 이후 기대 흐름

0916~0917에서 만든 구조가 있다면:

PROCESSING

↓
heartbeat 중단

↓
lease 만료

↓
Stale Job 감지

↓
Reconciliation

↓
RETRY_PENDING 또는 SUCCESS

로 복구되어야 한다.


✅ 22. Worker Crash 테스트에서 확인할 것

Job이 영원히 PROCESSING으로 남지 않는가?

다른 Worker가 Lease 만료 전 가져가지 않는가?

Lease 만료 후 안전하게 회수되는가?

외부 요청이 이미 성공했다면 중복 실행하지 않는가?

특히 마지막이 중요하다.


✅ 23. ‘외부에서는 성공했는데 Worker가 죽는 상황’

매우 까다로운 케이스다.

Provider 요청

↓
Provider SUCCESS

↓
Worker가 DB 업데이트 전 종료

DB:

PROCESSING

외부:

SUCCESS

이때 단순 Retry하면 중복 발송될 수 있다.

따라서:

providerMessageId

idempotencyKey

Reconciliation

가 필요하다.


✅ 24. 이 시나리오는 반드시 테스트할 가치가 있다

예:

Provider Send
SUCCESS

↓ 인위적 Crash

DB Update 수행 안 함

그 후 Reconciler가:

Provider 상태 조회

↓
DELIVERED

↓
Job SUCCESS 복구

하는지 검증한다.

이 테스트 하나가 0916~0917 설계 대부분을 실제로 검증한다.


✅ 25. Queue 중복 전달 테스트

Queue 시스템에서는 같은 메시지가 두 번 전달될 수 있다고 가정하는 편이 안전하다.

예:

job_123
job_123

두 Worker가 받는다.


✅ 26. 기대 결과

Atomic Claim 또는 Idempotency가 있다면:

Worker A
Claim 성공
→ 실행

Worker B
Claim 실패
→ Skip

이 되어야 한다.

최종 결과:

알림톡 1회

Audit Log 1회

상태 변경 1회

여야 한다.


✅ 27. 동시 실행 테스트

이건 실제로 Race Condition을 찾는 데 매우 중요하다.

Promise.all([
  execute(jobId),
  execute(jobId),
  execute(jobId),
]);

처럼 동시에 실행시켜본다.

결과가:

외부 Side Effect
1회

인지 확인한다.


✅ 28. Webhook 중복 테스트

외부 Provider는 같은 Webhook을 다시 보낼 수 있다.

예:

providerEventId = evt_123

를:

1회
2회
3회

전송한다.

DB에는:

WebhookEvent
1건

만 처리되어야 한다.


✅ 29. Webhook 순서 역전 테스트

더 어려운 상황은 이벤트 순서가 뒤집히는 것이다.

원래:

SENT
↓
DELIVERED

인데 실제 수신:

DELIVERED
↓
SENT

일 수 있다.

두 번째 이벤트 때문에 상태가 다시:

SENT

로 후퇴하면 안 된다.


✅ 30. 상태 전이 규칙이 필요한 이유

예:

PENDING
→ SENT
→ DELIVERED

인 경우:

DELIVERED
→ SENT

는 금지한다.

즉 상태 머신에서도:

Allowed Transition

을 정의해야 한다.


✅ 31. PostgreSQL 연결 실패 테스트

예:

DB 연결 10초 차단

시 확인한다.

API가 무한 대기하지 않는가?

Connection Timeout이 있는가?

HTTP 500/503이 적절히 반환되는가?

Queue Worker가 폭주하지 않는가?

DB 복구 후 자동 정상화되는가?

✅ 32. DB 장애에서 무조건 Retry하면 위험한 작업

예:

Transaction Commit 직후
Connection 끊김

이면 실제 Commit 여부가 불확실할 수 있다.

따라서 Write 작업은 특히:

무조건 Retry

가 위험할 수 있다.

Idempotency와 상태 확인이 필요하다.


✅ 33. Transaction 실패 테스트

예:

Order 저장 성공

Audit Log 저장 실패

같은 상황을 일부러 만든다.

하나의 Transaction이어야 한다면 최종 결과는:

둘 다 Rollback

이어야 한다.


✅ 34. Transaction Boundary 검증

0826에서 배운 Transaction Boundary와 연결된다.

Order 상태 변경

Audit Log

Outbox Event

가 하나의 일관된 변경이어야 한다면 실패 주입 후:

일부만 저장

되는지 확인한다.


✅ 35. Outbox Failure 테스트

예:

DB 변경
SUCCESS

Outbox Worker
FAILED

여기서 비즈니스 데이터까지 다시 Rollback할 수는 없다.

대신:

Outbox Event
Pending 유지

↓
Worker 복구

↓
후속 Event 발행

이 되어야 한다.


✅ 36. S3 Upload 실패 테스트

Export 구조에서:

Excel 생성
SUCCESS

↓
S3 Upload
FAILED

할 수 있다.

이때:

ExportJob 전체 재생성

보다:

이미 생성한 Artifact 유지

↓
Upload Step부터 Resume

할 수 있다면 효율적이다.


✅ 37. Step 기반 Workflow가 강한 이유

예:

ExportJob

1. Query Data
SUCCESS

2. Generate Excel
SUCCESS

3. Upload S3
FAILED

재시작:

Step 3

부터 가능하다.

이런 구조는 AI Run에도 그대로 적용된다.


✅ 38. AI Run Crash 테스트

예:

Analyse
SUCCESS

Modify
SUCCESS

Test
실행 중

↓
AI Runner 종료

다시 실행할 때:

Analyse부터 재실행

하는 것이 아니라 현재 상태를 Reconcile해야 한다.


✅ 39. AI Run에서 확인할 실제 상태

Git Commit

git diff

생성된 Artifact

Test Report

Process 상태

Checkpoint

Approval

를 확인한다.


✅ 40. AI Resume Test

예:

Step 1 Analyse
SUCCESS

Step 2 Modify
SUCCESS

Step 3 Test
SUCCESS

Step 4 Report
FAILED

Resume 시:

Report

부터 다시 시작해야 한다.

그리고 이전 파일 수정이 중복 실행되지 않아야 한다.


✅ 41. AI Tool Timeout 테스트

AI가 Tool을 실행했는데:

npm test

가 무한 대기한다고 하자.

Tool에도 Timeout이 필요하다.

예:

test.run

maxDuration
5분

초과:

TOOL_TIMEOUT

으로 중단한다.


✅ 42. Tool Process Cleanup

Timeout 처리했다고 부모 프로세스만 종료하면:

npm
↓
jest
↓
child process

등이 남을 수 있다.

그러면 다음 Run에 영향을 줄 수 있다.

따라서 Tool Runner는 종료 시 Child Process까지 정리되는지 확인해야 한다.


✅ 43. AI 실패 테스트에서 중요한 것

부분 수정된 파일이 남는가?

Checkpoint가 정확한가?

Retry가 같은 수정 작업을 중복 적용하는가?

Test Process가 남는가?

새 Run에서 이전 Run Artifact를 잘못 사용하는가?

등을 검증한다.


✅ 44. Failure Injection Mode를 만들어도 된다

코드에 개발/테스트 전용 실패 주입 기능을 둘 수 있다.

예:

if (
  config.failureInjection
    .notificationTimeout
) {
  await sleep(20_000);
}

단 반드시:

Production에서 기본 OFF

여야 한다.


✅ 45. 더 안전한 방법은 Mock Provider다

예:

MockNotificationProvider

를 만든다.

설정에 따라:

SUCCESS

TIMEOUT

HTTP_500

RATE_LIMIT

UNKNOWN

등을 반환하게 한다.


✅ 46. Mock Provider 예시

type MockScenario =
  | 'SUCCESS'
  | 'TIMEOUT'
  | 'SERVER_ERROR'
  | 'RATE_LIMIT'
  | 'CONNECTION_RESET';

테스트에서 원하는 상황을 정확하게 재현할 수 있다.


✅ 47. 외부 API마다 Adapter를 두면 테스트하기 편하다

예:

NotificationService
       ↓
NotificationProvider
       ↓
KakaoProvider

테스트에서는:

NotificationService
       ↓
MockProvider

를 넣는다.

이게 Repository/Adapter 구조를 사용하는 실질적인 장점 중 하나다.


✅ 48. Fault Scenario Registry

장애 테스트 시나리오를 목록으로 관리할 수도 있다.

예:

F001
Notification Provider Timeout

F002
Notification HTTP 500

F003
Worker Crash After External Success

F004
Duplicate Queue Delivery

F005
Duplicate Webhook

F006
Out-of-order Webhook

F007
DB Connection Failure

F008
S3 Upload Failure

✅ 49. Scenario별 기대 결과도 적는다

예:

F003

Scenario
외부 알림톡 성공 후 Worker Crash

Expected

Job:
PROCESSING → UNKNOWN → SUCCESS

External Send:
1회

Reconciliation:
수행

Duplicate Send:
0건

Audit:
Recovery 기록

이렇게 하면 테스트 기준이 명확하다.


✅ 50. Failure Matrix

표로 관리해도 좋다.

장애기대 방어최종 상태
Provider TimeoutTimeout + RetrySUCCESS / UNKNOWN
Provider 장기 장애Circuit BreakerRETRY_PENDING
Worker CrashLease + Reconcile복구
중복 QueueIdempotency1회 실행
중복 WebhookUnique Event1회 처리
S3 실패Step Resume재업로드
AI CrashCheckpointResume

이 표만 있어도 전체 자동화 안정성을 보기 쉽다.


✅ 51. 테스트 성공 기준은 ‘에러가 발생했다’가 아니다

예:

Provider Timeout 발생

자체는 테스트 성공이 아니다.

실제 성공 기준은:

주문 기능 영향 없음

Job 손실 없음

중복 발송 없음

Queue 폭주 없음

자동 복구 가능

로그 추적 가능

이다.


✅ 52. Blast Radius

장애가 어디까지 영향을 미치는지를 Blast Radius라고 표현한다.

예:

Notification Provider 장애

좋은 구조

Notification만 영향

반대로:

Notification 장애

↓
Order API Timeout

↓
고객 신청 실패

↓
관리자 API까지 느려짐

이면 Blast Radius가 너무 크다.


✅ 53. Failure Injection의 중요한 목적

결국:

장애를 발생시키는 것

보다:

Blast Radius가 예상대로 제한되는지 확인

하는 것이 중요하다.


✅ 54. Bulkhead 테스트

0924의 Bulkhead 구조도 실제로 검증해야 한다.

예:

Export Worker
100개 작업 폭주

시:

Notification Worker

가 영향을 받지 않아야 한다.


✅ 55. Resource Isolation 확인

예:

Export

CPU
Connection
Concurrency

를 과도하게 사용했을 때:

Order API

Notification

Webhook

까지 느려지는지 확인한다.


✅ 56. DB Connection Pool도 Bulkhead가 될 수 있다

모든 Worker가 같은 Connection Pool을 무제한 사용하면:

Export 폭주

↓
DB Connection 소진

↓
Order API 실패

할 수 있다.

따라서 중요 기능별 Concurrency 제한이 필요할 수 있다.


✅ 57. Rate Limit 테스트

외부 API가:

초당 10건

제한인데 Worker가:

초당 100건

보내면 429가 발생할 수 있다.

테스트에서는 의도적으로 낮은 Rate Limit을 걸어:

429 처리

Retry-After

Backoff

Queue 증가

가 정상인지 확인한다.


✅ 58. Backpressure

처리 속도보다 Job 생성 속도가 빠르면 Queue가 계속 쌓인다.

생성
100/s

처리
20/s

이라면:

Queue 증가

는 당연하다.

이때 시스템에는 Backpressure 전략이 필요하다.


✅ 59. Backpressure 대응

예:

Concurrency 제한

요청 속도 제한

Batch 처리

우선순위 Queue

일부 비핵심 작업 지연

생산자 속도 제한

등이 있다.


✅ 60. Queue가 무한히 쌓이게 두지 않는다

Queue Pending이:

10

100

1,000

100,000

으로 증가한다면 언젠가 복구 자체가 어려워질 수 있다.

따라서:

Queue Depth

Oldest Job Age

Growth Rate

를 같이 본다.


✅ 61. Queue 장애 테스트

예:

Worker 10분 정지

한다.

그동안 Queue가 쌓인다.

Worker를 복구했을 때:

정상 속도로 backlog 감소

Provider Rate Limit 초과 없음

DB 부하 급증 없음

인지 확인한다.


✅ 62. 복구 순간이 오히려 더 위험할 수 있다

장애 중:

Queue 10,000건

쌓였다.

Provider 복구 후 Worker가 전부 동시에 실행하면:

Recovery Storm

이 발생할 수 있다.


✅ 63. Recovery Storm 방지

Concurrency 제한

Rate Limiting

Gradual Ramp-up

Retry Jitter

Batch Size 제한

으로 천천히 복구한다.


✅ 64. 장애 복구 성능도 측정한다

0922의 SLO와 연결해서:

Recovery Throughput

Queue Drain Time

MTTR

를 측정할 수 있다.

예:

Pending 10,000건

30분 이내 정상화

같은 기준이다.


✅ 65. GameDay란?

GameDay는 특정 날짜에 장애 상황을 정해서 실제 대응 훈련을 하는 것이다.

예:

가정

오늘 알림톡 Provider가
30분 동안 장애 발생

그리고 실제로 Staging에서 상황을 재현한다.


✅ 66. GameDay의 목적

테스트 코드만 검증하는 것이 아니다.

Alert가 오는가?

Dashboard에서 찾을 수 있는가?

Runbook이 실제로 도움이 되는가?

복구 절차를 알고 있는가?

정상화 여부를 확인할 수 있는가?

같은 운영 프로세스를 같이 검증한다.


✅ 67. 1인 개발자도 GameDay가 필요한가?

대규모 조직처럼 정기적인 장애 훈련까지 할 필요는 없다.

하지만 중요한 기능이라면:

배포 전

큰 이벤트 전

사전예약 전

신규 자동화 도입 전

한 번씩 해볼 가치가 있다.

특히 휴대폰 사전예약처럼 특정 기간에 트래픽과 문의가 몰리는 서비스라면 더 그렇다.


✅ 68. 투게더몰용 간단한 GameDay

예:

시나리오

아이폰 사전예약 첫날

Notification Provider 10분 장애

확인:

고객 신청은 정상

NotificationJob Queue 누적

중복 발송 없음

Provider 정상화 후 순차 처리

관리자에서 Pending 확인 가능

Critical Alert 발생

Runbook으로 대응 가능

✅ 69. 또 다른 GameDay

시나리오

관리자 Excel 대량 다운로드
+
사전예약 주문 증가

확인:

Export가 DB Connection을 독점하지 않는가?

주문 API Latency가 유지되는가?

Export Worker Concurrency Limit가 작동하는가?

이것은 Bulkhead 검증이다.


✅ 70. 배포 GameDay

배포 후
Health Check 실패

를 가정한다.

확인:

Release 상태 감지

Traffic 전환 중단

Rollback 가능

이전 버전 정상

Incident Timeline 기록

등이다.


✅ 71. AI Automation GameDay

예:

AI가 파일 수정

↓
테스트 성공

↓
Report 생성 중 Agent 종료

복구:

Run Reconciliation

↓
Checkpoint 확인

↓
Report Step부터 Resume

되는지 확인한다.


✅ 72. GameDay 진행 전에 Safety Guard가 필요하다

최소한:

Production 데이터 사용 금지

실제 고객 알림 발송 금지

테스트 Provider 사용

테스트 계정 사용

Kill Switch 준비

실험 종료 조건 정의

가 필요하다.


✅ 73. Abort Condition

실험 중 예상보다 영향이 크면 즉시 중단해야 한다.

예:

Order API Error Rate > 1%

Production 요청 영향

DB CPU > 80%

예상치 못한 외부 발송 발생

하면 즉시 종료한다.


✅ 74. 실험에는 시간 제한을 둔다

예:

Failure Injection Duration

최대 5분

처럼 정한다.

실험 코드가 잘못되어 장애 상태가 계속 유지되는 것을 막는다.


✅ 75. 자동 만료

Feature Flag 방식으로 장애 주입 기능을 만든다면:

enabledUntil

을 둘 수 있다.

예:

현재 시각 < enabledUntil

인 경우에만 동작한다.

시간이 지나면 자동 비활성화한다.


✅ 76. Production 보호

실패 주입 코드를 포함한다면 최소한:

environment === 'production'

에서는 차단하는 것이 안전하다.

또는 별도의 매우 강한 승인 구조가 필요하다.

현재 프로젝트에서는 Production Failure Injection 자체를 하지 않는 편이 낫다.


✅ 77. Chaos 테스트에서 로그도 검증한다

Failure Injection은 기능 테스트뿐 아니라 Observability 테스트이기도 하다.

예:

Provider Timeout

발생 시:

Error Code

Correlation ID

Job ID

Attempt

Circuit State

가 로그에 남는지 확인한다.


✅ 78. Metric 검증

같은 상황에서:

notification_failed_total

retry_total

circuit_open

queue_pending

unknown_jobs

등이 실제로 증가하는지 본다.


✅ 79. Alert 검증

예:

Provider Error Rate 50%

를 만들었는데 아무 Alert도 오지 않는다면:

Monitoring은 있지만
실제 장애 감지는 못 하는 상태

다.


✅ 80. Alert가 너무 많이 오는 것도 실패다

반대로 한 장애에서:

Alert 50개

가 온다면 운영하기 어렵다.

같은 Incident로 묶거나 Severity별로 줄여야 한다.


✅ 81. Runbook 검증

GameDay에서 Runbook을 실제로 따라가본다.

그러면:

명령어가 틀림

메뉴 위치가 달라짐

필요한 로그 검색 방법이 없음

정상화 기준이 애매함

등을 발견할 수 있다.

Runbook은 실제로 써봐야 품질을 알 수 있다.


✅ 82. Postmortem까지 테스트한다

GameDay 종료 후 간단하게 기록한다.

무엇을 테스트했는가?

가설은 맞았는가?

어떤 방어 장치가 작동했는가?

어떤 문제가 발견됐는가?

어떤 Action Item이 생겼는가?

실제 Incident와 똑같은 형식으로 정리해도 좋다.


✅ 83. Chaos Result 예시

Experiment
Notification Provider Timeout

Hypothesis
주문 기능은 영향을 받지 않는다.

Result
PASS

Observed

Order API
정상

Notification Queue
최대 83건

Circuit
OPEN 정상

Recovery
Provider 정상화 후 4분 내 Queue 처리

Issue

UNKNOWN Metric이 Dashboard에 없음

Action

UNKNOWN Gauge 추가

✅ 84. 실패한 Chaos Test가 더 가치 있을 수 있다

실험에서:

설계대로 동작하지 않음

이 나와도 테스트 실패가 아니다.

오히려 실제 고객 장애 전에 문제를 발견한 것이다.

예:

Worker Crash 후
Job이 계속 PROCESSING

을 발견했다면:

Stale Recovery 구현 누락

을 실제 장애 전에 알게 된 것이다.


✅ 85. Resilience Regression Test

발견한 장애 시나리오는 테스트에 추가한다.

예:

2026-09-25

Incident:
Worker Crash After Provider Success

이후:

integration test

로 고정한다.

다시는 같은 문제가 코드 변경으로 재발하지 않도록 한다.


✅ 86. Incident → Test 흐름

좋은 운영 문화는:

Incident
↓
Postmortem
↓
Action Item
↓
Regression Test

로 이어진다.

장애를 문서화만 하고 끝내지 않는다.


✅ 87. 실제 장애를 Scenario Library에 추가

예:

F009

Worker Crash after
Provider Response
before DB Update

처럼 실제 발생한 Incident를 Failure Scenario로 추가한다.

시간이 지날수록 시스템 고유의 장애 테스트 자산이 쌓인다.


✅ 88. Chaos 테스트 자동화 수준

처음에는:

Manual

로 해도 충분하다.

예:

Mock Provider TIMEOUT 설정
↓
테스트 주문 생성
↓
Dashboard 확인

이후 반복 가치가 큰 시나리오는 CI에 넣는다.


✅ 89. CI에 넣기 좋은 테스트

예:

Duplicate Queue

Duplicate Webhook

Transaction Rollback

Idempotency

Retry Limit

Resume

State Transition

등이다.

빠르고 재현성이 높기 때문이다.


✅ 90. CI에 넣기 어려운 테스트

예:

실제 Network Delay

Process Crash

DB Failover

대규모 Load

Recovery Storm

등은 별도 Integration 환경에서 실행하는 편이 낫다.


✅ 91. Test Pyramid 관점

많이

Unit Test
Integration Test

↓

적당히

Failure Scenario Test

↓

적게

GameDay
Chaos Experiment

정도로 생각하면 된다.

모든 테스트를 Chaos Test로 만들 필요는 없다.


✅ 92. Chaos Engineering을 과하게 도입하지 않는다

현재 프로젝트에서:

Chaos Monkey

Service Mesh Fault Injection

Kubernetes Pod Random Kill

Multi-region Failover GameDay

까지 할 필요는 없다.

오히려 유지보수 부담만 늘어난다.


✅ 93. 지금 필요한 것은 현실적인 실패 시나리오다

투게더몰이라면:

Provider 장애

Worker 중단

DB 연결 문제

Webhook 중복

Export 실패

배포 실패

AI 자동화 중단

정도가 핵심이다.


✅ 94. Failure Injection API를 만들 때 주의

만약 내부 테스트 API를 만든다면:

POST /internal/test/failure

같은 Endpoint를 외부에서 접근할 수 있게 만들면 위험하다.

반드시:

개발 환경 전용

Authentication

Allowlist

Production Disable

같은 보호가 필요하다.


✅ 95. 더 나은 방식

가능하면 테스트 설정을:

Environment

Mock Adapter

Test Fixture

등으로 제어한다.

운영 API에 Failure Injection 기능을 섞지 않는다.


✅ 96. Fault Injection Context

테스트 로그에는 실제 장애와 구분할 수 있도록:

testScenarioId

를 넣어도 좋다.

예:

{
  "event": "notification.send.failed",
  "testScenarioId": "F001",
  "errorCode": "PROVIDER_TIMEOUT"
}

✅ 97. AI에게 장애 시나리오 생성을 맡길 수도 있다

AI에게 프로젝트 구조를 분석시켜:

이 시스템에서 발생 가능한
failure point를 찾아줘.

라고 할 수 있다.

예:

DB

External API

Queue

File System

S3

Git

LLM

Deployment

별로 시나리오를 찾아낼 수 있다.


✅ 98. 단 AI가 자동으로 장애를 실행하게 하지는 않는다

초기에는:

AI
→ 시나리오 제안

Human
→ 선택

Test Harness
→ 실행

구조가 안전하다.


✅ 99. AI Failure Scenario Review

예:

Scenario
Webhook duplicate delivery

Expected Protection
providerEventId UNIQUE

Risk
LOW

Environment
Integration Test

Production Impact
NONE

같이 위험도를 먼저 평가하게 한다.


✅ 100. AI로 결과 분석하기

실험 후:

Logs

Metrics

Timeline

Expected Result

를 구조화해서 제공하면 AI가:

가설 통과 여부

비정상 지표

누락된 방어 장치

추천 Action Item

을 정리할 수 있다.


✅ 101. Local LLM 보고서와 연결

현재 Work Report 자동화에도:

### Reliability Test

Scenario
Notification Timeout

Result
PASS

Found Issue
Queue Dashboard 개선 필요

Action
UNKNOWN Metric 추가

같은 내용을 넣을 수 있다.

실제로 시스템 안정성을 개선한 기록으로 남는다.


✅ 102. 포트폴리오 측면에서도 가치가 있다

단순:

Retry 기능 구현

보다:

외부 API Timeout, Worker Crash,
중복 Queue/Webhook 시나리오를 테스트하고

Idempotency, Reconciliation,
Circuit Breaker가 정상적으로
동작하는지 검증

했다고 설명하면 실제 운영 경험을 보여주기 좋다.

단 실제 적용한 범위만 이야기해야 한다.


✅ 103. Failure Scenario 테스트 구조 예시

test/
└─ reliability/
   ├─ notification/
   │  ├─ provider-timeout.spec.ts
   │  ├─ provider-500.spec.ts
   │  └─ duplicate-job.spec.ts
   │
   ├─ webhook/
   │  ├─ duplicate.spec.ts
   │  └─ out-of-order.spec.ts
   │
   ├─ export/
   │  └─ s3-failure.spec.ts
   │
   └─ ai/
      └─ resume-after-crash.spec.ts

✅ 104. Scenario 인터페이스 예시

interface FailureScenario {
  id: string;

  name: string;

  hypothesis: string;

  fault: string;

  expectedResult: string[];

  maxDurationMs: number;

  environment:
    | 'test'
    | 'staging';
}

✅ 105. 실험 결과 구조

interface FailureExperimentResult {
  scenarioId: string;

  startedAt: Date;

  completedAt: Date;

  status:
    | 'PASS'
    | 'FAIL'
    | 'ABORTED';

  observations: string[];

  actionItems: string[];
}

✅ 106. 가장 먼저 자동화할 테스트

현재 프로젝트에서는 이 다섯 개가 가장 ROI가 높다.

1.
동일 NotificationJob 동시 실행

2.
중복 Webhook

3.
Provider Timeout + Retry

4.
Worker Crash + Stale Recovery

5.
Transaction 중간 실패

이 다섯 가지는 운영 장애와 직접 연결될 가능성이 높다.


✅ 107. 그다음 테스트

6.
Circuit Breaker OPEN / HALF_OPEN

7.
Queue Backlog Recovery

8.
S3 Upload Resume

9.
AI Run Resume

10.
Deployment Health Check Failure

순으로 늘린다.


✅ 108. Codex 구현 프롬프트

현재 NestJS + Prisma 기반 프로젝트에
Reliability / Failure Scenario 테스트 구조를 추가해줘.

목표는 Production에 장애를 발생시키는
Chaos Engineering 플랫폼을 만드는 것이 아니라,

현재 구현된
Retry, Idempotency, Reconciliation,
Lease, Heartbeat, Circuit Breaker 등의
장애 대응 로직이 실제 실패 상황에서도
정상적으로 동작하는지 검증하는 것이다.

Production 코드에 위험한
Failure Injection Endpoint를 만들지 않는다.

우선 Test / Integration 환경에서만 동작하게 한다.

1. reliability 전용 테스트 폴더를 만든다.

예:

test/reliability/

notification/
webhook/
automation/
export/

2. Notification Provider 인터페이스에
테스트에서 사용할 Mock Provider를 구현한다.

Mock Provider는 다음 Scenario를 지원한다.

- SUCCESS
- TIMEOUT
- HTTP_500
- RATE_LIMIT
- CONNECTION_RESET

3. Provider TIMEOUT 테스트를 작성한다.

확인:
- Timeout 적용
- Retry 등록
- Attempt Count 증가
- Error Code 기록
- Correlation ID 유지

4. Provider HTTP 500 → 500 → SUCCESS
시나리오를 만든다.

최종 결과가 SUCCESS이고
중복 Side Effect가 발생하지 않는지 확인한다.

5. 동일 NotificationJob을
동시에 여러 Worker가 실행하는 상황을 테스트한다.

Atomic Claim 또는 Idempotency에 의해
실제 Side Effect가 1회만 실행되어야 한다.

6. 중복 Webhook 테스트를 만든다.

동일 providerEventId를
3회 전달해도 실제 Business 처리 횟수는
1회여야 한다.

7. Webhook Out-of-order 테스트를 작성한다.

DELIVERED 이후 SENT 이벤트가 들어와도
상태가 SENT로 후퇴하지 않도록 한다.

8. Worker Crash를 모사할 수 있는
Integration Test를 만든다.

Job을 PROCESSING 상태로 둔 뒤
heartbeat / lease를 만료시키고

Reconciliation을 실행했을 때
안전하게 복구되는지 확인한다.

9. 외부 Provider에서는 성공했지만
DB Update 전에 Worker가 종료되는
Scenario를 테스트한다.

Reconciliation 후 SUCCESS로 복구되고
외부 요청이 다시 실행되지 않아야 한다.

10. Transaction 중간 실패 테스트를 작성한다.

예:
Order Update 성공 직후
Audit 저장 과정에서 Exception 발생

Transaction 대상이라면
전체 변경이 Rollback되어야 한다.

11. Circuit Breaker가 구현되어 있다면 다음을 테스트한다.

- CLOSED → OPEN
- OPEN 상태에서 외부 호출 차단
- OPEN → HALF_OPEN
- 성공 후 CLOSED
- 실패 후 다시 OPEN

12. 모든 Failure Scenario에서
다음 값을 검증할 수 있게 한다.

- 최종 상태
- Retry 횟수
- Side Effect 실행 횟수
- Audit Log
- Error Code

13. 실제 고객 정보나
Production Secret을 사용하지 않는다.

14. Production 환경에서는
Mock Failure Provider를 사용할 수 없도록
보호한다.

15. reliability 테스트는
기존 Unit Test와 분리해서
별도 명령으로 실행할 수 있게 한다.

예:

pnpm test:reliability

16. 가장 먼저 다음 5개 Scenario를 완성한다.

- Duplicate Notification Job
- Duplicate Webhook
- Provider Timeout
- Worker Stale Recovery
- Transaction Rollback

기존 프로젝트 구조와 구현 상태를 먼저 분석하고,
이미 존재하는 기능은 중복 구현하지 말고
현재 구조에 맞게 테스트를 추가해줘.

✅ 109. GameDay 문서 템플릿

# Reliability GameDay

Date:

Scenario:

Environment:


## 1. Hypothesis

장애가 발생해도 무엇이 유지되어야 하는가?


## 2. Failure

어떤 장애를 발생시킬 것인가?


## 3. Expected Protection

- Timeout
- Retry
- Circuit Breaker
- Idempotency
- Reconciliation
등


## 4. Steady State

실험 전 정상 지표


## 5. Abort Condition

실험을 즉시 중단할 조건


## 6. Execution


## 7. Observations


## 8. Result

PASS / FAIL / ABORTED


## 9. Found Issues


## 10. Action Items

✅ 110. 실무 체크리스트

Failure Scenario

  • 실제 발생 가능한 장애인가?
  • 기대 결과가 정의되어 있는가?
  • 종료 조건이 있는가?
  • Production 고객에게 영향을 주지 않는가?

Retry

  • Timeout 이후 Retry가 정상인가?
  • Backoff가 적용되는가?
  • Jitter가 있는가?
  • Retry Storm이 발생하지 않는가?
  • Retry Limit이 있는가?

Idempotency

  • 동일 Job을 두 번 실행해도 Side Effect가 1회인가?
  • 중복 Webhook이 안전한가?
  • Worker Crash 후 재실행이 안전한가?

Reconciliation

  • UNKNOWN을 복구할 수 있는가?
  • PROCESSING Job이 영원히 남지 않는가?
  • Provider 실제 상태를 확인할 수 있는가?
  • 복구 Action도 Idempotent한가?

Circuit Breaker

  • OPEN 조건이 동작하는가?
  • OPEN에서 실제 요청이 차단되는가?
  • HALF_OPEN이 동작하는가?
  • Provider 정상화 후 복구되는가?

Queue

  • Queue 폭주를 감지할 수 있는가?
  • Oldest Job Age를 확인하는가?
  • Recovery Storm을 방지하는가?
  • Concurrency Limit가 있는가?

Observability

  • 실패가 Log에 남는가?
  • Error Code가 정확한가?
  • Correlation ID가 유지되는가?
  • Metric이 증가하는가?
  • Alert가 정상 발생하는가?

AI Automation

  • Run 중단 후 Resume 가능한가?
  • Tool Timeout이 있는가?
  • Child Process가 정리되는가?
  • 중복 파일 수정이 발생하지 않는가?
  • Checkpoint가 실제 상태와 일치하는가?

📌 요약

지금까지:

Retry

Idempotency

Reconciliation

Circuit Breaker

Bulkhead

Timeout

같은 장애 대응 구조를 설계했다.

하지만 중요한 것은:

코드가 존재한다

가 아니라

실제 장애 상황에서도
제대로 동작한다

는 것이다.

그래서 0925에서는 Failure Injection과 Chaos Testing을 다뤘다.

핵심 흐름은:

정상 상태 정의
↓
Hypothesis 작성
↓
Failure Injection
↓
관찰
↓
복구 확인
↓
Action Item

이다.

현재 프로젝트에서 가장 먼저 검증할 가치가 높은 것은:

Provider Timeout

Worker Crash

Duplicate Queue

Duplicate Webhook

Transaction Failure

다.

특히 다음 상황은 반드시 한 번 테스트할 가치가 있다.

외부 Provider에서는 성공

↓

Worker가 DB 기록 전에 종료

↓

우리 DB는 PROCESSING

↓

Reconciliation

↓

외부 상태 확인

↓

SUCCESS 복구

↓

중복 발송 없음

이 하나의 시나리오 안에:

Idempotency

Lease

Heartbeat

Reconciliation

Observability

가 모두 들어 있기 때문이다.

또한 장애 실험의 목적은:

시스템을 얼마나 심하게 망가뜨릴 수 있는가

가 아니다.

핵심은:

한 부분의 장애가
다른 핵심 기능까지 번지지 않는가?

즉 Blast Radius를 확인하는 것이다.

알림톡 Provider가 죽어도:

고객 주문은 정상 접수

되어야 하고,

Export가 폭주해도:

주문 API와 Notification Worker는 정상

이어야 한다.

그리고 장애가 끝난 뒤에도 중요하다.

Queue 10,000건

↓

Provider 정상화

↓

모든 Worker 동시 실행

처럼 복구 과정에서 다시 장애를 만드는 Recovery Storm도 방지해야 한다.

따라서 좋은 복구는:

Rate Limit
+
Concurrency Limit
+
Gradual Recovery

를 가진다.

현재 규모에서는 거대한 Chaos Engineering 플랫폼까지 만들 필요는 없다.

먼저:

Mock Provider

Reliability Integration Test

5~10개의 핵심 Failure Scenario

가끔 Staging GameDay

정도면 충분하다.

전체 운영 자동화 흐름은 이제:

Observe
↓
Measure
↓
Detect
↓
Resist
↓
Fail Safely
↓
Reconcile
↓
Recover
↓
Verify
↓
Learn

로 연결된다.

결국 장애 대응 시스템의 완성도는

“정상일 때 잘 돌아가는가”가 아니라, 일부가 망가졌을 때 어디까지 버티고 얼마나 안전하게 원래 상태로 돌아오는가

에서 드러난다.

0개의 댓글