AIとサービスを作る #03 ——実装編②:全画面実装からSupabase連携まで
前回のフェーズ0「環境構築」では、Next.jsのプロジェクトを立ち上げてGitHubにコードをアップロードするところまで進みました。今回はその続き、フェーズ1「画面の骨格を作る」とフェーズ2「データをつなぐ」 の記録です。
コードを書き始めたら想像以上にスムーズで、1日でかなりのところまで進みました。ただ途中でデザインの壁にぶつかったり、Supabaseのハマりポイントがあったりと、なかなか濃い一日でした。
📋 今回やったこと
# フェーズ 内容
1️⃣ フェーズ1 全7画面をダミーデータで実装
2️⃣ デザイン改善 カラーシステムを全面見直し
3️⃣ フェーズ2 Supabase Auth・DB・Realtime連携
4️⃣ 仕上げ 画面遷移の整備
📱 フェーズ1:全7画面の骨格を作る
Next.js + Tailwind CSSで、StudyBuddyの画面をすべてダミーデータで実装しました。
🔓 未ログイン層(4画面)
画面 URL 役割 🏠 ランディング / サービス紹介・CTA 📝 新規登録 /register アカウント作成 🔑 ログイン /login 認証 👤 プロフィール設定 /profile-setup 資格・時間帯・頻度の入力
🔒 ログイン済み層(4画面)
画面 URL 役割 🏠 ホーム /home 学習ダッシュボード 📊 進捗記録 /progress タイマー・グラフ 🤝 マッチング /matching パートナー検索・申請 💬 チャット /chat リアルタイムメッセージ
ファイル構成は Next.js の App Router の規約に従い、ルートグループで整理しました。
app/
page.tsx ← 🏠 ランディング
(auth)/ ← 🔓 未ログイン層
login/page.tsx
register/page.tsx
profile-setup/page.tsx
(app)/ ← 🔒 ログイン済み層
home/page.tsx
progress/page.tsx
matching/page.tsx
chat/page.tsx
components/
BottomNav.tsx ← 🧭 下部ナビゲーション
💡 (auth) や (app) という丸括弧のフォルダは URLに影響しないルートグループ です。ログイン前・後の画面を整理するために使っています。
まず完成したのがランディングページ。サービスの価値を伝える3つの特徴カードと「無料で始める」「ログイン」の2つのCTAボタンを配置しました。

🎨 デザインの壁:「緑の世界に全振り」問題
画面を作り終えてブラウザで確認したとき、最初のデザインはこんな感じでした。
❌ 改善前の状態
Webデザイナー視点でのフィードバックをもとに、カラーシステムを全面的に見直しました。
🔧 改善ポイント6つ
① 明度差をつけてカード・背景を分離
改善前:背景もカードも似たような緑 → のっぺり
改善後:
背景 #0F1A14(かなり暗い緑)
カード #1E2E26(背景より+30%明度)
② テキストを3段階の階層に
役割 色 用途 見出し #FFFFFF 白 タイトル・重要数値 本文 #E8EDEB オフホワイト 説明文 補足 #A7B1AC グレー ラベル・日時
③ 数値を「白×太字×大きめ」で主役に
StudyBuddyは進捗が主役のアプリなので、学習時間などの数字はすべて白・fontWeight800で表示。単位(h・分)は小さくして差をつけました。
④ アクセントカラーを役割ごとに使い分ける
🎨 色 カラーコード 用途 🟢 グリーン #3FAF7F CTAボタン・アクティブ状態のみ 🩵 ミント #7FE7C4 先週比プラス・応援メッセージ 🟡 ゴールド #D9A441 連続達成日数(モチベーション)
⑤ ボーダー・影を「緑→白透過・黒ベース」に
改善前:border: 1px solid #1D9E7540 ← 緑の半透明
改善後:border: 1px solid rgba(255,255,255,0.08) ← 白の半透明
box-shadow: 0 2px 16px rgba(0,0,0,0.35) ← 黒ベースで立体感
⑥ BottomNavアイコンをストローク(線)に
塗りつぶしアイコンから stroke 2px の線アイコンに変えて、アクティブ時だけグリーンに変化するようにしました。
✅ 学んだこと: デザインは「色の数を増やす」のではなく「明度差・白の使い方・アクセント1色で整える」のが鍵
改善後のホーム画面です。今週の学習時間・連続達成日数・今週vs先週グラフが並びます。データが蓄積されるほど情報量が増える設計になっています。

🔗 フェーズ2:Supabaseとつなぐ
画面の骨格が完成したので、いよいよバックエンドを接続します。
🔑 Supabase Auth でログイン実装
Supabase の @supabase/ssr パッケージを使って、Next.js App Router に対応した認証を実装しました。ブラウザ用とサーバー用でクライアントを分けるのがポイントです。
// lib/supabase/client.ts(ブラウザ用)
import { createBrowserClient } from '@supabase/ssr'
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}
認証ガードは proxy.ts(Next.js 16 から middleware.ts が改名)で実装。未ログインのままホーム画面にアクセスしようとすると、自動でログイン画面にリダイレクトされます。
🔓 未ログイン → /home にアクセス → 🔀 自動リダイレクト → /login
🔒 ログイン済み → /login にアクセス → 🔀 自動リダイレクト → /home

🗄️ DBテーブルの作成
引き継ぎ資料で設計していた4テーブルを Supabase の SQL Editor で作成しました。
テーブル構成:
テーブル 役割 👤 users プロフィール情報 🤝 matches ペア管理(pending / active / dissolved) 📊 study_logs 学習記録(duration_min・studied_at) 💬 messages チャットメッセージ
-- 学習記録テーブル
CREATE TABLE study_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
duration_min INTEGER NOT NULL,
studied_at TIMESTAMPTZ DEFAULT NOW()
);
あわせて Row Level Security(RLS) も設定。自分のデータしか読み書きできないようにしつつ、マッチング候補の表示やパートナーのログ参照など、必要な例外もポリシーで定義しました。
🛡️ RLSとは: Supabaseの行レベルセキュリティ機能。「誰がどのデータを見られるか」をSQL文で定義できます。これにより、万が一APIキーが漏れても他ユーザーのデータは守られます。
SQL実行後、ダッシュボードのTable Editorで4つのテーブルが正常に作成されているのを確認しました。

📂 ビジネスロジックを lib/ に切り出す
フェーズ4でのAPI分離を見越して、ロジックはUIから独立したファイルに書きました。
lib/
progress.ts ← ⏱️ 連続日数・週次集計・今週vs先週の計算
matching.ts ← 🤝 候補検索・スコアリング・申請処理
💡 なぜ分けるの? ロジックをUIに直接書くと、フェーズ4でAPIサーバーに切り出す際に大改修が必要になります。最初から分けておくことで、将来の作業がずっと楽になります。
進捗記録画面ではタイマーで計測した学習時間をそのまま記録でき、棒グラフで今週vs先週を比較できます。

⚡ チャットを Realtime で同期
チャット画面は Supabase Realtime を使って、相手のメッセージがリアルタイムで届くように実装しました。
const channel = supabase
.channel(`chat:${match.id}`)
.on('postgres_changes', {
event: 'INSERT',
schema: 'public',
table: 'messages',
filter: `match_id=eq.${match.id}`,
}, (payload) => {
setMessages((prev) => [...prev, payload.new as Message])
})
.subscribe()
相手がメッセージを送るとDBにINSERTが走り、それをRealtimeが検知してリアルタイムで画面に表示されます。マッチングが成立するとこの画面でメッセージのやり取りが始まります。現時点ではまだパートナーがいないため、下のような案内画面が表示されます。

⚠️ ハマりポイント:メール確認が必要でログインできない
新規登録してプロフィール設定ページを開くと、「ログインが必要です」というエラーが表示されてしまいました。
原因: Supabaseのメール確認機能がデフォルトでONになっており、登録直後はまだメールを確認していないのでログイン状態にならないのです。
✅ 解決方法:
Supabase ダッシュボード
→ Authentication
→ Sign In / Providers
→ Email の「Confirm email」をOFF
⚠️ 注意: 開発中はOFFで問題ありませんが、本番リリース時には必ずONに戻してメール確認フローを実装してください。
🧭 画面遷移の整備
ひととおり実装が終わったところで、画面遷移の抜け漏れに気づきました。
追加した遷移:
画面 追加した遷移 🔑 ログイン ロゴクリック → 🏠 トップページ 📝 新規登録 ロゴクリック → 🏠 トップページ 👤 プロフィール設定 ◀️「← ログインに戻る」リンク 🏠 ホーム 🚪 右上に「ログアウト」ボタン
特にログアウト手段は最初に実装し忘れていて、ホーム画面から抜け出せない状態になっていました。
💡 こういう「動いてから気づく抜け漏れ」はコードを書き始めないとわからない。設計だけでは見えない問題が、実装すると見えてくる。
マッチング画面では、フィルター(活動時間帯・報告頻度)でパートナー候補を絞り込んで申請できます。

💭 振り返り
今日は一日でフェーズ1とフェーズ2がほぼ完成しました。設計をきちんと済ませていたおかげで、実装の迷いが少なかったと思います。
✅ うまくいったこと
設計段階でファイル構成を決めていたので、迷わず実装できた
ビジネスロジックを最初からlib/に分けたので、コードが読みやすい
📚 学んだこと
デザインは「色の数」ではなく「明度差と情報の階層」が大事
Supabaseの認証は開発時と本番時で設定が変わる点に注意
RLSの設定は面倒だが、セキュリティの土台として最初にやるべき
次回 #04 はフェーズ3「磨いて公開する」に入ります。PWA設定・Webプッシュ通知・Vercelへのデプロイを進めて、いよいよ MVP完成 を目指します。
