재시도 로직을 설계할 때 idempotency와 backoff가 왜 중요한가요?
답변 포인트
중복 실행 안전성과 재시도 간격를 기준으로 정의, 장점, 한계, 예시를 함께 설명해보세요.
정답 및 해설
빠른 요약
재시도는 일시 장애에 유용하지만 중복 실행 위험이 있습니다. idempotency key로 중복 처리를 막고 backoff와 jitter로 장애 상황의 요청 폭주를 줄입니다.
재시도 로직은 일시적인 네트워크 오류나 5xx 장애를 견디게 해주지만, 잘못 설계하면 중복 결제, 트래픽 폭증, 장애 증폭을 만들 수 있습니다. 그래서 같은 요청을 여러 번 보내도 안전한 idempotency와 재시도 간격을 조절하는 backoff가 핵심입니다.
핵심 개념
- Idempotency는 동일 요청을 여러 번 처리해도 결과가 한 번 처리한 것과 같도록 만드는 성질입니다.
- Exponential backoff는 재시도 간격을 점점 늘려 장애 중인 서버에 부담을 줄입니다.
- Jitter는 여러 클라이언트가 동시에 재시도하는 thundering herd를 완화합니다.
동작 방식 또는 판단 기준
이 주제를 이해할 때는 다음 순서로 보면 실무 적용이 쉬워집니다.
- 무엇을 해결하려는가: 성능, 표현력, 안정성, 접근성 중 어떤 문제를 줄이려는지 확인합니다.
- 전제 조건은 무엇인가: 정렬 여부, 브라우저 지원, 네트워크 특성, 동시성 조건처럼 성립해야 하는 조건을 점검합니다.
- 비용은 어디서 발생하는가: 시간 복잡도, 메모리, 캐시, 재시도, 렌더링 비용처럼 병목 지점을 나눠 봅니다.
- 실패 시 어떤 문제가 생기는가: 잘못 적용했을 때의 버그나 운영 리스크를 함께 고려합니다.
실제 예시
async function retry<T>(fn: () => Promise<T>, max = 4): Promise<T> {
for (let attempt = 0; attempt < max; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === max - 1) throw error;
const base = 100 * 2 ** attempt;
const jitter = Math.random() * 100;
await new Promise(r => setTimeout(r, base + jitter));
}
}
throw new Error('unreachable');
}
await fetch('/payments', {
method: 'POST',
headers: { 'Idempotency-Key': crypto.randomUUID() },
body: JSON.stringify({ orderId, amount }),
});실무에서 주의할 점
- 모든 오류를 재시도하면 안 됩니다. 400/401 같은 클라이언트 오류는 대부분 재시도해도 실패합니다.
- POST 요청이 idempotent하지 않으면 네트워크 timeout 후 재시도가 중복 작업을 만들 수 있습니다.
- 무제한 재시도는 장애를 복구하는 것이 아니라 장애 트래픽을 증폭시킵니다.
실무 적용 가이드
- 재시도 가능한 오류(408, 429, 5xx, 네트워크 timeout)를 명확히 분류합니다.
- 결제/주문/메일 발송은 idempotency key와 중복 처리 방지 테이블을 둡니다.
- 최대 재시도 횟수, 전체 deadline, circuit breaker, observability를 함께 설계합니다.
함께 연결해서 보면 좋은 키워드
Retry, Idempotency, Backoff, Jitter, Resilience
정리
재시도 로직은 일시적인 네트워크 오류나 5xx 장애를 견디게 해주지만, 잘못 설계하면 중복 결제, 트래픽 폭증, 장애 증폭을 만들 수 있습니다입니다. 다만 개념 자체보다 중요한 것은 적용 조건과 한계를 함께 이해하는 것입니다. 작은 예제에서는 단순해 보여도 실제 서비스에서는 성능, 보안, 유지보수성, 접근성 요구사항이 함께 얽히므로, 문제의 성격을 먼저 파악한 뒤 적절한 도구로 선택하는 것이 좋습니다.