Vercel 함수 타임아웃과 영상 생성: 웹훅을 쓰세요
Vercel Function은 기본적으로 300초에 멈추지만 Sume 영상 실행은 몇 분이 걸립니다. webhook_url과 함께 제출하고 바로 반환한 뒤, 서명된 POST를 검증하세요.

Vercel 함수 타임아웃 때문에 AI 영상 생성이 끊기지 않게 하려면 함수 안에서 영상을 기다리지 마세요. Next.js route handler에서 webhook_url과 함께 Sume 실행을 제출하고 곧바로 반환한 뒤, 실행이 끝나면 두 번째 라우트가 Sume의 서명된 POST를 검증하게 하세요. Vercel Functions는 기본적으로 300초에 멈추고, Sume에서 롱폼 호스트 영상은 보통 15분에서 30분이 걸립니다.
Sume 관련 내용은 Format 호출하기 (영문), 실행과 결과 (영문), Run 웹훅 (영문), 웹훅 검증에서, Vercel과 Next.js 관련 내용은 출처에 나열한 각자의 문서에서 가져왔으며, 모두 2026-09-27에 확인했습니다. Sume에는 Vercel 전용 커넥터가 없으며, 아래 두 라우트는 모두 일반 HTTPS 호출입니다. Sume 자체의 대기 상한과 SDK 타임아웃은 영상 생성 API 타임아웃에서 다룹니다.
Vercel Function은 얼마나 오래 실행될 수 있나요?
Vercel이 기본으로 켜 두는 fluid compute에서는 모든 요금제의 기본 한도가 300초입니다. App Router에서는 maxDuration named export로 라우트 하나의 한도를 요금제 최대치까지 올립니다. 최대 실행 시간을 넘겨 실행되는 함수는 종료됩니다.
Sume 쪽에서 영상을 만드는 실행은 초가 아니라 분 단위로 걸리며, 아직 진행 중인 실행에는 expires_at 기한이 붙어 있습니다. 베타로 제공되는 1800초 상한조차 이 90분 기한보다 짧으므로, maxDuration을 늘린다고 설계 문제가 해결되지는 않습니다.
| 한도 | 값 |
|---|---|
| Vercel Function 기본값, 모든 요금제 | 300초 |
| Hobby의 Vercel 최대치 | 300초 |
| Pro와 Enterprise의 Vercel 최대치 | 800초. 지원되는 런타임에서는 베타로 함수별 최대 1800초(30분) |
| Sume 롱폼 호스트 영상 실행 | 보통 15~30분 |
Sume 실행 기한, expires_at | created_at부터 90분, 활동이 없는 실행은 그보다 일찍. 이후 실행은 failed로 강제 종료됨 |
함수가 타임아웃되면 영상은 어떻게 되나요?
Sume 쪽에서는 아무것도 멈추지 않습니다. 클라이언트 쪽 타임아웃은 Job이나 실행을 취소하지 않으므로, 작업은 계속 실행되고 계속 과금됩니다. 문제는 여러분 쪽에서 생깁니다. 함수가 실행 ID를 저장하기 전에 죽을 수 있고, 멱등성 키 없이 재시도하면 두 번째 유료 실행이 시작됩니다. 재전송 규칙은 AI 영상 API 멱등성 키에서 다루며, 이 라우트에는 다음 두 가지 습관이면 됩니다.
- 요청마다 새로 만든 UUID가 아니라, 주문 ID에 의도적으로 올리는 버전을 붙인 것처럼 만들고 있는 대상에서 유도한
Idempotency-Key를 보내세요. 같은 키와 본문으로 재시도하면 두 번째 청구 없이idempotency_hit: true와 함께 원래 실행이 돌아옵니다. - 반환하기 전에 생성 응답의
data.id를 저장하세요. 새 실행은202로, 재전송은200으로 응답하며, 둘 다 전체 영수증을 담고 있습니다.
Next.js route handler에서 실행은 어떻게 제출하나요?
Sume 요청은 route handler에서 만들어 API 키가 서버에만 머물게 하세요. 키는 Vercel 환경 변수로 저장합니다. Vercel은 환경 변수를 저장 시 암호화하며, 값을 바꾸면 새 배포에만 적용됩니다. 키를 클라이언트 JavaScript나 NEXT_PUBLIC_* 변수에 절대 넣지 마세요. 이 라우트는 지출 상한과 communication.webhook_url을 보냅니다. 이 URL은 실행이 완료되거나 실패할 때 서명된 format.run.terminal POST를 한 번 보내 달라고 Sume에 요청합니다.
// app/api/videos/route.ts (runs on the server)
export async function POST(request: Request) {
const { orderId, productUrl } = await request.json(); // after your own auth check
const res = await fetch("https://api.sume.com/v1/formats/acme/product-promo/runs", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SUME_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `order-${orderId}-promo-v1`,
},
body: JSON.stringify({
input: { product_url: productUrl },
generation_spend_cap_usd: 3,
communication: { webhook_url: "https://example.com/api/sume-webhook" },
}),
});
if (!res.ok) return new Response(await res.text(), { status: res.status });
const { data } = await res.json(); // 202 new run, 200 idempotent replay
await saveRun(orderId, data.id); // your database
return Response.json({ runId: data.id }, { status: 202 });
}타임아웃에 다시 걸리지 않고 결과를 받으려면 어떻게 하나요?
Sume는 전달 시도마다 10초를 주고, 느리거나 실패한 응답은 재시도하며 시도는 총 최대 10회입니다. 그래서 웹훅 라우트가 하는 일은 적습니다. Next.js route handler 문서가 웹훅용으로 보여 주는 방식대로 await request.text()로 원본 본문을 읽고, @sume-com/sdk의 verifyWebhook으로 검증하고, 이벤트를 request_id(재시도마다 같은 값)당 한 번만 기록한 뒤 204로 응답합니다. 나머지 전달 규약은 Sume 영상 실행용 서명된 웹훅에서 다룹니다.
// app/api/sume-webhook/route.ts
import { after } from "next/server";
import { verifyWebhook } from "@sume-com/sdk";
export async function POST(request: Request) {
const body = await request.text(); // raw, before any JSON.parse
const ok = await verifyWebhook({
body,
headers: request.headers,
secret: process.env.SUME_COM_WEBHOOK_SIGNING_SECRET!,
});
if (!ok) return new Response("bad signature", { status: 401 });
const event = JSON.parse(body);
if (event.event !== "format.run.terminal") return new Response(null, { status: 204 });
const fresh = await recordOnce(event.request_id, body); // insert-or-ignore
if (fresh) after(() => markOrderReady(event)); // runs after the 204 is sent
return new Response(null, { status: 204 });
}after() 안에서는 무엇을 실행해야 하나요?
next/server의 after()는 응답을 보낸 뒤에 실행할 작업을 예약하며, Next.js 15.1.0부터 stable입니다. 이 작업은 해당 라우트에 적용되는 플랫폼 기본값이나 설정한 최대 실행 시간 동안 실행되고, Vercel에서는 Next.js가 waitUntil로 호출을 살려 둡니다. 같은 함수 실행 예산을 204 뒤에 쓰는 것일 뿐입니다.
- 잘 맞는 작업: 주문을 준비 완료로 표시하기, 사용자에게 알리기,
primary_output_url을 여러분의 레코드에 저장하기. - 맞지 않는 작업: 몇 분씩 기다리는 모든 작업. 다음 단계가 추가 생성이라면 자체 웹훅을 단 Sume 실행을 새로 시작하세요.
- 1 MiB를 넘는 영수증은
payload: null로 도착하므로,after()안에서 API 키로error.result_url에서 가져오세요.
웹훅이 끝내 도착하지 않으면 어떻게 하나요?
result_url 읽기를 백업으로 두세요. GET /v1/format-runs/{run_id}/result는 실행이 종료되면 전체 영수증을, 진행 중에는 409 run_not_completed를 반환합니다. 취소되거나 건너뛴 실행은 POST를 절대 보내지 않습니다. 리다이렉트는 따라가지 않고 3xx는 실패한 시도로 치므로, 웹훅 라우트의 최종 URL을 등록하세요. webhook_delivery를 읽고 이벤트를 다시 보내는 방법은 Sume 웹훅이 도착하지 않나요?에서 보여 줍니다.
출처
관련 글
연동 카테고리의 다른 글
- Windsurf MCP 서버: Devin Desktop에 Sume 추가
Windsurf는 이제 Devin Desktop입니다. API 키 헤더로 Sume 호스팅 MCP 서버를 Devin Local 에이전트나 레거시 Cascade 에이전트에 추가하세요.
- Zapier AI 영상 자동화: Zap 두 개와 Sume 웹훅 하나
Zap 하나는 Custom Request로 Sume 영상 실행을 시작하고, 두 번째 Zap은 Catch Raw Hook으로 Sume의 서명된 웹훅을 받아 Code 단계에서 검증합니다.
- Claude 커스텀 커넥터로 Sume 추가하기 (원격 MCP)
Customize > Connectors에서 Sume 호스팅 MCP 서버를 Claude에 추가하고, Sume OAuth 동의가 무엇을 부여하는지 확인한 뒤, 유료 도구를 허용할지 정하세요.
- Airtable 자동화 영상 생성 API: 레코드마다 영상 하나
Airtable Run a script 액션으로 callback_url과 함께 POST /v1/videos를 호출하고, 두 번째 자동화에서 Sume 웹훅을 받아 URL을 저장하세요.
작성자 Sume