행사 등록

행사 등록5분 분량

무료 커뮤니티 행사를 위한 Eventbrite 없는 행사 등록

무료 커뮤니티 행사의 등록을 내 사이트에서 운영하세요: 세션 선택, 내 도구로 보내는 확인 이메일, 강의실별 인원, 체크인 명단.

Preet SutharIkiform 창업자

무료 행사라면 내 사이트의 등록 폼이 Eventbrite의 신청을 대신할 수 있습니다. 폼은 Ikiform으로 POST하고, Ikiform은 각 등록을 저장하고 팀에 이메일을 보냅니다. 내 이메일 제공업체에서 확인 이메일을 보내는 웹훅을 추가하고, 이메일별 최신 등록을 기준으로 세션별 좌석을 세세요. 유료 티켓, 강제 정원 제한, 체크인 스캔이 필요하면 Eventbrite나 다른 티켓 플랫폼을 계속 쓰세요.

티켓 플랫폼 없이 달라지는 점

Eventbrite는 다섯 가지 일을 한꺼번에 하므로, 이제 직접 해야 할 일을 잊기 쉽습니다. 무료 커뮤니티 데이, 밋업 시리즈, 워크숍이라면 대부분 작은 일입니다. 솔직하게 나누면 이렇습니다.

Eventbrite에서 내 설정으로 옮겨 오는 일과 각각 누가 맡는지행사 페이지그들의 페이지, 그들의 브랜드내 페이지등록그들의 결제 화면내 폼, Ikiform이 저장확인 이메일대신 발송이메일 도구로 웹훅정원강제 상한, 대기자 명단직접 세고 일시 중지체크인앱, QR 스캔인쇄한 명단일Eventbrite에서내 사이트에서Eventbrite에서 내 설정으로 옮겨 오는 일과 각각 누가 맡는지행사 페이지그들의페이지,그들의 브랜드내 페이지등록그들의 결제화면내 폼,Ikiform이저장확인 이메일대신 발송이메일 도구로웹훅정원강제 상한,대기자 명단직접 세고일시 중지체크인앱, QR스캔인쇄한 명단일Eventbrite에서내 사이트에서
  1. 행사 페이지는 Eventbrite의 페이지에서 내 디자인을 입힌 내 사이트로 옮겨 옵니다.
  2. 등록은 Eventbrite의 결제 화면에서 Ikiform으로 POST하는 내 폼으로 옮겨 옵니다.
  3. 확인 이메일은 더 이상 대신 보내 주지 않습니다. 웹훅이 내 이메일 제공업체에서 보냅니다.
  4. 정원은 더 이상 강제 상한이 아닙니다. 세션별 등록을 세고, 자리가 차면 폼을 일시 중지합니다.
  5. 체크인은 더 이상 QR 스캔 앱이 아닙니다. CSV 내보내기로 명단을 인쇄합니다.
처음 세 가지는 내 사이트에서 더 낫습니다. 나머지 두 가지는 더 불편하고, 규모가 크거나 유료인 행사라면 티켓 플랫폼에 남을 이유가 됩니다.

Eventbrite의 요금은 바뀌므로 비용을 비교하기 전에 해당 요금 페이지를 확인하세요. 무료 행사에서 떠나는 이유는 대개 수수료가 아닙니다. 참석자 명단이 남의 플랫폼에 있고, 페이지가 그들처럼 보이며, 등록자가 자체 계정과 이메일 설정을 가진 다른 회사에 정보를 넘기게 된다는 점입니다. 유료 행사라면 그대로 두세요. Ikiform에는 결제가 없습니다.

세션 선택이 있는 등록 폼

커뮤니티 데이에는 병렬 세션이 있는 경우가 많고, 강의실 크기도 제각각입니다. 어떤 세션에 참석할 계획인지 물어보면 행사 전에 인기 있는 워크숍을 더 큰 방으로 옮길 수 있습니다. 두 세션이 같은 시간에 열리므로, 이 정의는 네 개 중 세 개까지만 고를 수 있게 제한합니다.

미리보기

Ikiform의 검증기를 브라우저에서 실행합니다. 아무것도 전송되지 않습니다.

참석할 세션
점심
sessions는 배열로 저장되는 multi_select입니다. HTML에서는 체크박스 이름을 sessions[]로 지어야 하나만 골라도 목록으로 저장됩니다.
  • name은 명찰용이라고 표시되어 있어, 사람들이 불리고 싶은 이름을 받을 수 있습니다.
  • lunch는 No lunch 옵션이 있는 radio이므로, 실제로 먹을 사람 수만큼 주문할 수 있습니다.
  • updates는 체크되지 않은 체크박스로, 자체 created_at을 가진 제출에 true 또는 false로 저장됩니다. true일 때만 뉴스레터에 추가하세요.
  • 스택별 마크업은 메인 가이드의 RSVP 폼을 쓰고, 필드만 이것으로 바꾸세요.
등록 폼에서 앞으로의 소식도 제안한다면, 그 체크박스는 기본적으로 체크하지 않은 상태로 두고 무엇을 보낼지 정확히 밝히세요. 체크박스는 동의 기록입니다. 아래의 확인 이메일은 마케팅이 아니므로 동의가 필요 없습니다.

확인 이메일은 내 이메일 도구에서 보내기

Ikiform은 폼을 작성한 사람에게 이메일을 보내지 않습니다. 알림 이메일은 여러분과 팀에게만 가므로 자동 응답, 확인 이메일, 더블 옵트인이 없습니다. 이런 이메일은 웹훅으로 트리거해 이메일 도구에서 보내세요. 행사라면 그래도 보내세요. 사람들은 확인 이메일을 상사에게 전달하고, 행사 당일 아침에 검색해 찾기 때문입니다. 이 라우트 핸들러는 Ikiform의 서명된 웹훅을 받아 Resend API로 이메일을 보냅니다. 발송 엔드포인트가 있는 제공업체라면 어디든 같은 방식으로 작동합니다.

app/api/registered/route.ts
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";

const TITLES: Record<string, string> = {
  keynote: "Opening talk, 10:00",
  workshop_a: "Workshop A, 11:30",
  workshop_b: "Workshop B, 11:30",
  panel: "Closing panel, 15:00",
};

function escape(text: string) {
  return text.replace(/[&<>"']/g, (char) => `&#${char.charCodeAt(0)};`);
}

export async function POST(request: Request) {
  const payload = await request.text();
  try {
    const event = await verifyWebhook({
      payload,
      headers: request.headers,
      secret: process.env.IKIFORM_WEBHOOK_SECRET!,
    });
    if (event.type !== "submission.created") return new Response(null, { status: 204 });

    const fields = event.data.fields;
    const email = String(fields.email ?? "");
    const chosen = (Array.isArray(fields.sessions) ? fields.sessions : []).map((id) => TITLES[String(id)] ?? String(id));
    const response = await fetch("https://api.resend.com/emails", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.RESEND_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": request.headers.get("webhook-id") ?? event.data.submission_id,
      },
      body: JSON.stringify({
        from: "Community Day <hello@community.example>",
        to: email,
        reply_to: "hello@community.example",
        subject: "You're registered for Community Day, 14 March",
        html: [
          `<p>Hi ${escape(String(fields.name ?? ""))},</p>`,
          "<p>You're on the list for Community Day on 14 March at The Old Library. Doors open at 9:30.</p>",
          `<p>Your sessions: ${chosen.map(escape).join(", ")}.</p>`,
          '<p><a href="https://community.example/day.ics">Add it to your calendar</a>. Can\'t come after all? Reply to this email.</p>',
        ].join(""),
      }),
    });
    if (!response.ok) return new Response(`Email API answered ${response.status}`, { status: 502 });
    return new Response(null, { status: 204 });
  } catch (error) {
    if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
    throw error;
  }
}
  • verifyWebhook이 서명을 확인하므로, 다른 사람이 여러분의 도메인으로 이메일을 보내게 만들 수 없습니다.
  • Idempotency-Key는 전달의 webhook-id를 씁니다. 이 값은 자동 재시도와 실패한 전달의 수동 Retry에서도 그대로이므로, 응답을 잃어버린 전달이 두 번째 이메일을 보내지 않습니다.
  • 이름과 세션 제목은 HTML에 넣기 전에 이스케이프합니다. 필드 값은 신뢰할 수 없는 입력으로 다루세요.
  • 502를 반환하면 Ikiform이 재시도하므로, 이메일 제공업체의 짧은 장애로 확인 이메일을 잃지 않습니다. 스팸은 웹훅을 트리거하지 않으므로, 봇 때문에 모르는 사람에게 이메일을 보내는 일은 없습니다.

대시보드의 Webhooks에서 라우트를 추가하고 시크릿을 IKIFORM_WEBHOOK_SECRET에 복사하세요. Ikiform은 실패한 전달을 약 3일 동안 재시도하며, 전달 로그에서 각 시도를 볼 수 있습니다. 코드 없이 하려면 Zapier나 Make 같은 자동화 도구로 같은 이메일을 보낼 수 있습니다.

세션별 좌석

Ikiform은 강의실이 차도 특정 세션의 등록을 멈추지 않고, 마감된 옵션을 숨기지도 못합니다. 대신 각 사람의 최신 등록을 기준으로 세고, 숫자가 필요할 때마다 실행하세요.

seats.ts
import { createClient } from "@ikiform/sdk";

const ikiform = createClient({ apiKey: process.env.IKIFORM_SECRET_KEY! });
const ROOMS: Record<string, number> = { keynote: 200, workshop_a: 30, workshop_b: 30, panel: 200 };
const latest = new Map<string, string[]>();

for await (const submission of ikiform.submissions.iterate({ formId: process.argv[2], status: "received", limit: 100 })) {
  const email = String(submission.fields.email ?? "").trim().toLowerCase();
  const chosen = Array.isArray(submission.fields.sessions) ? submission.fields.sessions.map(String) : [];
  if (email && !latest.has(email)) latest.set(email, chosen);
}

for (const [session, seats] of Object.entries(ROOMS)) {
  const taken = [...latest.values()].filter((chosen) => chosen.includes(session)).length;
  console.log(`${session.padEnd(12)} ${String(taken).padStart(4)} of ${seats}${taken >= seats ? "  FULL" : ""}`);
}

워크숍이 차면 페이지를 수정해 마감으로 표시하고, 폼과 필드 정의에서 해당 옵션을 빼서 새 POST가 고를 수 없게 하세요. 하루 전체가 차면 Pause form을 고르세요. POST는 403 form_paused를 받고, 페이지에는 등록이 마감되었다고 표시해야 합니다.

상황해결 방법
워크숍 하나가 마감폼과 필드 정의에서 해당 옵션을 빼고, 페이지에도 알리세요
하루 전체가 마감Pause form을 고르고 폼을 대기자 명단용 이메일 주소로 바꾸세요
누군가 취소함확인 이메일에 답장합니다. 해당 제출을 삭제하면 집계에서 자리가 비워집니다
불참자가 예상됨무료 행사에서는 일정 비율의 불참이 흔합니다. 내 행사의 비율을 기록해 두고 다음에는 조금 더 많이 받으세요

입구에서

  1. 전날 저녁에 Submissions를 CSV로 내보내고, 최신 행을 남기며 중복 이메일을 제거하세요.
  2. 이름순으로 정렬하고 name, organisation, lunch만 남긴 뒤, 접수대용으로 두 부 인쇄하세요.
  3. 같은 시트에서 name 열을 써서 메일 머지로 명찰을 인쇄하세요.
  4. 현장 참가자는 손으로 적으세요. 행사 후에는 동의한 경우에만 내 명단에 추가하세요.

공지하기 전에 확인하기

  1. 폼의 Allowed domains에 사이트 도메인을 추가하고, 개발하는 동안에는 localhost도 추가하세요. 비워 두면 어디서든 POST를 받습니다.
  2. 내 이메일로 세션 두 개를 골라 등록해 보세요.
  3. 두 세션이 적힌 확인 이메일이 내 도메인에서 도착합니다.
  4. 웹훅을 추가했다면 Webhooks에서 엔드포인트를 여세요. 전달이 서버의 응답과 함께 succeeded로 표시됩니다.
  5. 좌석 집계를 실행하세요. 두 세션에 각각 한 자리가 찬 것으로 표시됩니다.
  6. 아무것도 도착하지 않았나요? 대시보드의 상태 필터를 Spam으로 바꾸세요. 스팸도 실제 제출과 같은 성공 응답을 받습니다.

주의할 점

문제해결 방법
최대 3개까지 고르라는 422누군가 네 세션을 모두 골랐습니다. max_items 규칙이 작동한 것입니다. 체크박스 옆에 Ikiform의 메시지를 보여 주세요.
하나만 고르면 세션이 하나만 저장됩니다체크박스 이름을 sessions[]로 지으세요. 필드를 multi_select로 정의하면 값이 하나여도 목록으로 저장됩니다.
확인 이메일은 없는데 등록은 Ikiform에 있습니다엔드포인트 로그에서 해당 전달을 여세요. 제공업체의 상태와 함께 502가 있으면 제공업체가 이메일을 거부한 것이며, 발신 도메인이 인증되지 않은 경우가 많습니다.
제출은 대시보드에 있는데 이메일이나 웹훅이 오지 않았습니다스팸으로 저장되었습니다. 스팸은 아무것도 보내지 않습니다. Spam 필터에서 열어 이유를 확인하고, 스팸이 아님으로 표시하면 이메일과 웹훅이 발송됩니다.
429 rate_limited한 방문자가 폼의 레이트 리밋보다 많이 보냈습니다. Standard는 방문자당 분당 5회를 허용합니다. Retry-After만큼 기다리거나, 공유 IP 하나에서 POST하는 폼이라면 Relaxed나 Custom을 고르세요.

질문

막히는 단계가 있거나 내 상황에 맞는 가이드가 필요하다면 이메일을 보내세요: hello@ikiform.com.