셀프서비스 예약 키오스크
키오스크는 신뢰 모델이 반전된 예약 웹사이트입니다. 클라이언트는 설계상 인증되지 않으며 물리적으로 공유되므로, 모든 신뢰는 서버에(가격, 용량, 멱등성) 있고 모든 상태는 일시적(인메모리, 자동 리셋, 영구 저장 안 함)입니다. 이 스킬의 모든 내용은 이 두 가지 사실에서 비롯됩니다.
사용 시기
- 슬롯 기반 용량(스테이션 × 시간 창)에 선택적 장비 및 소모품 부가 상품이 있는 워크업 예약 터미널 구축.
- 이미 온라인 예약이 있는 앱에 키오스크 모드 추가.
- 기존 키오스크를 아래의 엄격한 규칙에 따라 감사; provenance.md는 각 규칙의 근거를 기록합니다.
사용하지 말아야 할 때
- 온프레미스 오프라인 폴백 서버 — 이는 자매 스킬인
island-mode-server의 영역입니다. 이 스킬은 키오스크의 클라이언트 측 페일오버 계약만 정의합니다. - 디지털 사이니지 / 디스플레이 화면 — 다른 기능(재생, 입력 없음), 다른 스킬.
- 직원용 POS 또는 관리 예약 도구 — 직원은 인증되고 신뢰됩니다. 이 스킬의 신뢰 모델은 이에 적합하지 않습니다.
- 일반 예약 웹사이트 — 필요한 경우 백엔드 시점을 활용하되, 키오스크 메커니즘(키보드, 타이머, 터치 강화)은 여기서 불필요한 부하입니다.
아키텍처
KioskProvider (순수 리듀서: 단계, 선택, 장바구니, sessionId)
│ props: 사전 조각 훅: 가격책정 · 장비 · 가용성
화면: 서비스→날짜→시간→참가자→장비→요약→확인
│ └─ 사이드 플로우: 예약조회 → 예약수정
▼ 하나의 fetch 래퍼 (디바이스 키 + 오프라인 기본 URL 페일오버)
/api/kiosk/booking/{create,lookup,add-items,edit} ← 모든 신뢰 결정
▼ KioskBackend 시점: 서버 가격책정 · 프로모션 · 재고 잠금(TTL 15분)
트랜잭션 용량 재확인 · 카운터/온라인 결제 · 웹훅
실시간: location.lastBookingChangeAt → 자동 재조회 → REFRESH_SLOT
중요 사실
- 사용자 탭은 탐색; 백그라운드 갱신은 절대 탐색하지 않음.
SET_SLOTvsREFRESH_SLOT은 실시간 가용성과 구매 중인 요약 화면에서 고객을 순간이동시키는 것의 차이입니다. - 서버가 모든 가격을 책정합니다. 금전 관련 데이터는 인바운드로 전송되지 않습니다. 클라이언트 총액은 미리보기일 뿐입니다.
- 카운터 결제는 복원 경로 — 예약은
PENDING_COUNTER_PAYMENT로 확인되며, 루프에 결제 통합이 없습니다. 키오스크에서 온라인 결제는 고객이 스캔하는 QR을 의미하며, 키오스크를 다른 곳으로 이동시키지 않습니다. - 모든 텍스트 입력은 온스크린 키보드를 열고
inputMode="none"을 설정합니다. 둘 중 하나 없이는 사용할 수 없는 화면입니다. - 재고 잠금은 TTL로 예약되며(15분) 모든 실패 경로에서 해제됩니다 — 청소 작업은 안전망일 뿐이며 메커니즘이 아닙니다.
- 클라이언트 sessionId는 멱등성 키입니다. 더블탭, 재시도, 응답 손실: 최대 하나의 예약만 발생합니다.
엄격한 규칙
키오스크 세션 상태를 절대 영구 저장하지 마십시오. 다음 고객이 이전 고객의 이름을 보지 않아야 합니다. 인메모리 + 120초 비활성 리셋 + 30초 확인 리셋, 하드웨어에서 검증됨.
가격, 용량 또는 프로모션 유효성에 대해 클라이언트를 절대 신뢰하지 마십시오. 서버가 모든 금액을 계산하며, 유효하지 않은 프로모션에는
PROMO_INVALID로 응답합니다. 키오스크에 있는 고객은 금액을 확인할 방법이 없습니다.
모든 키오스크 요청에 대해 하나의 fetch 래퍼를 사용하십시오. 래퍼는 디바이스 헤더, 오류 봉투 및 페일오버 기본 URL을 전달하므로 인증, 오류 및 오프라인 페일오버가 모든 요청에 한 번에 적용됩니다.
디바이스 키가 구성된 경우, 헤더가 없으면 401입니다. 잘못된 키만 거부하는 것은 인증이 없는 것과 같습니다.
재고 잠금을 획득한 후 모든 실패 경로에서 해제하십시오. 잠금을 유지하는 409는 전체 TTL 동안 재고를 고정시킵니다.
빠른 시작
- state-machine.md를 읽고 리듀서와 프로바이더를 구현한 후, 테스트 스위트를 실행하십시오.
- screens.md의 계약에 따라 호스트의 디자인 시스템으로 화면을 구축하십시오 — 키보드, 타이머, 터치 강화.
- api-contract.md의 라우트를 booking-backend.md 시점 위에 구현하십시오.
- realtime-offline.md에 따라 신선도 및 오프라인을 연결하십시오.
- operations.md에 따라 배포 및 스모크 테스트를 수행하십시오.
적응 계약
| 시점 | 이 스킬이 제공하는 것 | 호스트가 제공해야 하는 것 |
|---|---|---|
| 도메인 엔티티 | 서비스/스테이션/장비/소모품 + 이름 변경 표 | 자체 용어(코트, 카트, 베이…) |
| 테넌트 범위 | 키오스크 URL의 locationId, 서버 검증 | 자체 위치/사이트 모델 |
| 디바이스 인증 | x-kiosk-api-key 계약 | 키 관리, 선택적 디바이스별 토큰 |
| 데이터 접근 | KioskBackend 인터페이스; Firestore 참조 + SQL 스케치 | 자체 ORM/SDK |
| 결제 | 카운터 상태 + checkout-session/webhook 계약 | Stripe 또는 동등한 것 |
| 실시간 | SubscribeFreshness 인터페이스 | Firestore/Supabase/SSE/폴링 |
| 오프라인 | NetworkStatus 계약 | LAN 서버(island-mode-server) 또는 없음 |
| UI 기본 요소 | 구조, 상태, 상호작용 규칙 | 버튼, 대화상자, 스피너, 디자인 |
| 스타일링 | 레이아웃 의도, 터치 대상, 하나의 --primary 토큰 | 자체 디자인 시스템 |
| 문자열 | 사전 키 트리, 모든 키 필수 | 자체 i18n 파일, 모든 로케일 |
| 유효성 검사 | 의존성 없는 가드 | 자체 스키마 라이브러리(있는 경우) |
참고 디렉토리
| 시나리오 | 트리거 키워드 | 참고 |
|---|---|---|
| 상태, 단계, 리듀서, 세션, 테스트 | step, reducer, GO_BACK, REFRESH_SLOT, sessionId, reset | state-machine.md |
| 화면, 키보드, 타이머, 터치, i18n 키 | 온스크린 키보드, 비활성, 카운트다운, QR, 터치 대상, 사전 | screens.md |
| 라우트, 인증, 유효성 검사, 멱등성, 오류 | x-kiosk-api-key, 401, 409, idempotency, PROMO_INVALID, lookup, PII | api-contract.md |
| 용량, 재고, 결제, 웹훅, 크론 | transaction, stock lock, TTL, PENDING_COUNTER_PAYMENT, webhook, refund | booking-backend.md |
| 실시간 갱신, 캐싱, 오프라인 페일오버 | lastBookingChangeAt, onSnapshot, s-maxage, offline banner, LAN, health poll | realtime-offline.md |
| 배포, 게이팅, 운영자 표면 | Edge --kiosk, assigned access, heartbeat, feature gate, smoke test | operations.md |
| 템플릿이 이전 구현과 다른 이유 | audit, ledger, kept deliberately, added | provenance.md |