UniPlan の
技術構成と仕組み
このアプリは サーバーを自前で持たない「サーバーレス(BaaS)」構成です。 フロントエンドは Cloudflare から配信され、ブラウザが直接 Supabase の API と通信し、 安全性は データベース側のルール(RLS) が担保します。
3層アーキテクチャ
役割を「見た目/ロジック/データ」の3つに分けて整理します。上2層はすべてブラウザ内で動きます。
プレゼンテーション層
見た目・UI(ブラウザ内)
- ▪Next.js 16 (App Router) / React 19
- ▪TypeScript + Tailwind CSS v4
- ▪画面・コンポーネント(科目カード, 時間割, モーダル)
- ▪Zustand(画面状態の保持)
アプリケーション層
ロジック(同じくブラウザ内)
- ▪supabase-js クライアント
- ▪認証フロー(OTP)lib/…・LoginModal
- ▪同期エンジン lib/sync.ts
- ▪レビュー処理・入力検証 lib/reviews.ts
データ層
サーバー側(Supabase / Resend)
- ▪PostgreSQL(reviews / user_state / review_reports)
- ▪Row Level Security(行ごとの閲覧/編集ルール)
- ▪Supabase Auth(ユーザー・JWT発行)
- ▪Resend(メール送信 SMTP)
システム全体図
登場人物は4つ。ブラウザを中心に、配信元(Cloudflare)・データ基盤(Supabase)・メール(Resend)がつながります。
① 画面の配信(静的ファイル)
Cloudflare Pages
CDN・HTML/JS/CSS
ユーザーのブラウザ
cistan.bibi-bus.com
② データ・認証のやりとり(API)
ブラウザ
supabase-js
Supabase
Auth + Postgres + RLS
Resend
メール送信
※ 自前のサーバー(バックエンド)は無し。ブラウザが Supabase の自動生成APIを直接叩きます。
データフロー(API のやりとり)
ログイン(OTP 6桁)
- 1ブラウザSupabase AuthPOST /auth/v1/otp
signInWithOtp(email) を呼ぶ
- 2SupabaseGoTrue
ユーザー作成/特定し、6桁トークンを生成
- 3SupabaseResendSMTP
メール送信を依頼
- 4ResendユーザーEmail
6桁コードが届く(テンプレ {{ .Token }})
- 5ブラウザSupabase AuthPOST /auth/v1/verify
verifyOtp(code) で照合
- 6SupabaseブラウザJWT 発行
アクセストークンを返す → localStorage(uniplan-auth) に保存
データの置き場所と移動
「どの変数・データが、どこに置かれ、どう動くか」を一覧にしました。
環境変数(ビルド時に埋め込み)
NEXT_PUBLIC_SUPABASE_URLSupabaseの接続先URLNEXT_PUBLIC_SUPABASE_ANON_KEY公開キー(RLSで保護されるため公開OK)クライアントの状態(Zustand → localStorage)
useProfile名前・学科・学年・学期useSimulationcompleted / enrolled(科目ID配列)useTimetableschedule({ '月-1': courseId })useTaskstasks(締切・提出物)useAuthログイン状態・メール・isPhotonuseSyncPrefsどの項目を同期するかサーバーのデータ(PostgreSQL テーブル)
reviews授業レビュー(評価・本文・受講年)user_state同期される個人データ(jsonb)review_reports通報(一般非公開)auth.usersアカウント(Supabase管理)認証トークン
JWT (access_token)メールに含まれる本人情報。RLSの判定に使用localStorage: uniplan-authブラウザに保存されるセッションセキュリティの考え方
RLS(行レベルセキュリティ)
「@photon の人だけレビュー閲覧」をDB側で強制。クライアントを改ざんしても他人のデータは取れません。
公開キーは安全
anonキーはブラウザに露出する前提の鍵。実際の保護は全てRLSが行うため公開してOK。秘密鍵(service_role)は使いません。
本人のみ書き込み
user_state は auth.uid() = user_id のときだけ読み書き可。他人のデータは触れません。
投稿の品質担保
1人1科目1件・50文字以上・1日10件まで(DB制約とトリガー)。通報機能あり。
用語集
この図はこのアプリの実装そのものに対応しています(コードと一致)。