Contents
see Listfetch는 성공 응답만 반환한다고 생각하면 운영에서 문제가 생긴다
브라우저와 Node.js에서 fetch는 HTTP 요청을 간단하게 만들지만, 기본 호출만으로는 서비스 장애를 다루기 어렵습니다. 네트워크가 끊기거나 서버 응답이 지연될 수 있고, 429 또는 503처럼 잠시 뒤 다시 시도하면 해결되는 상태 코드도 있습니다. 특히 fetch는 404, 500 같은 HTTP 오류에서 Promise를 reject하지 않습니다. 연결 자체가 실패했을 때만 예외가 발생하므로, 응답의 ok와 status를 직접 확인해야 합니다.
실무에서는 요청마다 무한정 기다리지 않고, 재시도 가능한 실패만 제한 횟수로 다시 호출하며, 호출자가 취소할 수 있는 구조를 둡니다. 이 세 가지를 분리해 두면 화면의 중복 클릭, 느린 외부 API, 배포 직후의 일시적 오류를 같은 방식으로 제어할 수 있습니다.
먼저 오류를 구분한다
- 네트워크 오류: DNS, 연결, TLS 오류 등으로 fetch 자체가 reject된 경우입니다.
- 시간 초과: 응답이 너무 늦어 애플리케이션이 요청을 중단한 경우입니다.
- 클라이언트 오류: 대부분의 400대 응답은 요청 형식이나 권한 문제이므로 자동 재시도가 도움이 되지 않습니다.
- 일시적 서버 오류: 408, 429, 500, 502, 503, 504는 조건을 확인한 뒤 재시도 후보가 될 수 있습니다.
POST 요청은 더 신중해야 합니다. 결제, 주문 생성, 메일 발송처럼 같은 요청을 두 번 실행하면 결과가 달라지는 작업은 무조건 재시도하면 안 됩니다. 서버가 idempotency key를 지원할 때만 같은 키를 함께 보내고 재시도하거나, 조회성 GET 요청부터 이 패턴을 적용하는 것이 안전합니다.
AbortController로 timeout과 사용자 취소를 함께 처리하기
아래 예제는 호출자가 전달한 signal과 내부 timeout signal을 합쳐 요청합니다. timeout을 네트워크 계층에 명확히 전달하고, 성공 여부를 HTTP 상태 코드로 판정합니다. Node.js 20 이상과 최신 브라우저에서는 AbortSignal.timeout과 AbortSignal.any를 사용할 수 있습니다.
const retryableStatus = new Set([408, 429, 500, 502, 503, 504]);
async function requestJson(url, options = {}) {
const { timeoutMs = 8000, retries = 2, signal, ...fetchOptions } = options;
for (let attempt = 0; attempt <= retries; attempt += 1) {
const timeoutSignal = AbortSignal.timeout(timeoutMs);
const combinedSignal = signal
? AbortSignal.any([signal, timeoutSignal])
: timeoutSignal;
try {
const response = await fetch(url, {
...fetchOptions,
signal: combinedSignal,
headers: { accept: 'application/json', ...fetchOptions.headers }
});
if (response.ok) return response.json();
if (!retryableStatus.has(response.status) || attempt === retries) {
throw new Error('HTTP ' + response.status);
}
} catch (error) {
if (signal?.aborted) throw error;
if (attempt === retries) throw error;
}
const baseDelay = 300 * 2 ** attempt;
const jitter = Math.floor(Math.random() * 150);
await new Promise(resolve => setTimeout(resolve, baseDelay + jitter));
}
}이 함수의 핵심은 모든 오류를 재시도하지 않는다는 점입니다. 401, 403, 404, 422처럼 수정 없이 반복해도 성공할 가능성이 낮은 응답은 즉시 호출자에게 전달해야 합니다. 또한 timeout이 발생했다는 사실만으로 서버가 요청을 처리하지 않았다고 단정할 수 없습니다. 쓰기 작업에는 서버 측 중복 방지 정책이 필요합니다.
429 응답은 Retry-After를 우선한다
외부 API가 429를 반환할 때는 자체 지수 백오프보다 Retry-After 헤더를 우선하는 편이 좋습니다. 서버가 초 단위 값을 보낼 수도 있고, 날짜 형식을 보낼 수도 있으므로 파싱 정책을 한 곳에 둡니다. 재시도 횟수와 대기 시간을 로그나 측정 지표로 남기면 특정 공급자 장애와 호출량 급증을 구분하는 데 도움이 됩니다.
function retryAfterMs(response) {
const value = response.headers.get('retry-after');
if (!value) return null;
const seconds = Number(value);
if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);
const dateMs = Date.parse(value);
return Number.isNaN(dateMs) ? null : Math.max(0, dateMs - Date.now());
}대량 요청에는 동시성 제한도 필요합니다. Promise.all로 수백 건을 한 번에 보내면 브라우저 연결 수나 API rate limit에 즉시 닿을 수 있습니다. 작업 큐를 두고 동시에 실행하는 수를 제한하거나, 목록을 페이지 단위로 처리하십시오. 화면에서는 이미 진행 중인 요청의 controller를 보관해 이전 검색 요청을 취소하면 오래된 응답이 최신 화면을 덮어쓰는 문제도 줄일 수 있습니다.
운영 적용 체크리스트
- fetch 뒤에 response.ok 또는 허용 상태 코드를 반드시 검사한다.
- timeout은 AbortController 계열 signal로 요청 자체를 중단한다.
- 재시도 대상 상태 코드, 횟수, 최대 대기 시간을 API 성격에 맞게 제한한다.
- POST·결제·생성 요청은 idempotency key 또는 서버 중복 방지 없이 자동 재시도하지 않는다.
- 429에서는 Retry-After를 존중하고, 대량 작업에는 동시성 제한을 둔다.
- 요청 URL의 민감한 쿼리값은 제외한 채 상태 코드, 시도 횟수, 지연 시간을 기록한다.
이 기준을 공통 HTTP 유틸리티로 만들면 각 화면과 배치 작업이 서로 다른 오류 처리 규칙을 갖는 일을 줄일 수 있습니다. 다만 공통화하더라도 인증 갱신, 파일 업로드, 결제처럼 부작용과 복구 방법이 다른 요청은 별도 정책으로 분리해 관리해야 합니다.
javascript
| No | 작성일 | Title |
|---|---|---|
| 357 | 2016. 04. 16. | [ datatable ] jquery datatable and editor exemple 사용법 예 |
| 312 | 2015. 07. 02. | [ jquery ] 선택된 라디오 박스 값 이름으로 가져오기 |
| 305 | 2015. 06. 16. | [ dynatree ] dynatree 함수들 |
| 286 | 2015. 05. 30. | [ javascript ] 운영체제 비트 확인하기 |
| 275 | 2015. 05. 04. | [ jquery ] dynatree option 옵션들 |
| 269 | 2015. 04. 08. | [ javascript ] jquery 에서 ajax 로 배열 넘기기 |
| 268 | 2015. 04. 08. | [ javascript ] jquery map() 을 이용하여 여러 폼값을 배열로 만들기. |
| 267 | 2015. 04. 08. | [ javascript ] jquery 로 해당 객체들중에 대상 선택하여 인덱스값 가져오기 |
| 167 | 2015. 03. 05. | [ javascript ] radio 버튼 자동체크하기 |
| 112 | 2014. 12. 31. | [ javascript ] 천단위 콤마찍기 |