통합과 배포 · 재시도가 만드는 중복 · 이론
타임아웃 뒤에 재시도하면 무슨 일이 생기는가
한 줄 요약
타임아웃은 요청이 실패했다는 뜻이 아니라 결과를 모른다는 뜻입니다.
이 차이를 놓치면 재시도가 중복 결제와 중복 발송을 만듭니다.
왜 이게 필요했나
결제 연동에서 가장 흔한 사고는 이렇습니다.
1. 우리가 결제 요청을 보냄
2. 3초 타임아웃 — 응답이 안 옴
3. "실패했나 보다" 하고 재시도
4. 결제사에서는 두 번 승인됨
3번의 판단이 틀렸습니다. 응답을 못 받은 것과 처리가 안 된 것은 다릅니다.
요청은 도착했고, 처리도 됐고, 응답만 유실됐을 수 있습니다.
이걸 분산 시스템에서는 불확실한 결과(indeterminate) 라고 부릅니다.
성공도 실패도 아닌 세 번째 상태이고, 이 상태를 다루는 방법이 따로 있습니다.
재시도해도 되는 것과 안 되는 것
핵심 질문은 "이 연산이 멱등한가" 입니다. 같은 요청을 두 번 보내도
결과가 한 번 보낸 것과 같으면 멱등합니다.
| 연산 | 멱등? | 재시도 |
| --- | --- | --- |
| GET /orders/123 | 예 | 자유롭게 |
| PUT /orders/123 {status:"PAID"} | 예 (같은 값을 씀) | 안전 |
| DELETE /orders/123 | 예 (두 번째는 없음) | 안전 |
| POST /orders {…} | 아니오 | 위험 — 멱등키 필요 |
| POST /payments {amount:1000} | 아니오 | 위험 |
| 잔액 차감 balance -= 1000 | 아니오 | 위험 |
HTTP 메서드의 멱등성은 규약이지 보장이 아닙니다. 서버가 PUT 에서
카운터를 증가시키면 그 PUT 은 멱등하지 않습니다. 문서가 아니라
동작을 확인해야 합니다.
멱등키
멱등하지 않은 연산을 안전하게 재시도하려면, 클라이언트가 요청마다
고유한 키를 만들어 보냅니다.
POST /paymentsIdempotency-Key: 3f9a1c7e-2b44-4c9d-a1f8-0d6e2b7c5a11{"order_id": 123, "amount": 1000}서버는 이 키를 저장해 두고, 같은 키로 다시 오면 **새로 처리하지 않고
저장해 둔 결과를 그대로 돌려줍니다.** 재시도가 몇 번이든 결제는 한 번입니다.
키를 만들 때 주의할 점이 있습니다. 재시도할 때 같은 키를 써야 합니다.
매 시도마다 새 UUID 를 만들면 아무 의미가 없습니다. 키는 "이 비즈니스
행위" 에 대해 하나이지 "이 HTTP 요청" 에 대해 하나가 아닙니다.
재시도 정책
무작정 재시도하면 장애를 키웁니다. 하류가 느려서 타임아웃이 났는데
전부가 즉시 재시도하면 부하가 배로 늘어 완전히 무너집니다.
- 지수 백오프 — 1초, 2초, 4초, 8초. 간격을 늘려 숨 쉴 틈을 줍니다.
- 지터 — 여기에 무작위를 섞습니다. 안 그러면 모든 클라이언트가
- 상한 — 최대 횟수와 최대 총 시간을 정합니다. 무한 재시도는 금지.
- 회로 차단기 — 연속 실패가 임계를 넘으면 한동안 아예 시도하지 않습니다.
똑같은 시각에 동시에 재시도합니다(thundering herd).
하류가 회복할 시간을 주는 장치입니다.
그리고 어떤 오류를 재시도할지 구분합니다.
| 응답 | 재시도 |
| --- | --- |
| 연결 거부, 타임아웃 | 예 (멱등하거나 멱등키가 있을 때) |
| 429 Too Many Requests | 예 — Retry-After 를 지켜서 |
| 500, 502, 503, 504 | 대개 예 |
| 400, 422 (요청이 잘못됨) | 아니오 — 몇 번을 보내도 같다 |
| 401, 403 | 아니오 — 자격증명을 고쳐야 한다 |
조사할 때의 신호
중복이 의심되면 이렇게 찾습니다.
SELECT order_id, count(*) FROM paymentsGROUP BY 1 HAVING count(*) > 1;그리고 그 중복 건들의 created_at 간격을 봅니다. 간격이 재시도 백오프와
비슷하다면(1초, 2초, 4초) 원인은 거의 확정입니다.
현장에서 만나는 모습
- 월말 정산에서 금액이 안 맞는다 → 타임아웃 재시도로 생긴 중복.
- 알림이 두세 번 간다 → 발송 API 에 멱등키가 없다.
- 하류가 느려지자 전체가 무너졌다 → 백오프와 회로 차단기가 없다.
이어지는 실습에서 할 것
처리는 하고 응답만 잃어버리는 결제 API 를 상대로 세 번 결제해 봅니다.
멱등키 없이 주문 4건 → 기록 7건비즈니스 행위당 키 하나 주문 4건 → 기록 4건시도마다 새 키 주문 4건 → 기록 7건 ← 키를 붙였는데도 소용없다세 번째가 이 실습의 핵심입니다. 키를 붙였다는 사실만으로는 아무것도
보장되지 않습니다.