Node.js 18의 Fetch API 도입과 마이그레이션 경험
배경
4월에 출시된 Node.js 18에서 Fetch API가 실험적 기능으로 추가되었다. 브라우저와 동일한 API를 사용할 수 있다는 점이 매력적이어서 사내 API 서버 프로젝트에 적용해보기로 했다.
기존에는 axios를 사용했는데, 번들 크기와 브라우저 코드 재사용성을 고려하면 native fetch로 전환할 만한 시점이라고 판단했다.
주요 변경 사항
에러 핸들링
axios는 4xx, 5xx 응답을 자동으로 throw하지만, fetch는 네트워크 에러만 reject한다.
// Before (axios)
try {
const { data } = await axios.get('/api/users');
return data;
} catch (error) {
// 4xx, 5xx 자동 catch
}
// After (fetch)
const response = await fetch('/api/users');
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
타임아웃 처리
axios의 timeout 옵션 대신 AbortController를 사용해야 한다.
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch('/api/data', {
signal: controller.signal
});
clearTimeout(timeoutId);
} catch (error) {
if (error.name === 'AbortError') {
// timeout 처리
}
}
실전 적용
공통 fetch wrapper를 만들어 기존 axios 인터페이스와 유사하게 사용할 수 있도록 했다.
const request = async (url, options = {}) => {
const { timeout = 8000, ...fetchOptions } = options;
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(url, {
...fetchOptions,
signal: controller.signal
});
clearTimeout(id);
if (!response.ok) {
throw new Error(`${response.status}: ${response.statusText}`);
}
return response.json();
};
결과
- node_modules 크기 약 2.3MB 감소
- 브라우저 클라이언트 코드와 HTTP 유틸 공유 가능
- Node 18 LTS 전환 전까지는
--experimental-fetch플래그 필요
당장 프로덕션 적용은 무리지만, 올 10월 LTS 출시 이후 본격 도입을 검토할 예정이다.