Node 16에서 18로 런타임 올린 뒤로 이상한 현상이 생겼어요. 리포트 생성처럼 처리에 시간이 좀 걸리는 API(길면 2~3분)가 완료되기 전에 커넥션이 끊깁니다. 클라이언트는 socket hang up, 서버 접근 로그엔 408이 찍혀요.
코드는 하나도 안 바꿨고 딱 Node 버전만 올렸는데 이렇게 됐습니다. 로드밸런서 타임아웃도 넉넉하게 잡혀 있고요. 예전엔 잘 되던 긴 요청이 왜 갑자기 끊길까요?
Node 16에서 18로 런타임 올린 뒤로 이상한 현상이 생겼어요. 리포트 생성처럼 처리에 시간이 좀 걸리는 API(길면 2~3분)가 완료되기 전에 커넥션이 끊깁니다. 클라이언트는 socket hang up, 서버 접근 로그엔 408이 찍혀요.
코드는 하나도 안 바꿨고 딱 Node 버전만 올렸는데 이렇게 됐습니다. 로드밸런서 타임아웃도 넉넉하게 잡혀 있고요. 예전엔 잘 되던 긴 요청이 왜 갑자기 끊길까요?
댓글 1
관련해서 두 개를 같이 조정하면 됩니다. server를 만든 다음에 이렇게요.
const server = app.listen(PORT)
server.requestTimeout = 5 * 60 * 1000 // 요청 전체 허용 시간
server.headersTimeout = 60 * 1000 // 헤더 다 받는 데까지 허용 시간
requestTimeout은 요청 시작부터 끝까지 전체 허용 시간이라 이걸 넉넉히(예: 5분) 늘리면 긴 API가 안 끊깁니다. headersTimeout은 헤더 수신까지의 시간이라 이건 오히려 짧게 유지하는 게 공격 방어에 좋아요. 둘의 역할이 다르니 requestTimeout만 올리세요.
다만 진짜 권하고 싶은 건, 2~3분 걸리는 걸 HTTP 요청 하나로 붙잡고 있는 구조 자체를 다시 보는 거예요. 그 방식은 이 타임아웃뿐 아니라 앞단 로드밸런서/프록시 타임아웃, 클라이언트 재시도, 배포 중 연결 끊김 등 깨질 지점이 너무 많습니다. 리포트 생성 같은 건 요청 받으면 즉시 202로 작업 ID를 돌려주고 백그라운드 잡으로 돌린 다음, 클라이언트가 상태를 폴링하거나 완료되면 알림(웹훅/SSE) 받는 비동기 패턴으로 바꾸는 게 정석이에요. 당장은 requestTimeout 늘려 급한 불 끄고, 여유 되면 잡 큐로 빼는 걸 로드맵에 넣으시길.
참고로 이 타임아웃들은 Nginx 뒤에 있으면 Nginx의 proxy_read_timeout하고도 같이 봐야 해요. 둘 중 짧은 쪽이 먼저 끊으니, Node만 늘리고 Nginx가 짧으면 여전히 끊깁니다.