Retry-After 헤더: 429·503 후 얼마나 기다려야 하나요?

Retry-After는 재시도 전에 얼마나 기다릴지 클라이언트에 알려 주는 헤더로, 초 단위 숫자나 HTTP 날짜이며 429나 503과 함께 옵니다. 읽는 법과 대응 방법을 정리했습니다.

읽는 시간 5분Sume
전체 글

Retry-After는 클라이언트가 다음 요청 전에 얼마나 기다려야 하는지 알려 주는 HTTP 응답 헤더입니다. 값은 Retry-After: 120처럼 초 단위 숫자이거나 HTTP 날짜입니다. 서버는 429 Too Many Requests와 503 Service Unavailable에 이 헤더를 함께 보낼 수 있습니다. 재시도 전에 최소한 그만큼 기다리고, 재시도마저 실패하면 더 길게 백오프하세요.

정의는 RFC 9110과 RFC 6585에서 인용했고, 2026-09-28에 확인했습니다. Sume 예시는 인증과 Run 웹훅 (영문) 문서, 그리고 따로 표시한 곳은 현재 API 코드에서 가져왔습니다.

Retry-After는 초 단위인가요, 밀리초 단위인가요?

초 단위입니다. RFC 9110은 이 값을 HTTP-date 또는 delay-seconds로 정의하며, delay-seconds는 시간을 초로 나타내는 음이 아닌 십진 정수입니다. 밀리초 형식은 없습니다. RFC가 드는 예시 두 개는 Retry-After: Fri, 31 Dec 1999 23:59:59 GMT와 Retry-After: 120이며, 뒤의 것은 이 분 동안 기다리라는 뜻입니다.

두 형식을 모두 처리하세요. 아래 파서는 현재 코드에 있는 Sume 웹훅 전송기의 파서와 같은 규칙을 따르며, 과거 날짜는 기다리지 않는다는 뜻으로 처리합니다.

// Wait in ms from a Retry-After value; null if absent or invalid.
function retryAfterMs(header: string | null, now = Date.now()): number | null {
  const value = header?.trim();
  if (!value) return null;
  if (/^-?\d+$/.test(value)) {
    const seconds = Number(value); // delay-seconds
    return Number.isSafeInteger(seconds) && seconds >= 0 ? seconds * 1000 : null;
  }
  const date = Date.parse(value); // HTTP-date
  return Number.isNaN(date) ? null : Math.max(0, date - now);
}

429 Too Many Requests는 얼마나 지속되나요?

서버의 요청 한도가 다시 요청을 통과시켜 줄 때까지이며, 그때가 언제인지 서버가 알려 주는 수단이 Retry-After입니다. RFC 6585는 429를 주어진 시간 동안 요청이 너무 많은 상태로 정의하고, 응답에 Retry-After를 넣을 수 있다고 말합니다. 요청을 어떻게 셀지는 서버가 정합니다.

Sume API에서 429 rate_limited는 키의 분당 읽기 또는 쓰기 예산을 다 썼다는 뜻이며, retry-after가 기다릴 초를 알려 줍니다. 현재 코드에서 창은 키와 예산별로 고정되어 있고, retry-after는 창이 초기화될 때까지 남은 초(최소 1)이며, 기본 창은 60초이므로 429 rate_limited는 일 분 안에 풀립니다. 요금제별 예산과 요청 속도를 맞추는 데 쓸 수 있는 ratelimit-* 헤더는 Sume API 오류와 요청 한도에서 다룹니다.

모든 429와 503에 Retry-After가 있나요?

아니요, 그렇지 않습니다. RFC 9110은 서버가 503에 이 헤더를 보낼 수 있다고 하고 RFC 6585는 429에 이 헤더를 넣을 수 있다고 하므로, 헤더가 없는 경우도 코드에서 처리하세요. Sume 영상 Job 동시성과 큐에서 설명하듯 Sume의 429 queue_full은 요청 한도가 아니라 용량 한도이므로, 시간이 지나서가 아니라 Job이 끝나야 풀립니다. 현재 코드 기준으로 정리하면 다음과 같습니다.

Sume 인증, 오류와 요청 한도 (영문), Generation admission 문서와 현재 API 코드 기준, 2026-09-28 확인.
응답대기 힌트할 일
429 rate_limitedretry-after 헤더: 키의 창이 초기화될 때까지 남은 초창이 초기화될 때까지 기다린 뒤 다시 보내세요.
429 queue_full본문의 retry_after_seconds: 30, 헤더 없음실행 중인 Job이 끝나기를 기다리거나, 큐에 있는 Job 중 필요 없는 것을 취소하세요. 같은 Idempotency-Key로 다시 보내면 같은 queue_full이 돌아옵니다.
503 deploy_drainingretry-after: 5기다린 뒤 같은 요청을 다시 보내세요.
503 database_busyretry-after: 1기다린 뒤 다시 보내세요.

Sume는 내 웹훅 엔드포인트가 보낸 Retry-After를 따르나요?

Run 웹훅이라면 따릅니다. 엔드포인트가 전달에 429나 503과 Retry-After로 응답하면, Sume는 자체 백오프와 여러분이 보낸 값 중 더 긴 쪽만큼 기다리며, 최대 한 시간으로 제한합니다. 문서는 이 일정을 최대 10회 시도에 걸친 min(max(30s × 2^(attempt−1) with jitter, Retry-After), 1h)로 제시하며, 현재 코드에서 전송기는 초 형식과 날짜 형식을 모두 읽습니다.

Job 웹훅은 다르게 동작합니다. 시도 사이에 고정된 지연을 두고 재시도하며, 기본값은 30초입니다.

Retry-After가 없으면 어떻게 해야 하나요?

지터를 더한 지수 백오프를 쓰고 시도 횟수에 상한을 두세요. 그래야 함께 실패한 클라이언트들이 한꺼번에 재시도하지 않습니다. Sume의 TypeScript SDK는 기본적으로 이렇게 하며, 재시도는 두 번이고, 응답에 retry-after가 있으면 그 값을 따릅니다. 애초에 어떤 오류를 재시도할 만한지, POST는 언제 안전하게 다시 보낼 수 있는지는 재시도 가능한 HTTP 상태 코드에서 다룹니다.

출처

관련 글

개발자 카테고리의 다른 글

개발자 글 전체 보기

작성자 Sume