Supabase Edge Function의 Sume 웹훅: JWT 대신 HMAC
Sume의 웹훅 POST에는 Supabase JWT가 없으므로 Edge Function을 verify_jwt = false로 배포하고, 모든 전달에서 Sume의 HMAC 서명을 확인하세요.

Supabase Edge Function은 해당 함수의 Supabase JWT 검사를 끄면 Sume 영상 웹훅을 받을 수 있습니다. Edge Functions는 기본적으로 유효한 JWT를 요구하는데, Sume의 POST에는 JWT가 없습니다. supabase/config.toml에서 그 함수에 verify_jwt = false를 설정하고, 대신 npm:@sume-com/sdk에서 import한 verifyWebhook으로 모든 전달을 Sume의 HMAC 서명으로 인증하세요.
Supabase 관련 내용은 Supabase의 함수 설정, Stripe 웹훅 처리, 백그라운드 작업, 한도 페이지와 출처에 나열한 관련 페이지에서, Sume 관련 내용은 Run 웹훅 (영문), 웹훅 검증, 실행과 결과 (영문)에서 가져왔습니다. 모두 2026-09-27에 확인했습니다. Supabase용 Sume 전용 커넥터는 없으며, 이 함수는 Sume가 POST를 보내는 Deno 함수일 뿐입니다. 웹훅 규약 전반은 Sume 영상 실행용 서명된 웹훅에 있습니다.
Edge Function은 왜 Sume의 POST를 거부하나요?
Edge Functions는 기본적으로 authorization 헤더에 유효한 JWT를 요구하는데, Sume는 Supabase 토큰을 보내지 않습니다. Supabase는 Stripe 웹훅을 예로 들어 이 경우의 해결책을 문서화해 두었습니다. supabase/config.toml에 verify_jwt = false를 담은 함수별 [functions.sume-webhook] 항목을 두는 것입니다. 로컬에서 서빙할 때는 supabase functions serve의 --no-verify-jwt 플래그가 같은 역할을 합니다.
Supabase는 이렇게 하면 누구나 유효한 JWT 없이 함수를 호출할 수 있다고 경고합니다. 그러니 여기서 Sume 서명 검사는 선택 사항이 아닙니다. 공개 URL과 여러분의 데이터베이스 사이를 지키는 것이 바로 이 검사입니다. Supabase의 Stripe 웹훅 함수도 같은 패턴을 따릅니다. supabase functions new의 스타터 템플릿이 publishable 키나 secret 키를 요구하는 것과 달리, 이 함수는 핸들러를 withSupabase({ auth: 'none' })로 감쌉니다.
Edge Function 코드는 어떤 모습인가요?
Edge Functions는 npm: 지정자로 npm 패키지를 import하며, @sume-com/sdk는 Deno에서 동작합니다. 검증에는 원본 바이트가 필요하므로, Stripe 예제처럼 JSON.parse보다 먼저 await req.text()로 본문을 읽으세요. verifyWebhook은 예외를 던지지 않고 false를 반환합니다.
// supabase/functions/sume-webhook/index.ts
import { withSupabase } from "npm:@supabase/server@^1";
import { verifyWebhook } from "npm:@sume-com/sdk@0.2.0";
// Sume signs every delivery, so deploy with verify_jwt = false.
export default {
fetch: withSupabase({ auth: "none" }, async (req) => {
const body = await req.text(); // raw, before JSON.parse
const ok = await verifyWebhook({
body,
headers: req.headers,
secret: Deno.env.get("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, event); // insert-or-ignore
if (fresh) EdgeRuntime.waitUntil(notifyUser(event)); // runs after the 204
return new Response(null, { status: 204 });
}),
};함수는 응답한 뒤 얼마나 더 작업할 수 있나요?
EdgeRuntime.waitUntil(promise)는 응답을 막지 않으면서 promise가 끝날 때까지 인스턴스를 계속 실행합니다. 그래도 wall clock, CPU, 메모리 한도의 제한은 받으므로, 알림이나 행 업데이트에는 맞지만 추가 생성을 기다리는 데는 맞지 않습니다. 로컬 테스트에서는 CLI가 요청마다 인스턴스를 종료해 백그라운드 작업이 일찍 멈춥니다. Supabase가 제시하는 해결책은 supabase/config.toml의 [edge_runtime] 아래에 policy = "per_worker"를 두는 것입니다.
| 한도 | 값 |
|---|---|
| Sume 전달 시도 | 시도당 10초, 최대 10회 |
| Edge Function 실행 시간(wall clock) | Free 150초, 유료 요금제 400초 |
| CPU 시간 | 요청당 2초, 비동기 I/O 제외 |
| 요청 유휴 타임아웃 | 150초. 그때까지 응답이 없으면 504 Gateway Timeout |
| 메모리 | 256MB |
| Sume 롱폼 호스트 영상 실행 | 보통 15~30분 |
시크릿과 URL은 어디서 얻나요?
첫 실행 전에 둘 다 한 번만 설정하세요.
- 서명 시크릿은 Sume 대시보드의 웹훅 탭에서 복사하거나,
account:read가 있는 키로GET /v1/webhooks/signing-secret에서 읽으세요. supabase secrets set SUME_COM_WEBHOOK_SIGNING_SECRET=…로 저장하세요. 시크릿은 다시 배포하지 않아도 바로 쓸 수 있으며, 이름은SUPABASE_로 시작할 수 없습니다. 함수는Deno.env.get으로 이 값을 읽습니다.supabase functions deploy sume-webhook으로 배포하세요. 그러면 함수가 공개 HTTPS URL인https://[YOUR_PROJECT_ID].supabase.co/functions/v1/sume-webhook에서 실행됩니다. 실행을 시작할 때 이 URL을communication.webhook_url로 넘기세요.http://localhost:54321/functions/v1/…의 로컬 서버는 전달을 받을 수 없습니다. Sume는 localhost와 HTTPS가 아닌 URL을 거부하기 때문입니다. localhost에서 Sume 웹훅 테스트하기를 참고하세요.
함수는 무엇을 저장해야 하나요?
저장하는 행의 키는 재시도마다 반복되는 request_id로 하고, payload.primary_output_url을 함께 보관하세요. media.sume.com의 Format 실행 미디어 URL은 계속 유지되지만 URL을 가진 누구에게나 공개되므로, 앱에 사용자별 접근 제어가 필요하다면 프록시하거나 복사해 두세요. 1 MiB를 넘는 영수증은 payload: null로 도착합니다. 또 다른 시크릿으로 저장해 둔 API 키로 error.result_url에서 가져오세요. 나머지 전달 규약은 Sume 영상 실행용 서명된 웹훅에서 다룹니다.
출처
관련 글
연동 카테고리의 다른 글
- Telegram 영상 생성 봇: Sume Job 후 sendVideo
Telegram 봇은 /video 커맨드를 Sume Job으로 바꿔 곧바로 답한 뒤, Sume의 서명된 웹훅이 도착하면 아티팩트 URL로 sendVideo를 호출할 수 있습니다.
- Python Text-to-Video API: 제출, 폴링, 다운로드
텍스트로 영상을 만드는 Sume API를 Python Requests로 호출하세요. POST /v1/videos 후 타임아웃을 두고 폴링하고, content 경로가 리다이렉트하는 MP4를 스트리밍하세요.
- Trigger.dev 웹훅 대기: Sume 실행용 waitpoint 토큰
Trigger.dev waitpoint 토큰을 만들고 token.url을 Sume 실행의 webhook_url로 보내면, Sume가 실행 결과를 POST할 때 wait.forToken()이 반환됩니다.
- Vercel AI SDK: Sume API 도구 호출로 영상 생성하기
Vercel AI SDK에서는 서버에서 Sume의 POST /v1/videos를 호출하는 tool()로 영상을 생성하세요. 도구는 Job id를 돌려주고, 클립은 폴링으로 받습니다.
작성자 Sume