개요

백오프는 실패한 작업을 즉시 재시도하지 않고 일정 시간 대기 후 다시 시도하는 전략을 말함 연속 실패 시 대기 시간을 점진적으로 늘려 서비스와 네트워크에 가해지는 부하를 낮추는 목적 RPC, 데이터베이스, 외부 API 호출, 트랜잭션 재시도 등에서 일반적으로 사용

왜 쓰는가

  • 과부하 방지 실패 직후 동시 재시도를 막아 서버와 네트워크 폭주를 예방
  • 일시적 장애 흡수 순간적인 지연이나 혼잡이 해소될 시간을 벌어 성공 확률을 높임
  • 비용 절감 불필요한 재시도 횟수와 리소스 낭비 감소

대표 백오프 패턴

  • 고정 백오프 Fixed 매번 동일 대기 시간 사용 예) 1초 → 1초 → 1초
  • 선형 백오프 Linear 시도할 때마다 일정 간격으로 증가 예) 1초 → 2초 → 3초 → 4초
  • 지수 백오프 Exponential 보통 2배로 증가하는 지수 증가 사용, 실무에서 기본값으로 가장 흔함 예) 1초 → 2초 → 4초 → 8초 → 16초
  • 지수 백오프 + 지터 Jitter 지수 증가에 무작위성을 섞어 동시 재시도 동기화를 깨뜨림 예) 1초 → 2.3초 → 4.1초 → 8.6초

실전 포인트

  • 동시성 환경에서는 지터 필수 동일한 주기 백오프만으로는 다수 클라이언트가 같은 타이밍에 몰려 서버를 다시 두들김
  • 상한 설정 최대 대기 시간과 최대 재시도 횟수 캡을 두어 꼬리 길어짐 방지
  • 실패 예산과 타임아웃 연계 전체 호출 타임아웃 내에서 재시도 예산을 배분, 1회 호출과 재시도들이 전체 SLA를 초과하지 않도록 관리
  • 멱등성 보장 재시도 가능한 작업은 멱등성을 만족해야 안전, 아니면 보상 로직 필요
  • 서버 힌트 활용 Retry-After 등 서버가 제시하는 대기 힌트가 있으면 우선 적용
  • 지터 방식 선택 전체 구간 무작위 분포 Full jitter가 단순하고 효과적이라는 보고가 많음

간단 예시

아래는 지수 백오프의 최소 구현 예시이며, 지터와 최대 대기 시간 제한은 상황에 맞게 추가 권장

async function retryWithBackoff(fn) {
  let delay = 1000 // 1초
  for (let i = 0; i < 5; i++) {
    try {
      return await fn()
    } catch (e) {
      await new Promise(r => setTimeout(r, delay))
      delay *= 2 // 지수 증가
    }
  }
}

한 줄 요약

백오프는 실패 직후 재시도를 지연시키고 연속 실패 시 대기 시간을 늘리는 전략이며, 실전에서는 지수 백오프에 지터를 더해 동시 폭주를 피하는 구성이 표준적 선택

참고자료