[EXT-037][실무] fetch 실패 원인 정확히 나누기 > IT 기술 공유

본문 바로가기

사이트 내 전체검색

뒤로가기 IT 기술 공유

[EXT-037][실무] fetch 실패 원인 정확히 나누기

페이지 정보

작성자 기술팀장 작성일 26-09-09 03:36 조회 4 댓글 0

본문

[이번 수업]
fetch() 실패는 원인마다 대응이 달라야 합니다. 서버의 503 응답, 시간 초과, 사용자 취소를 한 오류로 다루면 잘못 재시도하거나 취소를 장애로 기록합니다. 로컬 서버로 네 경우를 만들고 결과를 나눕니다.

[선수지식]
HTTP 상태 코드, async·await, try/catch와 EXT-036의 AbortSignal.any()를 알아야 합니다. 예제는 Node.js 20.3 이상에서 실행합니다.

[학습목표]
1. 정상 응답과 HTTP 오류를 response.ok로 구분합니다.
2. TimeoutError와 AbortError를 나눕니다.
3. 오류에 맞는 후속 처리를 정합니다.

[핵심개념]
fetch()는 503 같은 HTTP 오류도 Response로 완료합니다. try/catch만으로는 찾을 수 없으므로 response.ok를 검사해야 합니다. ok는 상태 코드 200부터 299일 때 참입니다. 연결 실패 같은 네트워크 오류는 Promise를 거부합니다.

합친 signal을 요청에 전달하면 시간 제한이 먼저 끝날 때 TimeoutError, 사용자가 먼저 취소할 때 AbortError가 catch로 들어옵니다. HTTP 오류는 status를, 중단 오류는 name을 보존해야 호출자가 재시도·안내·조용한 종료 중 알맞은 행동을 고를 수 있습니다.

[따라하기]
다음을 fetch-result.mjs로 저장합니다. 내 컴퓨터의 임시 서버만 사용합니다.

```js
import { createServer } from 'node:http';

const server = createServer((req, res) => {
  if (req.url === '/slow') {
    return setTimeout(() => res.end('slow'), 100);
  }
  if (req.url === '/error') {
    res.writeHead(503);
    return res.end('busy');
  }
  res.end('ok');
});
await new Promise((done) =>
  server.listen(0, '127.0.0.1', done));
const base = `http://127.0.0.1:${server.address().port}`;

async function request(path, cancelAfter) {
  const user = new AbortController();
  if (cancelAfter) setTimeout(() => user.abort(
    new DOMException('사용자 취소', 'AbortError'),
  ), cancelAfter);
  const signal = AbortSignal.any([
    user.signal, AbortSignal.timeout(50),
  ]);

  try {
    const response = await fetch(base + path, { signal });
    if (!response.ok) return `http=${response.status}`;
    return `body=${await response.text()}`;
  } catch (error) {
    return `error=${error.name}`;
  }
}

console.log(await request('/'));
console.log(await request('/error'));
console.log(await request('/slow'));
console.log(await request('/slow', 10));
server.close();
```

node fetch-result.mjs를 실행합니다. Windows PowerShell, macOS Terminal, Linux 셸에서 명령은 같습니다. 예상 결과입니다.

```text
body=ok
http=503
error=TimeoutError
error=AbortError
```

Node.js v25.8.1에서 확인했습니다.

[흔한 실수]
response.ok를 빼면 404나 503을 성공 데이터처럼 처리합니다. 모든 오류를 TimeoutError로 바꾸면 사용자 취소와 네트워크 장애를 잃습니다. 오류 객체나 응답 본문 전체를 무조건 기록하지 마세요. signal을 만들고 fetch에 전달하지 않는 실수도 피합니다.

[보안 주의]
사용자 입력을 fetch 주소로 쓰면 서버가 내부 주소에 요청하는 SSRF 위험이 생깁니다. 서버에서는 허용한 호스트와 프로토콜만 요청하세요. 응답 크기·시간·동시 요청 수도 제한하고 토큰과 개인정보는 로그에서 제외합니다. 실습은 본인 소유의 로컬 환경에서만 실행합니다.

[직접 해볼 과제]
/error의 상태 코드를 503에서 429로 바꿔 http=429를 확인하세요. 시간 제한을 150밀리초로 늘려 /slow가 body=slow로 끝나는지도 비교하고, 결과별 재시도 여부를 정리합니다.

[확인문제]
1. fetch()가 503 응답에서 자동으로 catch로 가지 않는 이유는 무엇인가요?
2. response.ok가 참인 상태 코드 범위는 어디인가요?
3. 사용자 취소와 시간 초과를 구분해야 하는 이유는 무엇인가요?

[다음 학습]
다음에는 재시도할 오류만 고르고 지수 백오프와 무작위 지연으로 서버 부담을 줄입니다.

[공식 참고 자료]
- WHATWG Fetch Standard — fetch(): https://fetch.spec.whatwg.org/#dom-global-fetch
- WHATWG Fetch Standard — Response.ok: https://fetch.spec.whatwg.org/#dom-response-ok
- WHATWG DOM Standard — AbortSignal: https://dom.spec.whatwg.org/#interface-AbortSignal
- Node.js Global objects — fetch: https://nodejs.org/api/globals.html#fetch

댓글목록 0

등록된 댓글이 없습니다.

Copyright © 소유하신 도메인. All rights reserved.

사이트 정보

회사명 : 회사명 / 대표 : 대표자명
주소 : OO도 OO시 OO구 OO동 123-45
사업자 등록번호 : 123-45-67890
전화 : 02-123-4567 팩스 : 02-123-4568
통신판매업신고번호 : 제 OO구 - 123호
개인정보관리책임자 : 정보책임자명

PC 버전으로 보기