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. 데이터 흐름(요약)

  1. 사용자 탐색
    Client → public_get_nearby_slots RPC → Slot summaries 반환

  2. 확보(Reservation 생성)
    Client → rpc_create_reservation → RESERVED 생성(단, expiresAt=slot.startAt+grace)

  3. 사용 확정(REDEEMED 확정)
    Client → rpc_redeem → 조건 통과 시 REDEEMED + remainingQuantity 감소

  4. 만료(EXPIRED 확정)
    Server read-time 또는 checkin-time에서 expiresAt 경과 시 EXPIRED 확정

  5. 제재(Penalty)
    EXPIRED 확정 시 penalty 평가 → penalty 레코드 upsert

  6. 가맹점 구독
    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. 관련 문서