AIと一緒にCMSを作る:4つのサービスの土台作りから記事CRUDまで

AI体験談・その他
4つのサービスを組み合わせてるイラスト
第1回では、CMSとは何か、なぜCMSを作ることになったのかを書きました。 今回は実際に手を動かし始めたところからの話です。4つのサービスの土台作り、ログイン機能の実装、そして記事の作成・編集・削除ができるようになるまでを書いています。

土台を整える:Next.js、GitHub、Supabase、Vercel

要求定義書ができたので、いよいよ環境を作り始めます。
ただ、ここでいう「環境を作る」というのは、アプリのコードを書くことではありません。コードを書く前に、4つのサービスを準備する必要がありました。

まず、それぞれが何をしているのかを簡単に説明します。

Next.jsは、アプリの本体を作るためのフレームワークです。
ログイン画面や記事の編集画面など、画面に表示されるものはすべてNext.jsで作ります。Insight Notesのときにも使ったので、これは馴染みがありました。

GitHubは、コードの保管庫です。自分のパソコンで書いたコードをGitHubに保存(push)しておくと、パソコンが壊れてもコードが消えません。
もうひとつ大事な役割として、GitHubにpushしたコードが、後述するVercelに自動で反映される仕組みの起点になっています。

Supabaseは、データベースと認証機能を提供するサービスです。
記事のタイトルや本文、公開・非公開の状態、ログインのためのユーザー情報など、アプリが扱うデータはすべてSupabaseに保存されます。
Insight Notesでも使っていたので、ダッシュボードの操作にはある程度慣れていました。

Vercelは、作ったアプリをインターネット上に公開するためのサービスです。
自分のパソコンではlocalhost:3000というアドレスで動いているアプリを、誰でもアクセスできるURLで見られるようにしてくれます。
GitHubと連携しているので、コードをGitHubにpushするだけで、Vercel側も自動で更新されます。

この4つの関係を整理すると、こうなります。
Next.jsでアプリの画面を作り、Supabaseにデータを保存して、GitHubでコードを管理し、Vercelで公開する。
それぞれの役割がはっきり分かれていて、全部がつながって初めてアプリとして動きます。

セットアップの手順はClaudeが順番に教えてくれました。
ターミナルでNext.jsのプロジェクトを作成して、GitHubにリポジトリを作って、Supabaseで新しいプロジェクトを作って、最後にVercelでデプロイする。
Insight Notesのときに一度やっている流れなので、ここは比較的スムーズに進みました。

ひとつだけ気をつけたのは、Supabaseのプロジェクトをどこに作るかです。
自分のSupabaseには2つの「組織(Organization)」があって、有料プランの組織にはBookConnectとMyFavoritePlaceという既存のプロジェクトが入っています。
無料プランの組織にはInsight Notesが1つだけ入っていて、無料プランでは1組織につき2プロジェクトまで作れるので、CMSもどきはInsight Notesと同じ無料の組織に入れました。

Next.jsでプロジェクトが立ち上がったスクショ

Vercelのデプロイが成功して「Congratulations!」の画面が出たとき、Next.jsのデフォルト画面がインターネット上に表示されているのを確認できました。まだ何の機能もない真っ白なアプリですが、ここまでくれば土台は完成です。

ログインできた

土台が整ったので、最初に作る機能はログインでした。

個人用のツールなので、会員登録の画面は作っていません。
Supabaseのダッシュボードから自分のアカウントを1つだけ手動で登録して、そのアカウントでログインできるようにするだけです。
自分しか使わないのだから、これで十分でした。

ログインの仕組みには、「Middleware(ミドルウェア)」という概念が登場しました。
Claudeはこれをホテルのフロントに例えて説明してくれました。
ホテルには各階に客室がありますが、どの部屋に行くにもまずフロントを通りますよね。
フロントで宿泊客かどうかを確認して、予約がある人はそのまま部屋に通し、予約のない人には「予約ページに行ってください」と案内する。
Middlewareはまさにこの役割で、どのページにアクセスしても、まず「この人はログイン済みか?」を確認して、ログインしていなければログイン画面に飛ばす仕組みです。

仕組みはわかったものの、この時点ではMiddlewareのコードの意味を全部理解できていたわけではありません。
Claudeの説明を聞いて「なるほど、番人みたいなものね」というレベルの理解で先に進みました。
コードの1行1行が何をしているかは、正直よくわからない部分もありました。
ただ、「何のために存在するのか」はわかっていたので、ここで立ち止まるよりも実際に動かしてみることを優先しました。

ログイン画面のスクショ
ログイン後のダッシュボードのスクショ

ログインフォームにメールアドレスとパスワードを入力して、ダッシュボードに遷移したときは素直にうれしかったです。Vercelにデプロイして、本番のURLでも同じ動作が確認できました。

記事を「書いて、直して、消す」

ログインができたので、次はCMSの心臓部、記事のCRUD機能を作りました。
CRUDというのは、Create(作成)、Read(読み取り)、Update(更新)、Delete(削除)の頭文字を並べたもので、データを扱うアプリの基本操作をまとめた言葉です。

実装の順番は、まず記事の一覧表示から始めて、次に新規作成、それから編集、最後に削除という流れでした。
この順番にも理由があって、作成機能がないと編集するデータがそもそも存在しないし、一覧表示がないとどの記事を編集するか選べない。依存関係を考えると、この順番が一番スムーズに動作確認できるとClaudeに説明されました。

記事の一覧ページを作ったとき、最初に表示されたのは「記事がありません」という空っぽの画面でした。
まだ1つも記事を作っていないのだから当然です。そこからテスト投稿を作って、一覧に記事のタイトルが表示されたときは、「お、動いてる」とちょっとした手応えがありました。

記事がない時のダッシュボードのスクショ
投稿フォームのスクショ
1件投稿後のダッシュボードのスクショ

編集画面では、タイトル、本文、公開状態(下書きか公開済みか)を変更できるようにしました。
削除ボタンも編集画面に置いて、押すと確認ダイアログが出る仕様です。

このあたりの実装は、ログインのときほど苦労しませんでした。
ログインのときに学んだServer ActionやSupabaseへの問い合わせの仕組みがそのまま使えたからです。
でも、Claudeの説明が飛び飛びになる場面は相変わらずで、「なぜこのファイルから先に作るのか」という順番の説明が抜けていて混乱したこともありました。
この問題は、次の記事で書く場面でもっと大きくなっていきます。

次の記事では、Claudeとのコミュニケーションで壁にぶつかった話と、そこから生まれたルール、そしてMarkdownエディタと画像アップロード機能の実装について書いています。

感想やコメントをいただけるとうれしいです。