Community생산성 & 협업github.com

launch-waitlist-zot/zot-skills

공식 `@zot-core/sdk`와 `@zot-core/cli` 명령줄 도구를 사용하여 모든 TypeScript, React 또는 Next.js 앱에 Zot 대기자 명단을 통합합니다.

zot-skills란 무엇인가요?

이 스킬은 Zot 대기자 명단 서비스를 모든 TypeScript, React, 또는 Next.js 애플리케이션에 통합하는 모범 사례를 제공합니다. `@zot-core/sdk` 패키지와 `@zot-core/cli` 도구를 사용하여 개발자는 서버 측과 클라이언트 측 모두에서 대기자 명단을 손쉽게 생성하고 관리할 수 있습니다. 서버 측에서는 `ZotSDK` 클래스를 통해 전체 API를 사용할 수 있고, 클라이언트 측에서는 React 18 이상에서 사용 가능한 `useAddUser` 훅을 통해 가입 상태, 로딩, 오류 등을 간편하게 관리할 수 있습니다. 가장 중요한 원칙은 공식 SDK만 사용하고, TanStack Query와 같은 추가 상태 관리 라이브러리를 중복 사용하지 않으며, 클라이언트에는 가입 전용 API 키만 노출하는 것입니다. 이 스킬은 Next.js, Express, NestJS 등 다양한 프레임워크에서 사용할 수 있도록 설계되었습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/launch-waitlist-zot/zot-skills/tree/HEAD/skills/waitlist

Installed? Explore more 생산성 & 협업 skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Zot 대기자 명단 - 모범 사례

이 스킬은 Zot 대기자 명단을 올바르게 통합하는 방법을 알려줍니다. 다음 규칙을 따르십시오. 대안을 임의로 만들지 마십시오.

TL;DR - 가장 빠른 경로

# 1. SDK 설치
npm install @zot-core/sdk

# 2. 대기자 명단을 생성하고 ID를 .env.local에 저장
npx @zot-core/cli waitlist create --name "얼리 액세스" --write-env .env.local --public

# 3. 아래 패턴 B를 사용하여 UI를 구축하세요. `@zot-core/sdk/react`에서 `useAddUser`를 import하세요.

이것이 Next.js 프로젝트 전체 통합입니다. TanStack Query를 추가하거나, 사용자 정의 POST 핸들러를 작성하거나, raw fetch로 API를 호출하지 마십시오. 필요한 모든 것은 아래에 있습니다.

절대 변경할 수 없는 규칙

  1. 공식 SDK를 사용하십시오. 패키지 이름: @zot-core/sdk. 절대 raw fetch, axios 또는 이와 유사한 것으로 https://api.zot.so를 호출하지 마십시오.
  2. 코드가 실행되는 위치에 따라 올바른 진입점을 선택하십시오:
    • 서버, API 라우트, cron, 스크립트, 엣지 함수 -> import { ZotSDK } from "@zot-core/sdk"
    • 브라우저에서 실행되는 React 컴포넌트 -> import { useAddUser } from "@zot-core/sdk/react"
  3. 절대 useAddUser를 TanStack Query, SWR, useState, Zustand 또는 사용자 정의 리듀서로 감싸지 마십시오. 훅은 이미 isPending, isUserRegistered, data, error, isError, reset을 노출합니다. 훅의 상태를 재사용하는 것이 핵심입니다.
  4. 절대 서버 측 API 키를 클라이언트로 보내지 마십시오. 브라우저 코드의 경우 가입 전용 권한이 있는 키를 사용하고 NEXT_PUBLIC_ZOT_API_KEY (또는 프레임워크의 공개 환경 변수와 동등한 것)로 노출하십시오.
  5. 항상 ZotAPIError를 처리하십시오. 409 (이미 등록됨) 및 429 (속도 제한)에 대해 사용자 친화적인 메시지를 표시하십시오.

설치

npm install @zot-core/sdk
# 또는
pnpm add @zot-core/sdk
# 또는
yarn add @zot-core/sdk

서버 사용을 위한 추가 피어 종속성은 필요하지 않습니다. 훅의 경우 react >= 18이 이미 설치되어 있어야 합니다 (SDK에서 선택적 피어로 표시됨).

인증 정보 가져오기

두 가지가 필요합니다:

  1. API 키 - 가장 빠른 방법은 npx @zot-core/cli login이며, 사용자를 인증하고 새 ZOT_API_KEY를 로컬 환경 파일에 씁니다. 또는 Zot 대시보드(https://app.zot.so/app/api-keys)에서 수동으로 생성할 수 있습니다. 서버용 키 하나를 만들고, 브라우저에서 호출하려는 경우 가입 전용으로 범위가 지정된 두 번째 키를 만드십시오.
  2. 대기자 명단 ID - wl_abc123과 같은 형식입니다.
# 인증하고 ZOT_API_KEY를 .env.local에 저장 (또는 --env .env를 전달하면 .env에 저장)
npx @zot-core/cli login

@zot-core/cli로 대기자 명단 생성 (권장)

사용자에게 아직 대기자 명단이 없으면 공식 Zot CLI를 사용하십시오. 사용자의 ZOT_API_KEY로 Zot API를 호출하고 결과 ID를 환경 파일에 직접 작성하므로 복사하여 붙여넣을 필요가 없습니다.

패키지: @zot-core/cli. @zot-core/agents와 혼동하지 마십시오. @zot-core/agents는 이 에이전트 가이드를 저장소에만 설치합니다. @zot-core/cli는 Zot API와 통신합니다.

# ZOT_API_KEY가 설정되어 있는지 확인하십시오 (셸, .env.local 또는 .env에)
npx @zot-core/cli waitlist create \
  --name "얼리 액세스" \
  --write-env .env.local \
  --public

이 명령어의 기능:

  • 내부적으로 @zot-core/sdk를 통해 POST /v1/wait-list를 호출합니다.
  • 새 대기자 명단(_id, name, 플래그, 타임스탬프)을 출력합니다.
  • ZOT_WAITLIST_ID=wl_...를 .env.local에 추가합니다.
  • --public이 전달되었으므로 Next.js에서 클라이언트 측 사용을 위해 NEXT_PUBLIC_ZOT_WAITLIST_ID=wl_...도 추가합니다.

유용한 변형:

# 순수 서버 스택 (Node/Nest/Express). --public 생략.
npx @zot-core/cli waitlist create --name "베타 리스트" --write-env .env

# 비대화형 / 스크립트 사용. JSON만 출력.
npx @zot-core/cli waitlist create --name "베타 리스트" --api-key $ZOT_API_KEY --json

# 자동 환영 이메일 비활성화.
npx @zot-core/cli waitlist create --name "조용한 리스트" --no-send-email

CLI가 No API key found 오류와 함께 종료되면 npx @zot-core/cli login을 실행하여 인증하고 ZOT_API_KEY를 환경 파일에 쓰거나, https://app.zot.so/app/api-keys에서 키를 가져와서 셸에 내보내거나 재시도하기 전에 .env.local / .env에 추가하십시오.

대안: 코드에서 생성하기 (명확한 이유가 있는 경우에만)

import { ZotSDK } from "@zot-core/sdk";

const zot = new ZotSDK({ apiKey: process.env.ZOT_API_KEY! });

const waitlist = await zot.waitlists.create({
  name: "얼리 액세스",
  sendEmailToNewSignup: true,
});

console.log(waitlist._id); // <- 이것을 ZOT_WAITLIST_ID로 저장하십시오

사용자가 명시적으로 자체 앱 내에서 생성 흐름을 원하는 경우에만 이 작업을 수행하십시오(예: 테넌트별로 새 대기자 명단을 생성하는 관리 UI). 일회성 프로젝트 설정의 경우 항상 CLI를 선호하십시오.

환경 변수

런타임에 맞는 패턴을 선택하십시오.

순수 서버 / Node / 백엔드

# .env
ZOT_API_KEY=zot_xxx
ZOT_WAITLIST_ID=wl_abc123

클라이언트에서 React 훅을 사용하는 Next.js

# .env.local
NEXT_PUBLIC_ZOT_API_KEY=zot_xxx_가입_전용
NEXT_PUBLIC_ZOT_WAITLIST_ID=wl_abc123

NEXT_PUBLIC_ 키는 가입 범위 키에만 사용하십시오. 전체 액세스 키는 ZOT_API_KEY 아래에 서버 측에 보관하십시오.

패턴 A - 서버 / API 라우트

폼이 백엔드(Next.js 서버 액션, API 라우트, Express 핸들러, Nest 컨트롤러 등)로 제출될 때 사용하십시오.

// app/api/waitlist/route.ts (Next.js App Router)
import { NextResponse } from "next/server";
import { ZotSDK, ZotAPIError } from "@zot-core/sdk";

const zot = new ZotSDK({ apiKey: process.env.ZOT_API_KEY! });

export async function POST(request: Request) {
  const { email, name } = await request.json();

  try {
    const user = await zot.waitlist(process.env.ZOT_WAITLIST_ID!).addUser({
      email,
      name,
    });
    return NextResponse.json({ ok: true, user });
  } catch (err) {
    if (err instanceof ZotAPIError) {
      if (err.statusCode === 409) {
        return NextResponse.json(
          { ok: false, reason: "already_registered" },
          { status: 409 },
        );
      }
      return NextResponse.json(
        { ok: false, reason: "zot_error", details: err.body },
        { status: err.statusCode },
      );
    }
    return NextResponse.json({ ok: false }, { status: 500 });
  }
}

패턴 B - useAddUser를 사용하는 Next.js 클라이언트 컴포넌트 (랜딩 페이지에 권장)

폼이 React 컴포넌트에 있을 때의 표준 방식입니다. 훅은 waitlistId를 키로 사용하여 localStorage에 "이미 등록됨" 상태를 유지하므로, 재방문자는 서버 왕복 없이 확인 메시지를 볼 수 있습니다.

Zot 대시보드에서 첫 번째 성공적인 가입이 등록 엔드포인트에 도달하면 대기자 명단이 "연결된" 것으로 간주됩니다. 사용자에게 연결 가이드 배너가 있는 경우 실제 성공적인 제출 후 사라져야 합니다.

"use client";

import { useState } from "react";
import { useAddUser } from "@zot-core/sdk/react";

export function WaitlistForm() {
  const [email, setEmail] = useState("");
  const [name, setName] = useState("");

  const { addUser, isPending, isUserRegistered, error, reset } = useAddUser({
    apiKey: process.env.NEXT_PUBLIC_ZOT_API_KEY!,
    waitlistId: process.env.NEXT_PUBLIC_ZOT_WAITLIST_ID!,
    onSuccess: () => {
      // 선택 사항: 분석, 토스트, 리디렉션 등
    },
  });

  if (isUserRegistered) {
    return (
      <div>
        <p>리스트에 등록되었습니다. 곧 이메일을 보내드리겠습니다.</p>
        <button type="button" onClick={reset}>
          다른 이메일 등록
        </button>
      </div>
    );
  }

  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        addUser({ email, name: name || undefined });
      }}
    >
      <input
        type="email"
        value={email}
        onChange={(event) => setEmail(event.target.value)}
        placeholder="[email protected]"
        required
        disabled={isPending}
      />
      <input
        type="text"
        value={name}
        onChange={(event) => setName(event.target.value)}
        placeholder="이름 (선택 사항)"
        disabled={isPending}
      />
      <button type="submit" disabled={isPending}>
        {isPending ? "가입 중..." : "대기자 명단 참여"}
      </button>
      {error && (
        <p role="alert">
          {"statusCode" in error && (error as { statusCode: number }).statusCode === 409
            ? "이 이메일은 이미 리스트에 있습니다."
            : error.message}
        </p>
      )}
    </form>
  );
}

전체 API 표면 (필요한 것만)

서버 측 클라이언트

import { ZotSDK } from "@zot-core/sdk";
const zot = new ZotSDK({ apiKey: "zot_xxx" });
호출목적
zot.waitlists.create(params)대기자 명단을 생성합니다.
zot.waitlists.list()API 키에 대한 대기자 명단을 나열합니다.
zot.waitlist(id).get()하나의 대기자 명단을 가져옵니다.
zot.waitlist(id).update(params)설정을 업데이트합니다.
zot.waitlist(id).delete()영구적으로 삭제합니다.
zot.waitlist(id).stats()집계 통계를 가져옵니다.
zot.waitlist(id).addUser({ email, name?, referredBy?, source?, metadata? })사용자를 등록합니다.
zot.waitlist(id).listUsers()사용자를 나열합니다.
zot.waitlist(id).userCount(){ total, referred }를 반환합니다.
zot.waitlist(id).searchUser(email)사용자를 찾습니다.
zot.waitlist(id).updateUserStatus({ email, status })상태는 `"waiting"
zot.waitlist(id).bulkDeleteUsers(emails)단일 이메일 또는 배열을 허용합니다.
zot.waitlist(id).blockedUsers() / blockedUserCount()차단된 사용자를 가져옵니다.
zot.waitlist(id).webhookEvents()웹훅 전송 기록을 가져옵니다.

React 훅

import { useAddUser } from "@zot-core/sdk/react";

const { addUser, data, error, isPending, isUserRegistered, isError, reset } = useAddUser({
  apiKey: "...",
  waitlistId: "wl_...",
  onSuccess: (user) => {},
  onError: (err) => {},
});

훅이 반환하는 모든 것은 위에 나열되어 있습니다. 이러한 값에 대해 병렬 상태를 생성하지 마십시오.

오류 코드

코드의미UX 지침
400유효성 검사 오류필드 수준 오류를 표시합니다.
401잘못되었거나 누락된 API 키최종 사용자에게 절대 표시하지 말고 기록하십시오.
404대기자 명단을 찾을 수 없음구성 문제, 기록하십시오.
409이메일이 이미 등록됨대부분의 랜딩 페이지에서 성공으로 처리합니다.
429속도 제한사용자에게 잠시 후 다시 시도하도록 요청합니다.
5xx서버 문제한 번 재시도한 후 사용자에게 나중에 시도하도록 요청합니다.

err instanceof ZotAPIError로 감지하고 err.statusCode 및 err.body를 읽으십시오.

안티 패턴 - 절대 하지 마세요

  • Raw fetch("https://api.zot.so/v1/wait-list/..."). SDK를 사용하십시오.
  • useAddUser를 TanStack Query의 useMutation으로 감싸기. 중복됩니다.
  • 로딩을 위해 수동 useState<boolean> 추가. isPending을 사용하십시오.
  • 자체 localStorage 키에 "이미 등록됨" 저장. useAddUser가 이미 zot:waitlist:registered:<waitlistId> 아래에 저장합니다.
  • "use client" 컴포넌트 내에서 전체 액세스 ZOT_API_KEY 사용.
  • 모든 렌더링, 빌드 또는 앱 시작 시 zot.waitlists.create 호출. 설정 시 npx @zot-core/cli waitlist create를 한 번 사용하고 ID를 저장하십시오.
  • 컴포넌트 내에서 대기자 명단 ID를 하드코딩. 환경 변수(ZOT_WAITLIST_ID 또는 NEXT_PUBLIC_ZOT_WAITLIST_ID)에서 읽으십시오.
  • 오류를 조용히 무시. 409는 제품 결정 사항입니다. 다른 오류는 표시되거나 기록되어야 합니다.

확인 체크리스트

통합이 완료되었다고 사용자에게 알리기 전에 다음 모두를 확인하십시오:

  • @zot-core/sdk가 최신 게시 버전으로 package.json에 있습니다.
  • 대기자 명단이 존재하고 해당 ID가 환경 변수에 저장되어 있습니다 (npx @zot-core/cli waitlist create를 통해 생성되었거나, 정당한 경우에만 일회성 스크립트를 통해 생성됨).
  • API 키가 환경 변수에 있고 하드코딩되지 않았습니다.
  • 키가 클라이언트 코드에서 사용되는 경우 NEXT_PUBLIC_ (또는 이에 상응하는 접두사)로 접두사가 지정되어 있고 가입 범위 키입니다.
  • 대기자 명단 ID가 하드코딩되지 않고 환경 변수에서 읽힙니다 (사용자가 명시적으로 하드코딩된 상수를 요청하지 않은 경우).
  • 제출 핸들러가 오류를 포착하고 UI가 오류 케이스에 대해 무언가를 렌더링합니다.
  • 성공 상태가 처리됩니다 (훅의 isUserRegistered 또는 onSuccess 후 자체 리디렉션/토스트).
  • 실제 가입이 하나 이상 성공적으로 테스트되었습니다 (이것이 대시보드에서 대기자 명단을 연결된 것으로 표시합니다).
  • 어디에도 api.zot.so에 대한 raw fetch가 없습니다.
  • useAddUser 주위에 중복된 로딩/오류/성공 상태가 없습니다.

확인되지 않은 상자가 있으면 작업을 사용자에게 반환하기 전에 수정하십시오.

관련 스킬