Node.js fetch의 숨겨진 5분 타임아웃 함정
Node.js 18+의 내장 fetch는 기본적으로 5분(300초) 헤더 타임아웃이 설정되어 있습니다. 이는 문서화되지 않은 undici의 기본값이며, 장시간 실행되는 API 호출 시 예상치 못한 타임아웃을 발생시킵니다.
문제 상황
딥 리서치 API를 개발하던 중, 정확히 5분 후에 다음과 같은 에러가 발생했습니다:
[Error [HeadersTimeoutError]: Headers Timeout Error] {
code: 'UND_ERR_HEADERS_TIMEOUT'
}
처음에는 다양한 원인을 의심했습니다:
- Cloudflare Worker의 CPU 시간 제한 (30초)
- ALB 타임아웃 설정
- NAT Gateway 제한
- 각종 네트워크 장비 설정
하지만 모든 설정을 확인해봐도 5분 제한은 없었습니다.
진짜 원인 발견
문제는 Node.js 18+에서 도입된 내장 fetch가 내부적으로 사용하는 undici의 기본 설정이었습니다.
// 이 코드가 문제였습니다
const response = await fetch('https://deep-research.creatrip.team/deep-research', {
method: 'POST',
// ... 다른 옵션들
});
undici는 기본적으로 headersTimeout: 300000 (5분)이 설정되어 있습니다. 이는 서버가 응답 헤더를 5분 내에 보내지 않으면 연결을 끊는다는 의미입니다.
해결 방법들
- AbortController를 사용한 커스텀 타임아웃
const fetchWithTimeout = (url, init = {}, timeoutMs = 600000) => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
return fetch(url, {
...init,
signal: init.signal || controller.signal
}).finally(() => clearTimeout(timeoutId));
};
// 사용법
const response = await fetchWithTimeout(
'https://api.example.com/long-running-task',
{ method: 'POST' },
600000 // 10분
);
- node-fetch 사용
import fetch from 'node-fetch';
const response = await fetch('https://api.example.com/long-running-task', {
method: 'POST',
timeout: 600000 // 10분
});
- undici 직접 사용
import { fetch } from 'undici';
const response = await fetch('https://api.example.com/long-running-task', {
method: 'POST',
headersTimeout: 600000, // 10분
bodyTimeout: 600000 // 10분
})
왜 이런 일이?
Node.js 팀의 공식 답변:
"This is not a bug, it's working as intended."
undici의 기본 타임아웃 설정은 대부분의 웹 요청에는 합리적이지만, 장시간 실행되는 작업(AI 처리, 대용량 데이터 분석 등)에는 문제가 됩니다.
교훈
- 새로운 기술 도입 시 기본값 확인: Node.js 18+로 업그레이드하면서 fetch가 내장되었지만, 기존 node-fetch와는 다른 기본을 가집니다.
- 에러 메시지 주의 깊게 읽기: UND_ERR_HEADERS_TIMEOUT는 undici 특유의 에러 코드입니다.
- 문서화의 중요성: 이런 기본값들이 명확히 문서화되어 있지 않아 디버깅에 시간이 오래 걸렸습니다.
마무리
5분이라는 숫자가 너무 정확해서 어딘가 설정된 값이라고 확신했는데, 설마 fetch 자체에 하드코딩되어 있을 줄은...
다른 개발자분들도 비슷한 상황을 겪지 않기를 바라며 이 글을 공유합니다. 혹시 장시간 실행되는 API를 다루신다면 타임아웃 설정을 꼭 확인해보세요!
최근에 개발한 내 Deep Research API의 경우, 실행 시간이 굉장히 긴데 클라이언트에서 계속해서 5이 지나니까 에러가 발생한다고 하더라.
요로코롬:
로컬에서 테스트 해봤을때는 5분이 넘어도 에러가 나지 않았는데 대체 왜 그런지 이해가 안되서 분석을 하는 도중에
훌륭한 동료 개발자가 그 이유를 찾았음.
위 본문에서 확인 가능하고..
→
Node.js 깃헙에 등재된 이슈 말이다.
Closed as not planned
인게 참 이해가 안된다, 인터페이스만 봐서는 어떤 동작이 일어나는지 알 수 없지 않는가?근데 ㅋㅋ
버그가 아닌 의도한 대로 작동합니다. 저는 이것을 마무리할 것이지만 후속 질문이 있다면 대화로 전환할 수 있습니다.
에??아이고 어지럽다.
단순히 timeout를 바꾸고 싶은 상황인데 AbortController를 만들어야 하는 것도 실소 포인트.
원인 찾으려고 진짜 오만 뻘짓을 다했다.. 5분과 관련이 있을 법한거 다 뒤져봤는데 원인을 아니까 너무 허탈함.