Spillway 연동 가이드

사이트에 접속이 몰릴 때 서버가 감당할 수 있는 만큼만 들여보내고, 넘치는 방문자는 순서대로 줄 세우는 대기열을 붙이는 방법입니다. 사이트 코드에는 몇 줄만 넣고, 어느 페이지·버튼에 대기열을 걸지, 몇 명까지 들일지, 언제 시작할지는 관리 콘솔에서 바로 바꿉니다.

가드 v0.6.0대기 타입: 단일 페이지 · 버튼·이벤트입장 방식: 입장 인원 제한 · 입장 속도 제한Cloudflare Workers 사이트

1준비물

Spillway 담당자에게 아래 네 가지를 받습니다. 관리 콘솔에서 직접 확인할 수 있는 것은 콘솔 위치를 함께 적었습니다.

항목예시어디서
고객사 idacme-shop콘솔 [사이트 연동] → 연결 정보
엔진 주소https://queue.spillway.co.kr콘솔 [사이트 연동] → 연결 정보
서명 키43자 문자열 (비밀 값)콘솔 [사이트 연동] → [서명 키 보기]
콘솔 계정이메일 + 임시 비밀번호담당자가 발급 (첫 로그인 때 비밀번호 변경)
서명 키는 비밀 값입니다. 코드나 저장소에 그대로 넣지 말고 Worker 의 secret 으로만 넣어 주세요. 키가 있으면 입장권을 만들 수 있습니다.

현재 가드는 Cloudflare Workers 로 서빙되는 사이트를 지원합니다. 다른 환경(Nginx, Spring 등)은 담당자에게 문의해 주세요.

2대기 타입·입장 방식 고르기

대기실마다 대기 타입을 하나 고릅니다. 한 사이트에 두 타입의 대기실을 함께 둘 수 있습니다.

단일 페이지 사용 가능

정해진 주소에 들어올 때 줄을 세웁니다. 예: 오픈 이벤트 페이지, 메인 페이지. 사이트 코드는 가드만 있으면 됩니다.

버튼·이벤트 사용 가능

페이지는 자유롭게 보고, 버튼을 누를 때(구매·예약·전송) 줄을 세웁니다. 페이지에 스크립트 한 줄과 버튼 속성이 필요합니다.

구간(시작~종료) 준비 중

시작 주소부터 종료 주소까지 한 흐름(예: 주문서~결제)에 머무는 인원을 제한합니다.

입장 방식

대기실마다 몇 명을 어떻게 들일지 고릅니다. 새 대기실은 입장 인원 제한으로 시작합니다.

입장 인원 제한 기본

동시 이용 인원(예: 300명)을 넘을 때만 줄을 섭니다. 자리가 있으면 대기 화면 없이 바로 입장합니다. 입장한 사람은 입장 후 이용 시간(기본 10분) 동안 자리를 차지한 것으로 봅니다. 한꺼번에 몰리는 것을 막고 싶으면 분당 입장 상한(선택)을 함께 둡니다.

입장 속도 제한

모든 방문자가 대기 화면을 거쳐 분당 입장 인원만큼 일정하게 들어갑니다. 서버가 처리할 수 있는 속도를 정확히 알고 있을 때 알맞습니다.

입장 인원 제한에서 이미 기다리는 사람이 있으면, 자리가 나도 새로 온 사람이 먼저 들어가지 않고 줄 뒤에 섭니다 (새치기 방지).

3가드 설치 (공통)

가드는 사이트 Worker 맨 앞에서 요청을 검사하는 작은 라이브러리입니다. 두 대기 타입 모두 이 단계가 필요합니다.

① 설치

터미널
npm i github:chandarasoft/spillway-guard#v0.6.0

② Worker 의 fetch 를 가드로 감싸기

src/index.ts
import { withSpillway } from "spillway-guard";
const SPILLWAY = {
  tenant: "acme-shop",                       // 고객사 id
  engineUrl: "https://queue.spillway.co.kr", // 엔진 주소 (또는 env.SPILLWAY_ENGINE_URL)
  // siteOrigin: "https://www.example.com",  // CDN 뒤에 있을 때 방문자가 보는 주소
};
export default {
  // 줄을 서야 하면 대기 화면으로, 자리가 있으면 대기 화면 없이 바로 입장(응답에 입장 쿠키),
  // 그 밖에는 그대로 통과 (엔진 장애 때도 통과)
  fetch: (request, env, ctx) => withSpillway(request, env, SPILLWAY, (req) => handle(req, env, ctx)),
};
async function handle(request, env, ctx) {
  // … 기존 처리
}

어느 주소·API 에 대기열을 걸지, 입장 방식과 인원·속도, 예약 시작은 코드에 적지 않습니다. 가드가 엔진에서 대기실 규칙을 받아 오므로(10초 캐시), 바꿔도 사이트를 다시 배포할 필요가 없습니다. 이 코드는 콘솔 [사이트 연동]에서 고객사 id 가 채워진 채로 복사할 수 있습니다.

v0.5.0 의 guardPage 를 쓰고 있다면 그대로 동작합니다. 다만 입장 인원 제한 대기실에서 자리가 있을 때 같은 주소로 리다이렉트가 한 번 생기므로 withSpillway 로 바꾸는 것을 권합니다.

③ 설정 값 넣기

wrangler.jsonc
"vars": {
  "SPILLWAY_ENGINE_URL": "https://queue.spillway.co.kr"
}
터미널 — 서명 키는 secret 으로
npx wrangler secret put SPILLWAY_TOKEN_SECRET
안전 원칙: 엔진에 연결할 수 없거나 설정이 빠지면 가드는 그냥 통과시킵니다(fail-open). 대기열 때문에 사이트가 막히는 일은 없습니다. CSS·JS·이미지 같은 정적 파일과 검색엔진 봇도 자동으로 통과합니다.

4단일 페이지

콘솔 설정

  1. [대기실] → [새 대기실] → 대기 타입 단일 페이지
  2. 대기 대상 경로에 줄을 세울 주소를 넣습니다. * 는 그 아래 모든 주소입니다.
    예: /, /event/* · 제외: /event/notice
  3. [주소 확인] 칸에 실제 주소를 넣어 줄을 서는지 바로 확인합니다.
  4. 입장 제어에서 입장 방식을 고르고 동시 이용 인원(입장 인원 제한) 또는 분당 입장(입장 속도 제한)을 정해 저장합니다. 저장 즉시 반영됩니다.

방문자가 겪는 흐름

대기 대상 주소 접속→자리가 있으면 바로 입장
꽉 찼거나 입장 속도 제한→대기 화면 (앞에 N명, 예상 시간)→차례가 되면 자동 이동→정해진 시간 동안 자유 이용

사이트 코드는 3단계(가드)만으로 끝입니다. 대기 화면의 문구·색상·로고는 콘솔 [대기 화면]에서 바꾸고 미리보기로 확인합니다.

5버튼·이벤트

콘솔 설정

  1. [대기실] → [새 대기실] → 대기 타입 버튼·이벤트, 대기실 id 예: checkout
  2. 대기 대상 API 에 막을 요청을 넣습니다. 예: POST /api/orders (메서드 없이 /api/vote/* 는 모든 메서드)
  3. [API 확인] 칸에서 POST /api/orders 를 넣어 막히는지 확인하고 저장합니다.

페이지에 넣을 코드

HTML — 페이지에 한 번
<script src="https://queue.spillway.co.kr/sdk.js"
        data-tenant="acme-shop" defer></script>
HTML — 버튼에 속성 하나
<form action="/api/orders" method="post">
  <button type="submit" data-spillway-room="checkout">구매하기</button>
</form>

버튼을 누르면 줄을 서야 할 때만 페이지 위에 대기 창이 뜹니다. 차례가 되면 창이 닫히고 원래 동작(폼 전송, 클릭 처리)이 그대로 이어집니다. 한가할 때는 창이 뜨지 않고 바로 진행됩니다. 입력해 둔 내용은 그대로 남습니다.

코드로 제어할 때 (선택)

JavaScript
// 입장권을 헤더에 붙여 호출. 입장권이 만료돼 429 가 오면 다시 줄을 세우고 한 번 재시도
const res = await Spillway.fetch("checkout", "/api/orders", {
  method: "POST",
  body: JSON.stringify(order),
});

// 줄만 세우고 이후 동작은 직접
try {
  await Spillway.gate("checkout");
  openPaymentWindow();
} catch (e) {
  if (e.name === "SpillwayCancelled") { /* 사용자가 대기 창을 닫음 */ }
}
기능설명
data-spillway-room버튼·링크에 붙이면 클릭 전에 줄을 세움
Spillway.gate(room)입장권을 받을 때까지 대기 (유효한 입장권이 있으면 바로)
Spillway.fetch(room, url, init)입장권을 x-spillway-token 헤더로 붙여 호출
입장권 유효 시간대기실의 입장 후 시간 (기본 10분, 입장 인원 제한이면 "입장 후 이용 시간") — 그동안은 다시 눌러도 바로 처리
서버 쪽은 3단계의 가드가 막아 줍니다. 대기 대상 API 를 입장권 없이 직접 호출하면 429 {"spillway":"queued"} 를 돌려줍니다. 입장권은 헤더나 쿠키(__spw_{고객사}_{대기실})에서 읽습니다.

6보안 정책(CSP)

사이트가 Content-Security-Policy 를 쓰고 있다면 버튼·이벤트 타입에 엔진 주소를 허용해야 합니다. 빠지면 브라우저가 sdk.js 를 막습니다 (개발자 도구에 "Provisional headers are shown" 만 보임).

public/_headers (예)
Content-Security-Policy: default-src 'self';
  script-src 'self' https://queue.spillway.co.kr;
  frame-src  https://queue.spillway.co.kr;
  …

script-src 는 sdk.js, frame-src 는 대기 창(엔진 화면을 iframe 으로 띄움) 때문에 필요합니다. 단일 페이지 타입은 사이트를 떠나 엔진 화면으로 이동하므로 CSP 변경이 필요 없습니다.

7CDN·프록시 뒤에 있을 때

CloudFront 같은 CDN 이 Worker 앞에 있으면 두 가지를 확인합니다.

  • 방문자 주소 알려 주기 — Worker 가 받는 주소(*.workers.dev)와 방문자 주소가 다르면, 대기 후 돌아갈 주소가 틀어집니다. 가드 옵션에 siteOrigin: "https://www.example.com" 을 넣어 주세요.
  • 쿠키·요청 전달 — CDN 이 __spw_ 로 시작하는 쿠키와 x-spillway-token 헤더, 대기 대상 API 의 POST 요청을 Worker 까지 넘겨야 합니다. 빠지면 입장한 뒤에도 계속 대기 화면이나 429 가 나옵니다.

콘솔 [고객사 설정]의 허용 사이트에는 방문자가 보는 주소(https://www.example.com)를 넣습니다.

8선착순 이벤트 (예약 시작)

티켓 오픈처럼 정한 시각에 시작하는 이벤트는 대기실을 예약 시작으로 둡니다. 사이트 코드는 바꿀 것이 없습니다.

  1. [대기실] → 대기실 선택 → 운영 시간에서 예약 시작을 고르고 시작 시각(한국 시간)을 넣습니다.
  2. 사전 대기 시작을 고릅니다: 시작 10분 전 · 30분 전(기본) · 1시간 전.
  3. [대기 화면] → 화면 종류 [시작 전]·[종료]에서 문구를 바꾸고 미리보기로 확인합니다.
사전 대기 전: 사이트 원래 페이지→사전 대기: 카운트다운 화면→시작 순간 무작위 추첨으로 순번→순번대로 입장
  • 추첨 대상은 시작하는 순간 카운트다운 화면을 열어 둔 사람입니다. 창을 닫고 떠난 사람은 빠집니다. 일찍 온 사람이 유리하지 않으므로 새로 고침을 반복할 필요가 없습니다.
  • 시작한 뒤에 온 사람은 추첨된 사람들 뒤에 순서대로 섭니다.
  • 종료는 자동이 아니라 콘솔에서 누릅니다. 종료 뒤에 온 사람은 종료 안내를 보고, 이미 순번을 받은 사람은 계속 입장합니다. [다시 열기]로 되돌릴 수 있습니다.
  • 예약한 시각을 기다리지 않고 [지금 시작]으로 바로 시작할 수도 있습니다.

9운영하기

하고 싶은 것콘솔 위치반영
지금 상황 보기[대기 현황] → 대기실 탭 (대기·이용 중 인원, 실시간 입장 흐름 그림)2초마다 갱신
동시 이용 인원·입장 속도 바꾸기[대기 현황] → 대기실 탭 → 조절 → [적용]즉시 (대기실 설정에 저장)
지금 기다리는 사람만 모두 입장[대기 현황] → 대기자 일괄 입장즉시
당분간 아무도 기다리지 않게[대기 현황] → 즉시 해제 (재개로 복귀)즉시
대기실 켜기·끄기[대기 현황] → 대기열 사용 스위치즉시 (사이트 최대 10초)
이벤트 지금 시작 · 종료 · 다시 열기[대기 현황] → 대기실 탭 → 이벤트즉시 (시작 전 화면에도 바로 알림)
대상 주소·API, 입장 방식, 시작 시각 바꾸기[대기실]저장 즉시 (사이트 최대 10초)
대기 화면 문구·색상 (대기 중·시작 전·종료)[대기 화면] (모든 대기실 / 대기실별)저장 즉시, 기다리던 화면도 새로 고침
설정 되돌리기[변경 이력] → 이 버전으로 되돌리기즉시

서명 키 교체

키가 유출됐거나 담당자가 바뀌었을 때, Spillway 담당자에게 교체를 요청합니다. 교체부터 사이트 반영까지는 대기열이 통과 모드가 되므로 한가한 시간에 진행하세요.

  1. 담당자가 콘솔에서 새 키로 교체
  2. [사이트 연동] → [서명 키 보기]로 새 키 확인
  3. npx wrangler secret put SPILLWAY_TOKEN_SECRET 로 넣고 배포

10문제 해결

증상원인과 조치
sdk.js 요청에 "Provisional headers are shown"사이트 CSP 가 막음 → 6. 보안 정책에 엔진 주소 추가
버튼이 계속 429 로 막힘① 콘솔 대기실 id 와 data-spillway-room 값이 다름 ② 사이트의 서명 키가 콘솔의 키와 다름(운영·개발 키 혼동) ③ CDN 이 쿠키를 넘기지 않음
대기 화면에 invalid_return_url[고객사 설정] 허용 사이트에 방문자 주소가 없음
입장한 뒤에도 다시 대기 화면서명 키 불일치, 또는 CDN 이 __spw_ 쿠키를 넘기지 않음
입장 인원 제한인데 자리가 있어도 대기 화면이 나옴이미 기다리는 사람이 있으면 새로 온 사람도 줄 뒤에 섭니다(정상). 또는 분당 입장 상한에 걸렸는지, guardPage 대신 withSpillway 를 쓰는지 확인
예약 시작인데 원래 페이지가 그대로 보임사전 대기 시작 전이라 정상입니다. 사전 대기 시각부터 카운트다운 화면이 보입니다
규칙을 바꿨는데 그대로가드가 규칙을 10초 캐시합니다. 잠시 후 다시 확인
엔진 장애 때가드와 sdk.js 모두 대기 없이 통과시킵니다. 사이트는 그대로 동작합니다

11오픈 전 점검표

  • 가드 v0.6.0 설치, Worker 의 fetch 를 withSpillway 로 감쌈
  • SPILLWAY_ENGINE_URL 은 운영 엔진 주소, SPILLWAY_TOKEN_SECRET 은 운영 콘솔의 키
  • 콘솔 허용 사이트에 방문자 주소 등록
  • (버튼·이벤트) sdk.js 스크립트와 data-spillway-room, CSP 의 script-src·frame-src
  • (CDN 사용 시) siteOrigin, 쿠키·헤더·POST 전달
  • 콘솔에서 동시 이용 인원 1명(또는 분당 입장 1명)으로 두고, 창 두 개로 접속해 두 번째 창이 줄을 서는지, 즉시 해제로 입장되는지 확인
  • (예약 시작) 시작 시각·사전 대기 시간, [대기 화면]의 시작 전·종료 미리보기
  • 확인 후 동시 이용 인원·입장 속도를 운영 값으로 되돌리기