Node.js 22에서 달라진 fetch API 기본 동작
문제 상황
사내 API 게이트웨이를 Node.js 20에서 22로 업그레이드했다. 배포 후 외부 API 호출이 간헐적으로 hang 상태로 빠지는 이슈가 발생했다.
기존 코드는 단순했다.
const response = await fetch('https://external-api.com/data');
const data = await response.json();
원인 분석
Node.js 22부터 undici 기반 fetch 구현이 기본으로 들어가면서, 타임아웃 처리 방식이 달라졌다. 이전 버전에서는 암묵적으로 소켓 타임아웃이 있었지만, 22부터는 명시적으로 설정하지 않으면 무한 대기할 수 있다.
해결 방법
AbortController를 사용해 타임아웃을 명시적으로 구현했다.
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch('https://external-api.com/data', {
signal: controller.signal
});
const data = await response.json();
return data;
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Request timeout');
}
throw error;
} finally {
clearTimeout(timeoutId);
}
유틸 함수로 분리해서 재사용했다.
export async function fetchWithTimeout(url, options = {}, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
return await fetch(url, {
...options,
signal: controller.signal
});
} finally {
clearTimeout(timeoutId);
}
}
교훈
Node.js 메이저 버전 업그레이드 시 변경사항을 꼼꼼히 체크해야 한다. 특히 네트워크 레이어는 프로덕션 환경에서 예상치 못한 동작을 보일 수 있으므로, 스테이징에서 충분한 부하 테스트가 필요하다.