TechFeedTechFeed
Cloud & DevOps

504 Gateway Timeout, maxDuration, 스트리밍 | 응답이 끊기기 전에 어디를 자를까?

배포만 504면 함수가 라우트 최대 실행 초 안에 응답을 못 끝낸 상태다. 오류 이름의 함수 호출 시간 초과, maxDuration, 스트리밍 시계, after로 일을 자르는 순서를 본다. 한도만 키우지 말고 첫 바이트를 먼저 보낸다. Next.js, 버셀, 클라우드플레어, API, 함수, 개발자 기준. 2026년 9월 MDN, RFC 9110, 버셀 공식 문서.

by

504는 앞단이 업스트림에서 시간 안에 응답을 못 받아 연결을 끊은 상태입니다. 버셀이면 오류 이름이 함수 호출 시간 초과이고, 라우트에 적은 최대 실행 초보다 함수가 오래 돌았을 때 납니다.


문의 저장이나 미리보기처럼 조금 긴 일을 한 요청 안에서 끝까지 붙잡으면, 로컬은 되고 배포만 회색 504가 뜹니다. 제가 돌리는 발행 함수도 썸네일을 만든 뒤에야 JSON을 돌려주던 시절에 같은 줄을 봤죠.


응답이 끊기기 전에 일을 자르세요. 첫 바이트를 먼저 보내고, 긴 일은 응답 뒤로 넘깁니다. 한도 숫자만 키우면 같은 504가 조금 늦게 옵니다. 근거는 MDN 504버셀 함수 호출 시간 초과 문서입니다.


504는 실행 한도가 먼저 끊긴 줄이다

함수가 설정한 초 안에 끝나지 않아 게이트웨이가 끊은 줄입니다. 코드가 틀렸다는 뜻부터 단정하지 마세요.


MDN은 504를 게이트웨이 또는 프록시가 업스트림에서 제때 응답을 받지 못했을 때 나는 코드로 적습니다. RFC 9110도 같은 뜻입니다. 버셀은 이 줄을 함수 호출 시간 초과로 이름 붙입니다. 함수가 플랜과 라우트에 적힌 최대 실행 초를 넘기면, 앞단이 브라우저에 504를 돌려줍니다.


로그에 성공이 찍히고 화면만 거절이면 자리가 다릅니다. 호출은 끝났는데 유효한 HTTP가 없으면 502입니다. 그 칸은 버셀 업스트림 502에서 다룹니다. 여기서는 시계가 먼저 끝난 줄, 즉 함수가 아직 돌고 있는데 한도가 닫힌 줄만 봅니다.


로컬 next dev는 이 한도가 느슨합니다. 배포 뒤에만 앞단이 초를 재니, 한국에서 1인으로 넥스트와 버셀을 붙인 사이트에서 자주 갈리는 증상입니다. 카카오 콜백이나 문의 폼처럼 외부에서 한 번만 치는 경로가 특히 그렇습니다.


먼저 기억할 것 | 504는 "시간 안에 응답이 없다"는 기록입니다. 502는 "응답이 비었거나 형식이 깨졌다"는 기록입니다. 한도 숫자를 올리기 전에 오류 이름부터 읽으세요.


게이트웨이가 업스트림 대기 한도를 넘겨 504로 연결을 끊는 개념 이미지
함수가 아직 돌아도 앞단 시계가 먼저 끝나면 화면은 504다

최대 실행 초와 플랜 한도를 같이 읽기

라우트 숫자와 플랜 상한을 같이 보고, 작은 쪽이 실제 한도입니다. 파일에 큰 숫자만 적으면 배포에서 잘립니다.


넥스트 앱 라우터는 라우트 파일에 export const maxDuration = 60처럼 초를 적습니다. 페이지 라우터와 구형 런타임은 vercel.json의 함수 경로에 같은 이름을 적습니다. 버셀 문서는 이 값이 함수가 돌 수 있는 가장 긴 시간이라고 적습니다. 요청을 처리하고 응답을 보내는 시간, 스트림이 끝나는 시간까지 포함합니다.


플루이드 컴퓨트가 켜진 2026년 8월 문서 기준 표는 아래와 같습니다. 취미는 기본과 상한이 같고, 프로는 상한이 더 깁니다. 숫자는 플랜과 런타임에 따라 바뀌니, 글의 표보다 대시보드와 공식 표가 우선입니다.


플랜기본 초상한 초연장 상한
취미300300없음
프로3008001800(베타)
엔터프라이즈3008001800(베타)

예전에 취미 플랜 10초를 기준으로 쓴 글이 검색에 많이 남아 있습니다. 지금 기본이 300초여도, 라우트에 15를 적으면 15초입니다. 파일 숫자, 플랜 상한, 앞단 프록시 초 세 칸을 나란히 보세요. 배포 함수에서만 fetch가 끊기는 줄은 ETIMEDOUT 쪽입니다. 여기는 함수 전체 시계입니다.


상태와 함수 호출 시간 초과 이름을 같이 읽기
curl -sI https://your-domain.example/api/slow # HTTP/2 504 # x-vercel-error: FUNCTION_INVOCATION_TIMEOUT # x-vercel-id: icn1::... # CF-Ray가 있으면 앞단 클라우드플레어도 같이 본다

스트리밍을 켜도 시계는 멈추지 않는다

첫 바이트가 나가도 스트림이 끝날 때까지 한도 시계는 갑니다. 헤더만 빨리 보내면 한도가 리셋된다고 보지 마세요.


버셀 한도 문서는 요청 처리와 응답 전송, 스트리밍 응답까지 최대 실행 초에 들어간다고 적습니다. 채팅 토큰을 흘려 보내거나 큰 파일을 조각 내어 내려도, 마지막 바이트가 나가기 전에 시계가 끝나면 앞단은 504를 냅니다. 사용자는 중간에 끊긴 본문만 봅니다.


버셀 지식 베이스는 긴 생성 작업에 스트리밍을 권합니다. 이유는 사용자가 기다림을 덜 느끼게 하려는 쪽이지, 한도 시계를 멈추려는 쪽이 아닙니다. 모델 호출이 수분 이어지면 스트림을 켜도 같은 초 안에 끝나야 합니다. 한 요청에서 끝까지 붙잡을 일이 아니면 수락만 먼저 보내고, 결과는 조회 경로로 나눕니다.


헤더를 보낸 뒤에 본문에서 예외가 나면 상태 코드를 바꿀 수 없습니다. 그 자리는 잘린 본문이나 502로 보이기도 합니다. 시계가 먼저 닫혔는지, 스트림이 깨졌는지는 실시간 함수 로그와 오류 이름을 같이 봐야 갈립니다.


함정 | 스트림 첫 줄을 빨리 보내도 최대 실행 초는 그대로입니다. 긴 생성은 스트림과 한도를 같이 보고, 한 요청에 안 들어가면 작업을 쪼개세요.


응답 스트림이 이어지는 동안에도 함수 실행 시계가 계속 가는 개념 이미지
첫 바이트가 나가도 스트림이 끝날 때까지 한도 초는 줄어든다

응답을 자르고 긴 일을 뒤로 넘기는 자리

수락 응답을 먼저 보내고, 긴 일은 응답 뒤에 붙입니다. 프록시가 받을 첫 바이트를 한도 안에 만드는 쪽이 504를 줄입니다.


넥스트 앱 라우터의 after는 응답이 나간 뒤에 일을 붙입니다. 문의 저장 확인, 미리보기 생성, 페이지뷰 기록처럼 사용자가 당장 안 봐도 되는 일은 여기로 옮기세요. 핸들러가 JSON을 돌려준 뒤에야 앞단은 200이나 202를 확정합니다. 응답 후 작업의 자리는 after와 waitUntil에서 따로 다룹니다.


응답 자체에 결과가 꼭 필요하면 작업을 쪼개세요. 수락만 주고, 결과는 조회 API나 웹훅으로 받습니다. 한 요청 안에서 외부 API를 여러 번 직렬로 기다리면 시계가 빨리 닳습니다. 같은 키로 몰리면 429 백오프가 겹치니, 재시도와 504를 한 로그에 섞지 마세요.


자리한 요청에 두면자를 곳
썸네일·미리보기생성 끝나야 200202 수락 후 뒤로
메일·알림외부 SMTP 대기응답 뒤 발송
모델 생성스트림이 수분작업 아이디로 조회
웹훅 전달상대 서버 지연큐에 넣고 바로 200
여러 외부 API직렬 await 합산필요한 것만, 나머지는 뒤

한도만 키우고 일을 안 자르면 같은 504가 조금 늦게 옵니다. 버셀 문서도 함수가 응답을 안 돌려주면 결국 시간 초과가 난다고 적습니다. 응답이 비어서 나는 502와 시계가 닫혀 나는 504를 한 수정으로 덮지 마세요.


한 요청에서 긴 일을 붙잡으면 504가 나는 자리
// app/api/preview/route.js export const maxDuration = 60; export async function POST(request) { const body = await request.json(); await makeOgImage(body); // 이 대기가 한도를 넘기면 504 return Response.json({ ok: true }); }
수락을 먼저 보내고 긴 일은 after로
import { after } from 'next/server'; export const maxDuration = 30; export async function POST(request) { const body = await request.json(); const id = crypto.randomUUID(); after(async () => { await makeOgImage(id, body); }); return Response.json({ accepted: true, id }, { status: 202 }); }

앞단 504와 함수 504를 헤더로 가르기

오류 이름이 시간 초과면 함수 칸입니다. CF-Ray만 있으면 앞단이 먼저 끊은 칸입니다.


버셀이 만든 504에는 x-vercel-error에 함수 호출 시간 초과가 붙습니다. 응답에 CF-Ray만 있고 버셀 오류 이름이 없으면, 주황 구름을 켠 클라우드플레어가 원본 연결을 기다리다 끊은 줄일 수 있습니다. 원본 호스트로 curl이 200이면 코드를 고치기 전에 앞단 대기 초와 SSL 모드를 보세요.


커스텀 도메인에 프록시를 켜 둔 한국 사이트는 앞단 대기 초가 원본 함수 한도보다 짧을 때가 있습니다. 함수는 아직 돌고 있는데 브라우저는 이미 앞단 504를 본 뒤입니다. DNS만 클라우드플레어에 두고 프록시를 끈 상태와, 프록시를 켠 상태를 한 번씩 비교하면 앞단인지 원본인지 빨리 갈립니다. 터널이 죽은 뒤의 거절은 클라우드플레어 터널과 자리가 겹칩니다.


앞단과 원본을 섞지 말 것 | 함수 로그는 아직 도는데 화면만 504이고 CF-Ray만 있으면, maxDuration을 올리기 전에 주황 구름부터 보세요. 원본 curl이 200이면 앞단 설정입니다.


클라이언트와 앞단 프록시, 원본 함수가 이어진 경로에서 어느 시계가 먼저 끝나는지 보여주는 개념 이미지
CF-Ray만 있으면 앞단, 버셀 오류 이름이 TIMEOUT이면 함수 칸이다

한도만 올리기 전에 자를 일 다섯 가지

외부 호출 직렬, 썸네일, 메일, 웹훅 대기, 모델 생성을 한 요청에서 빼세요. 숫자가 필요하면 그다음에 올립니다.


버셀 문서의 점검 순서는 함수가 너무 오래 도는지, 응답을 안 돌려주는지, 무한 루프인지, 업스트림이 느린지입니다. 한국 1인 배포에서 제가 먼저 보는 목록은 더 짧습니다. 실시간 함수 로그를 연 채로 같은 요청을 한 번 더 보내고, 오류 이름과 헤더를 읽은 뒤에야 숫자를 만집니다.


  • [ ] curl로 상태와 x-vercel-error를 읽었다
  • [ ] 대시보드 실시간 함수 로그를 연 채로 같은 요청을 다시 보냈다
  • [ ] 라우트의 최대 실행 초와 플랜 상한 중 작은 쪽을 확인했다
  • [ ] 첫 바이트 전에 await로 붙잡은 긴 일이 있는지 봤다
  • [ ] 수락 응답 뒤로 옮길 일을 골랐다
  • [ ] CF-Ray가 있으면 앞단과 원본을 갈랐다

같은 키로 재시도가 붙으면 한도 안에 일을 더 넣게 됩니다. 429와 504가 한 화면에 보이면 재시도부터 멈추세요. 빌드 단계 빨간 줄은 CI 전용 스크립트 실패와 자리가 다릅니다. 여기는 런타임 응답입니다.


참고 자료


내부 연계: 버셀 업스트림 502, 배포 함수 fetch 타임아웃, 응답 후 after, 429 백오프, 클라우드플레어 터널


인용한 한도 숫자와 헤더 이름은 2026년 9월 공개 문서 기준입니다. 플랜 표는 대시보드가 우선입니다.


자주 묻는 질문

로컬은 되는데 버셀만 504입니다. 회선 문제인가요?

로컬 개발 서버는 앞단 실행 한도가 느슨합니다. 배포 뒤에만 게이트웨이가 초를 재니, 긴 await가 그때 드러납니다. 프리뷰 URL에 curl로 헤더를 읽고, 실시간 로그를 연 채로 같은 본문을 다시 보내세요.


502와 504는 어떻게 다른가요?

MDN 기준으로 502는 업스트림에서 잘못된 응답을 받은 줄, 504는 응답 자체를 시간 안에 못 받은 줄입니다. 버셀에서는 응답 없음이 502, 함수 호출 시간 초과가 504입니다. 한도 숫자를 올리기 전에 오류 이름부터 보세요.


maxDuration을 올리면 504가 사라지나요?

오류 이름이 시간 초과면 한도 칸이 맞습니다. 그래도 일을 안 자르면 같은 줄이 조금 늦게 옵니다. 플랜 상한보다 큰 숫자는 잘립니다. 수락 응답을 먼저 보내는 쪽이 먼저입니다.


스트리밍을 켜면 한도가 늘어나나요?

늘어나지 않습니다. 버셀은 스트림이 끝나는 시간까지 최대 실행 초에 넣습니다. 첫 바이트가 빨라도 마지막 바이트 전에 시계가 끝나면 504입니다. 긴 생성은 작업 아이디로 나누세요.


클라우드플레어를 켜 둔 도메인입니다. 어디를 보나요?

응답에 CF-Ray만 있고 버셀 오류 이름이 없으면 앞단입니다. 원본 호스트로 curl이 200이면 주황 구름과 앞단 대기 초를 보세요. 버셀 오류 이름이 시간 초과면 함수 칸입니다.


카카오 콜백만 504입니다. 로그인 토큰 문제인가요?

토큰 거절은 보통 401입니다. 콜백만 504면 콜백 핸들러가 한도 안에 응답을 못 끝낸 줄입니다. 리다이렉트를 주기 전에 긴 일을 붙잡는지, 터널·앞단이 먼저 끊는지부터 보세요.


504는 앞단이 업스트림에서 시간 안에 응답을 못 받은 상태입니다. 함수 로그와 화면 실패를 같은 줄로 읽지 말고, 오류 이름을 읽고, 응답을 먼저 보낸 뒤 긴 일을 빼세요. 한도 숫자는 그다음입니다. 관련 글: 502, ETIMEDOUT, after, 429.


504Gateway TimeoutmaxDuration스트리밍버셀함수 타임아웃Next.js클라우드플레어API데브옵스개발자after

함께 보면 좋은 문제 해결

EXPLORE / Cloud & DevOps

이어서 읽어보기

전체 토픽 둘러보기