Cloudflare Workers로 Sume 영상 웹훅을 Queue에 넣기

Cloudflare Worker에서 Sume의 서명된 POST를 검증하고 작은 메시지를 큐에 넣은 뒤 204로 빠르게 응답하세요. Queue 메시지는 128 KB, 영수증은 최대 1 MiB입니다.

읽는 시간 6분Sume
전체 글

Cloudflare에서 Sume 영상 웹훅을 받으려면 Worker에서 @sume-com/sdk의 verifyWebhook으로 서명된 POST를 하나하나 검증하고, Cloudflare Queue에 작은 메시지를 보낸 뒤, Sume의 시도당 10초 제한 안에서 넉넉히 204로 응답하세요. 그러면 consumer Worker가 영수증을 가져옵니다. 큐에는 ID와 URL만 넣고 영수증 자체는 절대 넣지 마세요. Queue 메시지는 128 KB로 제한되지만, Sume는 영수증을 최대 1 MiB까지 인라인으로 담습니다.

Cloudflare 관련 내용은 Workers의 Context와 시크릿 페이지, Queues의 JavaScript API, 한도, 전달 보장, 설정 페이지에서, Sume 관련 내용은 Run 웹훅 (영문)과 웹훅 검증에서 가져왔습니다. 모두 2026-09-27에 확인했습니다. Cloudflare용 Sume 전용 커넥터는 없습니다. SDK에는 fetch와 WebCrypto만 있으면 되고, 문서에 Cloudflare Workers에서 동작한다고 나와 있습니다. 설치 방법은 Sume TypeScript SDK 빠른 시작에서, 서명과 재시도 규약은 Sume 영상 실행용 서명된 웹훅에서 다룹니다.

ctx.waitUntil()에서 작업을 끝내면 왜 안 되나요?

HTTP로 트리거되는 Worker에서 ctx.waitUntil()은 응답을 보낸 뒤 최대 30초까지 실행을 연장합니다. 이 한도는 요청 안의 모든 waitUntil() 호출이 함께 쓰며, 그 뒤에도 끝나지 않은 promise는 취소됩니다. 더 긴 작업에 대한 Cloudflare의 권장 방법은 Queue에 메시지를 보내고 별도의 consumer Worker에서 처리하는 것이며, consumer에서는 호출마다 최대 15분의 wall time이 주어집니다.

Sume의 Run 웹훅 (영문)과 Cloudflare의 Context, Queues 한도, JavaScript API, 전달 보장 페이지 기준, 2026-09-27 확인.
한도값
Sume 전달 시도시도당 10초, 최대 10회
Sume 인라인 영수증최대 1 MiB. 더 크면 error.result_url과 함께 payload: null로 도착
ctx.waitUntil()응답을 보낸 뒤 최대 30초
Queue 메시지128 KB(1 KB는 1000바이트)
sendBatch()메시지 100개 또는 총 256 KB
Queue consumer 호출wall time 15분
Queue 전달최소 한 번. 드물게 두 번 이상

Worker는 Queue에 무엇을 넣어야 하나요?

consumer가 결과를 찾는 데 필요한 것, 즉 중복 제거 키와 URL만 넣으세요. send()는 크기가 128 KB 미만인 본문만 받는데, 실행 영수증은 이보다 훨씬 클 수 있습니다. 모든 실행 영수증에는 자체 result_url이 있으며, 영수증이 너무 커서 인라인으로 담지 못했을 때는 같은 포인터가 error.result_url로 도착합니다. consumer는 API 키로 이 URL을 읽어 data로 감싼 전체 영수증을 받습니다.

Worker 코드는 어떤 모습인가요?

Worker 하나가 producer와 consumer를 겸할 수 있습니다. fetch()가 웹훅을 받고, queue()가 Queue에서 배치를 받습니다. verifyWebhook은 node:crypto 대신 WebCrypto를 쓰며, 그 덕분에 Workers에서 import할 수 있습니다. 이 함수는 async이고, 예외를 던지는 대신 false를 반환합니다. 응답하기 전에 send()를 await하면 Sume가 2xx를 받기 전에 메시지가 이미 Queue에 들어가 있습니다.

import { verifyWebhook } from "@sume-com/sdk";
export default {
  async fetch(request, env) {
    const body = await request.text(); // raw, before JSON.parse
    const secret = env.SUME_COM_WEBHOOK_SIGNING_SECRET;
    if (!(await verifyWebhook({ body, headers: request.headers, secret }))) {
      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 url = event.payload?.result_url ?? event.error?.result_url;
    await env.SUME_EVENTS.send({ request_id: event.request_id, result_url: url });
    return new Response(null, { status: 204 }); // well inside Sume's 10 s
  },
  async queue(batch, env) {
    for (const message of batch.messages) {
      if (await alreadyHandled(env, message.body.request_id)) continue;
      const res = await fetch(message.body.result_url, {
        headers: { Authorization: `Bearer ${env.SUME_API_KEY}` },
      });
      if (!res.ok) throw new Error(`result_url answered ${res.status}`); // retries the batch
      await saveRun(env, (await res.json()).data); // the full receipt
    }
  },
};

이벤트를 두 번 처리하지 않으려면 어떻게 하나요?

양쪽 모두 반복될 수 있습니다. Queues는 최소 한 번, 드물게는 두 번 이상 전달합니다. Sume는 느리거나 실패한 시도를 재시도하고, 다시 보내기(Redeliver)는 요청이 있을 때 이벤트를 다시 POST합니다. Cloudflare는 고유 ID를 데이터베이스 키나 멱등성 키로 쓰라고 제안합니다. Sume는 이미 그런 ID를 보냅니다. 실행의 모든 재시도에서 같은 값인 request_id이며, 그래서 consumer는 가져오기 전에 이 값을 확인합니다.

  • queue()가 예외를 던지면 배치 전체가 실패로 처리되고, consumer의 재시도 설정에 따라 재시도됩니다. max_retries의 기본값은 3입니다.
  • consumer에 dead_letter_queue를 지정하세요. 지정하지 않으면 계속 실패하는 메시지는 결국 버려집니다. 실행 자체는 사라지지 않으며, result_url이 여전히 실행을 반환합니다.

Queue와 시크릿은 어떻게 연결하나요?

코드 밖에서 설정할 것이 세 가지 있습니다.

  • Wrangler 파일에서 Queue를 바인딩하세요. queue = "sume-events"와 binding = "SUME_EVENTS"를 담은 [[queues.producers]] 항목, 그리고 같은 큐에 대한 [[queues.consumers]] 항목이 필요합니다.
  • SUME_COM_WEBHOOK_SIGNING_SECRET과 SUME_API_KEY는 wrangler secret put으로 Worker 시크릿에 저장하세요. 시크릿은 암호화된 텍스트 값이며, 환경 변수처럼 env에서 읽습니다.
  • Sume에는 Worker의 공개 HTTPS URL을 communication.webhook_url로 넘기되, 포트를 명시하지 마세요. 현재 코드는 포트가 명시된 URL을 거부합니다. 다른 거부 사유는 Sume 웹훅 URL 규칙에 정리되어 있습니다.

출처

관련 글

연동 카테고리의 다른 글

연동 글 전체 보기

작성자 Sume