Supabase cron Edge Function: AI 영상 예약 실행

pg_cron과 pg_net으로 Supabase Edge Function을 예약 실행하고, 영상 API 키는 함수 시크릿에 두고, 날짜로 만든 키로 하루에 실행 하나를 시작하세요.

읽는 시간 6분Sume
전체 가이드

Supabase Edge Function을 cron 스케줄로 실행하려면 Supabase Cron(pg_cron 확장)과 pg_net을 활성화한 뒤, 함수 URL로 net.http_post를 호출하는 SQL을 cron 작업으로 예약하세요. 이때 URL과 키는 Supabase Vault에서 읽습니다. AI 영상이라면 영상 API 키는 SQL이 아니라 함수가 보관합니다. 함수는 날짜로 만든 Idempotency-Key와 웹훅 URL로 실행을 시작하고 바로 반환합니다.

Supabase 관련 내용은 Supabase의 Edge Functions 스케줄링, Cron, pg_net 페이지와 출처에 나열한 Edge Functions 페이지에서, Sume 관련 내용은 Format 호출하기 (영문), 실행과 결과 (영문), 인증에서 가져왔습니다. 모두 2026-09-27에 확인했습니다. Sume에는 Supabase 전용 커넥터가 없으며, 함수가 HTTPS로 직접 호출합니다. 받는 쪽은 Sume용 Supabase Edge Function 웹훅에서 다룹니다.

SQL 쿼리로 Supabase에서 cron을 어떻게 활성화하나요?

Supabase Cron은 pg_cron 확장 위에서 동작하며, pg_net을 쓰면 cron 작업이 HTTP 요청을 보낼 수 있습니다. 아래 SQL로 둘 다 활성화하거나, Dashboard의 Integrations 아래에 있는 Cron 모듈에서 pg_cron을 활성화하세요. Supabase는 두 가지를 주의하라고 합니다. pg_net API는 베타라서 함수 시그니처가 바뀔 수 있고, pg_cron을 비활성화하면 모든 cron 작업이 영구 삭제됩니다.

create extension pg_cron with schema pg_catalog;
grant usage on schema cron to postgres;
grant all privileges on all tables in schema cron to postgres;
create extension pg_net with schema "extensions";

pg_cron으로 Edge Function을 어떻게 예약하나요?

Supabase의 레시피는 프로젝트 URL과 키를 Vault에 저장한 다음, 함수를 대상으로 net.http_post를 예약합니다. 아래 버전은 여기에 cron_secret을 더합니다. Supabase는 publishable 키를 브라우저에서 써도 안전한 키라고 설명하므로, 이 키를 가졌다고 해서 호출자가 누구인지 증명되지는 않습니다. 작업 이름은 대소문자를 구분하며, 같은 이름으로 두 번째 작업을 만들면 첫 번째 작업을 덮어씁니다. Supabase 예제는 스케줄에 GMT 기준으로 주석을 달므로 0 6 * * *는 06:00 GMT입니다. net.http_post는 기본적으로 2000 ms를 기다리며, Supabase가 직접 제시한 분 단위보다 짧은 간격의 예제는 timeout_milliseconds를 5000으로 설정합니다.

select vault.create_secret('https://project-ref.supabase.co', 'project_url');
select vault.create_secret('YOUR_SUPABASE_PUBLISHABLE_KEY', 'publishable_key');
select vault.create_secret('a-long-random-string', 'cron_secret');

select cron.schedule(
  'daily-recap-video',
  '0 6 * * *', -- 06:00 GMT every day
  $$
  select net.http_post(
    url:= (select decrypted_secret from vault.decrypted_secrets where name = 'project_url')
      || '/functions/v1/start-daily-video',
    headers:= jsonb_build_object(
      'Content-Type', 'application/json',
      'apikey', (select decrypted_secret from vault.decrypted_secrets where name = 'publishable_key'),
      'x-cron-secret', (select decrypted_secret from vault.decrypted_secrets where name = 'cron_secret')
    ),
    timeout_milliseconds:= 5000
  ) as request_id;
  $$
);

Edge Function은 영상 API에 무엇을 보내야 하나요?

SUME_API_KEY와 CRON_SECRET은 supabase secrets set으로 설정하세요. 시크릿은 다시 배포하지 않아도 바로 쓸 수 있으며, 이름은 SUPABASE_로 시작할 수 없습니다. 함수는 스타터 템플릿의 키 검사를 그대로 두고 자체 시크릿 검사를 더한 뒤, 저장된 Sume 레시피인 Format의 실행 하나를 시작합니다. 키와 본문은 UTC 날짜만으로 만들므로, 그날 다시 호출되어도 똑같은 요청을 보냅니다. data.id는 날짜별로 저장하세요. 202는 새 실행이고, 200은 그날 실행의 재전송입니다.

import { withSupabase } from "npm:@supabase/server@^1";

export default {
  fetch: withSupabase({ auth: ["publishable", "secret"] }, async (req) => {
    const cronSecret = Deno.env.get("CRON_SECRET");
    if (!cronSecret || req.headers.get("x-cron-secret") !== cronSecret) {
      return new Response("forbidden", { status: 403 });
    }
    const day = new Date().toISOString().slice(0, 10); // UTC date
    const res = await fetch("https://api.sume.com/v1/formats/acme/daily-recap/runs", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${Deno.env.get("SUME_API_KEY")}`,
        "Content-Type": "application/json",
        "Idempotency-Key": `daily-recap-${day}`,
      },
      body: JSON.stringify({
        input: { day },
        communication: { webhook_url: "https://project-ref.supabase.co/functions/v1/sume-webhook" },
      }),
    });
    const { data, error } = await res.json();
    if (!res.ok) return Response.json(error, { status: res.status });
    return Response.json({ run_id: data.id, replay: data.idempotency_hit });
  }),
};

SQL에서 영상 API를 바로 호출하면 왜 안 되나요?

pg_net도 JSON을 POST할 수 있지만 이 용도에는 잘 맞지 않습니다. 모든 cron 작업은 cron.job 테이블에 저장되므로 명령에 적은 키가 테이블에 남는데, Sume 문서는 API 키를 신뢰할 수 있는 서버에 두라고 안내합니다. 또 net.http_post는 비동기라서 요청 ID만 반환하고, 응답은 net._http_response에 들어가 기본적으로 6시간 동안 보관됩니다. 저장해야 할 실행 ID를 SQL은 끝내 보지 못하지만, 함수는 봅니다.

cron 작업이 두 번 실행되거나 호출이 타임아웃되면 어떻게 되나요?

날짜 키 덕분에 그날 일어나는 반복은 모두 안전합니다. 여기서 중요한 Sume의 멱등성 규칙은 두 가지이며, 나머지는 AI 영상 API 멱등성 키에서 다룹니다.

  • 같은 키, 같은 본문: 원래 영수증과 idempotency_hit: true가 담긴 200이 돌아옵니다. 두 번째 실행도, 두 번째 청구도 없습니다.
  • 같은 키, 다른 본문: 409 idempotency_conflict가 돌아오고 아무것도 실행되지 않습니다. webhook_url은 본문의 일부이므로 고정해 두세요.
  • 무슨 일이 있었는지 보려면 cron 작업의 모든 실행과 그 상태를 기록하는 cron.job_run_details, 그리고 함수의 status_code와 timed_out 플래그가 담긴 net._http_response를 읽으세요.
Supabase의 Cron, pg_net, Edge Function 한도 페이지와 Sume의 실행과 결과 (영문) 기준, 2026-09-27 확인.
구성 요소한도 또는 기본값
Supabase Cron동시에 실행되는 작업은 8개 이하, 작업마다 10분 이하로 둘 것을 Supabase가 권장
net.http_post기본 타임아웃 2000 ms. 응답은 6시간 보관
Edge Function wall clockFree 플랜 150 s, 유료 플랜 400 s
Sume 롱폼 영상15분에서 30분 걸리는 작업
Sume 실행 마감created_at으로부터 최대 90분. 그 뒤 failed로 강제 종료

함수가 영상을 기다려야 하나요?

아닙니다. 롱폼 영상은 위 표에 나온 Edge Function wall clock 한도 어느 쪽보다도 오래 걸리며, 함수가 지켜보기를 멈춰도 실행과 그 지출은 멈추지 않습니다. 실행이 완료되거나 실패하면 communication.webhook_url로 서명된 POST가 한 번 전송되므로, 두 번째 Edge Function이 결과를 받습니다. Sume용 Supabase Edge Function 웹훅에서 verify_jwt = false와 HMAC 검사를 쓰는 그 함수를 보여 줍니다. Cloudflare에서 쓰는 같은 트리거 패턴은 Cloudflare Workers cron job에서 다룹니다.

출처

관련 글

연동 카테고리의 다른 글

연동 글 전체 보기

작성자 Sume