System Overview
1. 목표와 원칙
1인 개발이 유지 가능한 단순 구조를 유지한다.
상태 확정(수량 차감/만료/제재)은 서버(=DB 트랜잭션)에서만 수행한다.
클라이언트는 “표시와 요청”만 담당한다.
예약/체크인은 당일 시간 슬롯에 한정한다. (ADR-P-004, ADR-DS-003)
2. 시스템 구성요소
클라이언트
Mobile App (React Native + Expo)
User Web (Next.js, 조회 전용)
Merchant Web (Next.js, 발행/통계/구독 관리)
백엔드(서버리스)
Supabase
Postgres (데이터 저장소, 단일 진실원)
RLS (권한 제어)
RPC (도메인 로직 및 트랜잭션)
Auth (소셜 + 매직링크)
Stripe (가맹점 구독 결제)
Cloudflare Pages (웹 배포)
3. 책임 분리(경계)
클라이언트 책임
UI 렌더링(시간 중심 위계, ADR-U-004)
사용자 입력 및 1차 유효성 검사
API 호출 및 결과 표시
타이머 UI(서버 expiresAt 기반 표시)
네트워크 오류 처리 및 재시도(제한적)
서버(DB/RPC) 책임
상태 전이 확정(Reservation/Slot)
동시성 제어(수량 차감 트랜잭션, ADR-DS-002)
발행 제한(월 발행/동시 노출/재발행, ADR-DS-004)
만료 확정(ADR-DS-001, ADR-DS-003)
제재 계산 및 적용(ADR-DS-005)
무결성 보장 및 감사 로그(ADR-DS-006)
4. 데이터 흐름(요약)
사용자 탐색
Client → public_get_nearby_slots RPC → Slot summaries 반환확보(Reservation 생성)
Client → rpc_create_reservation → RESERVED 생성(단, expiresAt=slot.startAt+grace)사용 확정(REDEEMED 확정)
Client → rpc_redeem → 조건 통과 시 REDEEMED + remainingQuantity 감소만료(EXPIRED 확정)
Server read-time 또는 checkin-time에서 expiresAt 경과 시 EXPIRED 확정제재(Penalty)
EXPIRED 확정 시 penalty 평가 → penalty 레코드 upsert가맹점 구독
Stripe → webhook → Supabase subscriptions/merchants 플랜 상태 동기화(ADR-W-013)
5. 신뢰 모델(Trust Model)
서버(DB)만 상태를 확정한다.
클라이언트의 시간/위치/요청 시각은 참고값이며 최종 판단은 serverNow 기준이다.
공개 웹은 read-only API만 사용한다(ADR-W-011).
6. 운영팀(비개발자) 지원 포인트
가맹점 비활성화, 슬롯 강제 종료, 제재 해제 등은 “운영 테이블 + admin_actions”로 처리한다.
모든 수동 조치는 audit log에 남긴다(ADR-DS-006).
7. 관련 문서
ADR-DS-001~007, ADR-DS-003(v2), ADR-DS-004(v3)
ADR-U-004~008