はじめに設定しましょう

1
学科・年度
2
学年・学期
3
取得済み科目

学科

1〜2年生はまだわからなければ「未選択」のままでOKです

入学年度

UUniPlan
TECH / ARCHITECTURE

UniPlan の
技術構成と仕組み

このアプリは サーバーを自前で持たない「サーバーレス(BaaS)」構成です。 フロントエンドは Cloudflare から配信され、ブラウザが直接 Supabase の API と通信し、 安全性は データベース側のルール(RLS) が担保します。

01

3層アーキテクチャ

役割を「見た目/ロジック/データ」の3つに分けて整理します。上2層はすべてブラウザ内で動きます。

プレゼンテーション層

見た目・UI(ブラウザ内)

1
  • Next.js 16 (App Router) / React 19
  • TypeScript + Tailwind CSS v4
  • 画面・コンポーネント(科目カード, 時間割, モーダル)
  • Zustand(画面状態の保持)

アプリケーション層

ロジック(同じくブラウザ内)

2
  • supabase-js クライアント
  • 認証フロー(OTP)lib/…・LoginModal
  • 同期エンジン lib/sync.ts
  • レビュー処理・入力検証 lib/reviews.ts

データ層

サーバー側(Supabase / Resend)

3
  • PostgreSQL(reviews / user_state / review_reports)
  • Row Level Security(行ごとの閲覧/編集ルール)
  • Supabase Auth(ユーザー・JWT発行)
  • Resend(メール送信 SMTP)
02

システム全体図

登場人物は4つ。ブラウザを中心に、配信元(Cloudflare)・データ基盤(Supabase)・メール(Resend)がつながります。

① 画面の配信(静的ファイル)

Cloudflare Pages

CDN・HTML/JS/CSS

HTTPS配信

ユーザーのブラウザ

cistan.bibi-bus.com

② データ・認証のやりとり(API)

ブラウザ

supabase-js

REST / Auth API

Supabase

Auth + Postgres + RLS

SMTP

Resend

メール送信

※ 自前のサーバー(バックエンド)は無し。ブラウザが Supabase の自動生成APIを直接叩きます。

03

データフロー(API のやりとり)

ログイン(OTP 6桁)

  1. 1
    ブラウザSupabase AuthPOST /auth/v1/otp

    signInWithOtp(email) を呼ぶ

  2. 2
    SupabaseGoTrue

    ユーザー作成/特定し、6桁トークンを生成

  3. 3
    SupabaseResendSMTP

    メール送信を依頼

  4. 4
    ResendユーザーEmail

    6桁コードが届く(テンプレ {{ .Token }})

  5. 5
    ブラウザSupabase AuthPOST /auth/v1/verify

    verifyOtp(code) で照合

  6. 6
    SupabaseブラウザJWT 発行

    アクセストークンを返す → localStorage(uniplan-auth) に保存

04

データの置き場所と移動

「どの変数・データが、どこに置かれ、どう動くか」を一覧にしました。

環境変数(ビルド時に埋め込み)

NEXT_PUBLIC_SUPABASE_URLSupabaseの接続先URL
NEXT_PUBLIC_SUPABASE_ANON_KEY公開キー(RLSで保護されるため公開OK)

クライアントの状態(Zustand → localStorage)

useProfile名前・学科・学年・学期
useSimulationcompleted / enrolled(科目ID配列)
useTimetableschedule({ '月-1': courseId })
useTaskstasks(締切・提出物)
useAuthログイン状態・メール・isPhoton
useSyncPrefsどの項目を同期するか

サーバーのデータ(PostgreSQL テーブル)

reviews授業レビュー(評価・本文・受講年)
user_state同期される個人データ(jsonb)
review_reports通報(一般非公開)
auth.usersアカウント(Supabase管理)

認証トークン

JWT (access_token)メールに含まれる本人情報。RLSの判定に使用
localStorage: uniplan-authブラウザに保存されるセッション
05

セキュリティの考え方

RLS(行レベルセキュリティ)

「@photon の人だけレビュー閲覧」をDB側で強制。クライアントを改ざんしても他人のデータは取れません。

公開キーは安全

anonキーはブラウザに露出する前提の鍵。実際の保護は全てRLSが行うため公開してOK。秘密鍵(service_role)は使いません。

本人のみ書き込み

user_state は auth.uid() = user_id のときだけ読み書き可。他人のデータは触れません。

投稿の品質担保

1人1科目1件・50文字以上・1日10件まで(DB制約とトリガー)。通報機能あり。

06

用語集

フロントエンドユーザーが直接触れる画面側。ここではブラウザで動くNext.js/React。
バックエンドデータ保存・認証などの裏側。ここでは自前で作らずSupabaseが担当。
プレゼンテーション層見た目・UI担当。コンポーネントや画面。
アプリケーション層処理・ロジック担当。認証・同期・検証など。
データ層データの保管・ルール担当。Postgres + RLS。
APIプログラム同士の窓口。ここでは Supabase の REST/Auth エンドポイント。
BaaSBackend as a Service。サーバーを書かずに認証・DBを使える仕組み(Supabase)。
CDN世界中に配信する仕組み。Cloudflareが静的ファイルを高速配信。
RLSRow Level Security。行ごとに「誰が見れる/書けるか」をDBで制御。
JWTログイン情報を含む署名付きトークン。本人確認に使う。
SMTPメール送信のプロトコル。ResendがOTPメールを配送。
環境変数URLや鍵などを設定として外から渡す値。NEXT_PUBLIC_…。
静的エクスポート事前にHTML化して配信する方式。Cloudflare Pages向け。
状態管理画面が持つデータの保持。ここではZustand + localStorage。

この図はこのアプリの実装そのものに対応しています(コードと一致)。