메인 콘텐츠로 건너뛰기
자동화는 앱의 일부로, 일정에 따라 또는 외부 서비스에서 이벤트를 보낼 때 자동으로 실행됩니다. kazzle.config.ts의 process 컴포넌트에서 선언합니다. 하나의 컴포넌트는 필요한 만큼 많은 트리거를 가질 수 있습니다.

구조

{
  name: 'events',
  type: 'process',
  path: './components/events/index.ts',
  processMode: 'persistent', // or 'triggered'
  triggers: [
    { name: 'daily-digest', kind: 'schedule', schedule: '0 9 * * *', path: '/cron/daily-digest' },
    { name: 'stripe',       kind: 'webhook',                          path: '/webhook/stripe' },
  ],
}
두 가지가 일어나고 있습니다:
  • processMode생명주기를 선택합니다 — 장시간 실행되는 서버 또는 트리거당 일회성 실행.
  • triggers[]는 이 컴포넌트를 실행해야 하는 이벤트를 나열합니다.
두 부분은 독립적입니다. 지속적인 서버는 cron을 가질 수 있습니다. 임시 프로세스는 웹훅을 가질 수 있습니다. 워크로드에 맞는 생명주기를 선택한 후 원하는 만큼 많은 트리거를 연결하세요.

processMode

모드실행 내용사용 시기
persistent (기본값)장시간 실행되는 HTTP 서버. 트리거가 POST됩니다.컴포넌트가 이미 HTTP를 제공하거나 메모리에 상태를 유지합니다 (큐, 웹소켓, 캐시).
triggered진입 스크립트가 트리거당 생성되고 종료됩니다.순수 백그라운드 작업 — 야간 정리, 단일 Stripe 웹훅 핸들러 등. 유휴 서버 없음.

트리거

각 트리거는 name (컴포넌트 내에서 고유), kind, 그리고 모드에 따라 schedule 및/또는 path를 가집니다.
필드필수 여부참고
name항상웹훅 URL 세그먼트 및 로그에서 사용됩니다. Kebab-case.
kind항상'schedule' 또는 'webhook'.
schedulekind: 'schedule'일 때5필드 cron 표현식. 분 단위 해상도가 최소입니다.
pathprocessMode: 'persistent'일 때트리거가 도착하는 서버의 HTTP 경로.

지속 모드 — 서버로의 HTTP

지속 컴포넌트의 트리거가 실행되면, Kazzle은 선언된 path에서 서버로 POST합니다. 요청은 다음을 포함합니다:
헤더의미
Authorization: Bearer ${KAZZLE_TRIGGER_SECRET}이를 검증하세요. 일치하지 않는 호출을 거부하세요.
x-kazzle-trigger-name매니페스트의 트리거 name.
x-kazzle-trigger-run-id로그 상관관계를 위한 불투명 ID.
x-kazzle-triggered-bycron | webhook | manual.
웹훅 트리거의 경우 원본 요청 본문이 POST 본문으로 전달됩니다. 일정 트리거의 경우 본문은 비어있습니다.
// components/events/index.ts (persistent mode)
const TRIGGER_SECRET = process.env.KAZZLE_TRIGGER_SECRET ?? '';

Bun.serve({
  port: Number(process.env.PORT),
  hostname: process.env.HOST,
  async fetch(req) {
    const url = new URL(req.url);

    if (req.method === 'POST' && url.pathname === '/cron/daily-digest') {
      if (req.headers.get('authorization') !== `Bearer ${TRIGGER_SECRET}`) {
        return new Response('Unauthorized', { status: 401 });
      }
      await sendDigest();
      return Response.json({ ok: true });
    }

    if (req.method === 'POST' && url.pathname === '/webhook/stripe') {
      if (req.headers.get('authorization') !== `Bearer ${TRIGGER_SECRET}`) {
        return new Response('Unauthorized', { status: 401 });
      }
      const event = await req.json();
      await handleStripe(event);
      return Response.json({ ok: true });
    }

    return new Response('not found', { status: 404 });
  },
});

트리거 모드 — 트리거당 일회성

triggered 컴포넌트의 트리거가 실행되면, Kazzle은 진입 스크립트를 새로 생성하고 종료될 때까지 기다립니다. path는 없으며, 스크립트는 환경 변수에서 어떤 트리거가 실행되었는지 알아냅니다.
환경 변수
TRIGGER_NAME매니페스트의 트리거 name.
TRIGGERED_BYcron | webhook | manual.
RUN_ID로그 상관관계를 위한 불투명 ID.
WEBHOOK_PAYLOADJSON 본문 (웹훅 트리거만).
// components/events/index.ts (triggered mode)
const trigger = process.env.TRIGGER_NAME;
const runId = process.env.RUN_ID;

if (trigger === 'daily-digest') {
  await sendDigest();
} else if (trigger === 'stripe') {
  const event = JSON.parse(process.env.WEBHOOK_PAYLOAD ?? '{}');
  await handleStripe(event);
}

console.log(`run ${runId} done`);
트리거 컴포넌트는 프로덕션에서 유휴 머신이 없습니다 — 호출당 시작되고 종료 시 종료됩니다.

웹훅 URL

POST https://api.kazzle.app/webhooks/{spaceId}/{appId}/{componentName}/{triggerName}
triggerName 세그먼트는 해당 컴포넌트의 triggers[]에서 kind: 'webhook' 항목과 일치해야 합니다. 알 수 없는 트리거 이름은 404를 반환합니다.

일정 해상도

Cron 표현식은 5필드 (분, 시간, 월의 날짜, 월, 요일)이며 분 단위 해상도가 최소입니다. 분 미만의 일정은 매니페스트 검증 시 거부됩니다.

실행 기록 방식

각 트리거 실행은 trigger_name, triggered_by, run_id 및 실행의 종료 상태와 함께 process_runs 행을 작성합니다. 자신의 코드에서 이를 쿼리하거나 앱의 실행 보기에서 검사할 수 있습니다.

크레딧 부족

실패한 실행은 기록되고 로깅되지만, 일정은 정상 주기에 따라 계속 실행됩니다 — 불안정한 실행이 트리거를 비활성화하지 않습니다. 실행을 중지하는 유일한 것은 크레딧입니다: 모든 트리거 실행은 스페이스의 잔액에 대해 확인되며, 스페이스가 크레딧 부족 상태이거나 청구 설정이 없는 동안 실행은 402로 건너뜁니다. 이는 자동 복구됩니다 — 일정은 활성 상태로 유지되고 충전 후 다음 실행은 수동 재개 없이 정상적으로 실행됩니다.

나중에 자동화 추가

간단한 앱은 트리거 없이 시작하고 나중에 추가할 수 있습니다 — 일일 요약 추가, Stripe 연결, 정리 실행. 컴포넌트의 생명주기 (processMode)와 트리거 (triggers[])는 독립적이므로 앱의 나머지 부분을 다시 작성하지 않고도 변경할 수 있습니다.