🔰

【AI駆動開発】Spec KitでWeb初心者がアプリを作ることは可能か?

に公開

今回のテーマ

こんにちは。aoyagihです。

最近会社の業務でWebアプリを作る必要が出てきました。
しかし、私自身はスマホアプリの開発経験はあるものの、Web開発の経験はほとんどなく悩んでいたところ、GitHubが公開した「Spec Kit」というツールを知りました。
https://github.com/github/spec-kit

AIと対話しながら「仕様書」を固め、そこから開発を進めていく「仕様駆動開発(Spec-Driven Development)」を支援するものらしいのですが、これを使ってWeb初心者がアプリを作ったりすることは可能なのでしょうか?

今回は、私の趣味が飲食店巡りということで、「食べログ」や「グルメマップ」のような、「自分が行った飲食店を記録して閲覧できるWebアプリ」を作れるか試してみました。

Spec Kitについて

まず、Spec Kitの使い方ですが、以下の記事が参考になりました。

https://zenn.dev/acntechjp/articles/1d35658b0114b6

この記事によると、Spec KitはVSCodeのCopilotチャット等で使えるコマンド群であり、何か作るためにまずは以下の2つのコマンドで「仕様」と「技術計画」を入力する必要があるようです。

  • /specify: 仕様の作成
  • /plan: 技術計画の作成

では、それぞれ何を入力すればよいのでしょうか。以下のブログにその説明がありました。

https://github.blog/ai-and-ml/generative-ai/spec-driven-development-with-ai-get-started-with-a-new-open-source-toolkit/

Specify (仕様)

Specify: You provide a high-level description of what you’re building and why, and the coding agent generates a detailed specification. This isn’t about technical stacks or app design. It’s about user journeys, experiences, and what success looks like. Who will use this? What problem does it solve for them? How will they interact with it? What outcomes matter?
(訳:構築するもの(What)とその理由(Why)の概要を提供すると、コーディングエージェントが詳細な仕様を生成します。これは技術スタックやアプリ設計に関するものではありません。ユーザージャーニー、体験、そして何が成功とみなされるかに関するものです。誰が使うのか?どんな問題を解決するのか?どのように操作するのか?どの成果が重要か?)

つまり、/specify では「技術的な話は抜きにして、誰が・なぜ・何をするためのアプリなのか」を明確にするフェーズのようです。

Plan (技術計画)

Plan: Now you get technical. In this phase, you provide the coding agent with your desired stack, architecture, and constraints, and the coding agent generates a comprehensive technical plan. ... If you have performance targets you need to hit … all of that goes here.
(訳:ここから技術的な話になります。このフェーズでは、希望するスタック、アーキテクチャ、制約をコーディングエージェントに提供し、エージェントが包括的な技術計画を生成します。 ... パフォーマンス目標がある場合も、すべてここに含めます。)

/plan は、/specify で決めた「何を作るか」を実現するために、「どういう技術(スタック、アーキテクチャ)で、どんな制約(ルール)を守って作るか」を明確にするフェーズのようです。

この2つを入力すれば、開発の土台ができあがることが分かりました。

LLMによるコマンド入力内容の壁打ち

とはいえ、Web初心者の私にとって、いきなり「仕様」や「技術計画」をまとめるのは困難です。
そこで、まずはGemini 2.5 Proに入力内容の壁打ち相手になってもらうことにしました。

Spec Kitの /specify/plan に入力する内容を整理してもらうため、具体的には以下のようなプロンプトをGeminiに投げました。

Geminiに最初に投げたプロンプト

行った飲食店を記録するWebページを作りたい。
Webについて詳しくないので、要件を固めるための壁打ちをさせて。
最終的に以下の2つをまとめて欲しい。
・仕様
何を構築し、なぜ構築するのか
・技術的側面
スタック、アーキテクチャ、制約条件等

現在考えているやりたいことは以下の通りであるが、疑問点があれば確認して。
<店の登録>
・最大100件まで登録できる
・行った店で撮った写真をアップロード(最大5枚まで)
・テキストで感想を入力
・総合評価、味、コスパ、面白さを星5段階で評価する
・タグも複数個登録可能
・住所も登録(市町村単位まで)
・食べログのURLがある場合は入力

<店の表示>
(全体表示)
・サムネイルで店名と総合評価(例:★★★★☆)、市町村(例:東京都千代田区)
・店名やキーワード、タグ、市町村で検索が可能
・1ページに20件表示。全部で5ページまで存在。(<前へ 1 2 … 5 次へ> のようなよくあるUIでページ移動)
(個別表示)
・全体表示で店をクリックすると、個別ページが表示され、上記で登録した要素が表示される

これに対するGeminiの回答が以下になります。
アプリの利用者や機能面など、抜けている点を確認する質問が来たので、回答します。

Geminiの回答

こんにちは。飲食店の記録Webページ作成、面白そうですね!要件を固めるための壁打ち、承知しました。

いくつか確認させてください。これらが明確になると、どのような技術で作るのが最適かが見えてきます。

🙋‍♂️ 確認したいこと

  1. 利用者は誰ですか?

    • あなた個人だけが使う(記録・閲覧する)想定ですか?
    • それとも、友人や不特定の他の人にも見せたり、登録してもらったりしますか?
    • (これによって、ログイン機能などが必要かどうかが変わってきます)
  2. 編集・削除機能は必要ですか?

    • 一度登録したお店の情報(感想、写真、評価など)を、後から修正したり、お店の登録自体を削除したりする機能は必要ですか?
  3. なぜ「Webページ」で作りたいですか?

    • (例えば「プログラミングの学習を兼ねて」「PCでもスマホでも見たい」「デザインを自由に作りたい」など、目的によって最適な作り方が変わるためです)
  4. 検索の「キーワード」とは?

    • 「キーワードで検索」とありますが、これは具体的にどの項目(「店名」や「感想」のテキストなど)を検索対象にしたいですか?
  5. 技術的な希望や制約はありますか?

    • 特に使ってみたいプログラミング言語や技術(例: Python, JavaScript, React, WordPressなど)はありますか?
    • サーバー代などの費用は、なるべく無料に抑えたいですか?

まずは、特に 1〜3 について教えていただけると、グッと具体的な仕様に近づけると思います。
もちろん、他の点についても「こうしたい」というイメージがあれば、ぜひお聞かせください!

上記に対する筆者の回答

・友人や不特定の他の人にも見せるが、登録は自分だけが可能

・編集・削除機能は必要

・PCでもスマホでも見たいのでWebで作る。あとはプログラミングの学習を兼ねて

・店名、タグ、市町村、感想等のテキストで検索する

・バックエンドはPython, フロントエンドはNext.js(TypeScript)を使いたい

・サーバー代は無料で抑えたい

そして、Geminiによる最終的なまとめが以下になります。
「仕様」と「技術的側面」について詳細にまとめてくれました。

Geminiによる最終的なまとめ

いただいた情報に基づき、「仕様」と「技術的側面」の2点にまとめます。 学習目的と実用性を兼ねた、とても良い構成だと思います!


1. 仕様(何を構築し、なぜ構築するのか)

プロジェクト概要

何を構築するか:
あなた(管理者)だけが登録・編集できる、個人用の「飲食店訪問記録 Webアプリケーション」を構築します。
登録されたデータは、友人や不特定多数の人が閲覧できる公開ギャラリー(ポートフォリオ)として機能します。

なぜ構築するか:

  • 実用目的: 自身が訪問した飲食店の記録(写真、感想、評価など)を一元管理し、PCやスマートフォンからいつでも閲覧できるようにするため。
  • 学習目的: バックエンド (Python) とフロントエンド (Next.js) を使ったモダンなWebアプリケーション開発の技術を習得するため。

機能一覧

👤 管理者機能(あなた専用)

  • 認証:
    • 管理者(あなた)だけがデータの登録・編集・削除を行えるよう、ログイン機能が必要です。
  • データ登録 (Create):
    • 新しい飲食店の情報を登録します。
    • 登録件数の上限は 100件 です。
  • データ編集 (Update):
    • 一度登録したお店の情報を後から修正します。
  • データ削除 (Delete):
    • 登録したお店の情報を削除します。

🌍 閲覧者機能(全員に公開)

  • 一覧表示 (トップページ):
    • 登録されたお店を一覧で表示します。
    • 各お店の情報として「サムネイル(写真1枚目)」「店名」「総合評価 (例: ★★★★☆)」「市町村」を表示します。
    • 1ページあたり 20件 表示します。
  • ページネーション:
    • 全100件のデータを20件ずつ、最大5ページ に分けて表示します。(例: <前へ 1 2 3 4 5 次へ>
  • 検索機能:
    • 「店名」「タグ」「市町村」「感想テキスト」を対象としたキーワード検索機能を提供します。
  • 詳細表示:
    • 一覧ページで特定のお店をクリックすると、そのお店の登録データ(下記「データ項目」参照)をすべて表示する個別ページに移動します。

データ項目(お店ごとに登録する情報)

  1. 店名 (テキスト)
  2. 写真 (画像ファイル、最大5枚までアップロード可能)
  3. 感想 (テキスト、複数行入力可)
  4. 評価 (星5段階評価)
    • 総合
    • コスパ
    • 面白さ
  5. タグ (複数登録可能、例: #ラーメン, #ランチ, #個室あり)
  6. 住所 (市町村単位まで、例: 東京都千代田区)
  7. 食べログURL (URLリンク)

2. 技術的側面(スタック、アーキテクチャ、制約条件)

「学習目的」「指定スタック」「無料運用」の3点を満たすための、現実的かつモダンな構成案です。

推奨技術スタック

  • フロントエンド: Next.js (TypeScript) (指定通り)
    • UIコンポーネント: Tailwind CSSChakra UI などを利用すると、レスポンシブデザイン(PC/スマホ対応)を効率的に実装できます。
  • バックエンド: Python + FastAPI
    • 指定のPythonを使用します。フレームワークとして、モダンで高速、ドキュメントも豊富な FastAPI が Next.js とのAPI連携学習に最適です。
  • データベース (DB): Supabase (PostgreSQL)
    • 無料枠が強力で、リレーショナルデータベース (PostgreSQL) を簡単に利用できます。
  • 画像ストレージ: Supabase Storage
    • アップロードされた写真(最大 100件 * 5枚 = 500枚)を保存するために、DBと同じSupabaseのストレージ機能を使うのが最もシンプルです。

アーキテクチャ(構成案)

「ヘッドレス (Headless)」 または 「Jamstack」 と呼ばれる、フロントエンドとバックエンドを完全に分離する構成を推奨します。

  1. フロントエンド (Next.js):

    • UI(見た目)とユーザー操作を担当します。
    • データの表示・検索時は、バックエンドAPIにリクエストを送信します。
    • デプロイ先: Vercel (Next.jsと相性が良く、無料枠で十分運用可能)
  2. バックエンド (Python FastAPI):

    • 「API (Application Programming Interface)」として動作します。
    • フロントエンドからのリクエスト(例: 「お店一覧をください」「このお店を登録して」)に応じて、データベースを操作(読み書き)し、結果をJSON形式で返します。
    • デプロイ先: Render (Pythonの無料枠があり、Vercelと組み合わせて使いやすい)
  3. データベース & ストレージ (Supabase):

    • お店のテキスト情報(店名、感想、評価など)は SupabaseのDB (PostgreSQL) に保存します。
    • アップロードされた写真は Supabase Storage に保存します。

認証(「自分だけ」登録・編集する方法)

バックエンドAPIを悪用されないよう、登録・編集・削除の操作には認証が必要です。

  • 学習目的なら: JWT (JSON Web Token) 認証の実装がおすすめです。
    1. Next.jsで管理者用ログインページを作成します。
    2. FastAPIでID/パスワードを検証し、成功したら「トークン」を発行します。
    3. Next.jsは、以降の登録・編集リクエスト時にその「トークン」を一緒に送信します。
    4. FastAPIは「トークン」が正しいか検証してから処理を実行します。
  • 手軽さ重視なら: Supabaseに組み込まれている Supabase Auth を利用するのも手です。

制約条件と考慮事項

  • 無料枠の制限:
    • 上記の構成 (Vercel + Render + Supabase) は、すべて 無料枠 の範囲で実現可能です。
    • ただし、Renderの無料枠は「一定時間アクセスがないとスリープする」ため、管理者ページを開いた最初の読み込みが少し遅くなる可能性があります(閲覧者側はVercelなので高速です)。
    • Supabaseの無料枠 (Storage 1GB) は、写真500枚程度なら十分収まります。
  • データ上限 (100件):
    • プログラム側で登録件数をカウントし、100件に達したらそれ以上登録できないように制御する必要があります(DBの機能というより、アプリケーションのロジックで実装します)。
  • ページネーション (5ページ):
    • 100件固定なので、フロントエンド (Next.js) で 1 2 3 4 5 のボタンを静的に配置しても良いでしょう。
    • 学習目的なら、バックエンドAPIに「何ページ目のデータを要求するか」(例: /api/restaurants?page=2) を伝え、DBから該当の20件だけを取得する(LIMITOFFSET)実装がより実践的です。

(なお、上記の内容に関して適切かどうか判断できず不安でしたが、後でWebに明るい同僚の方に見ていただいたところ、モダンかつ定番の構成になっており問題ないとのことでした。)

Spec Kitを使って開発

Geminiとの壁打ちで、仕様と技術的側面が明確化されました。
それぞれ、下記の画像のように/specify/plan の引数にそのままコピペして貼り付けます。
(Spec Kitのインストール方法や、コマンド入力方法は冒頭紹介した記事をご覧ください。また、最新のコマンドは「speckit.」をつけたものが正しいようです)

今回、生成AIのモデルは「Claude Sonnet 4.5」を使用しました。
上記のコマンドを入力すると、基本的にはAIが作業を進めていきつつ、所々人間側にレビューを求めてくるので、チャットで修正依頼をしたり、ファイル書き込みの承認ボタンを押したりします。

生成されたmdファイルは以下になります。

spec.md(/specifyで生成)

Feature Specification: 飲食店訪問記録ポートフォリオシステム

Feature Branch: 001-restaurant-portfolio
Created: 2025年10月18日
Status: Draft
Input: User description: "飲食店訪問記録 Webアプリケーション"

User Scenarios & Testing (mandatory)

User Story 1 - 訪問記録の閲覧 (Priority: P1)

訪問者(友人や一般の人)が、管理者が訪問した飲食店の記録を一覧で閲覧し、興味のある店舗の詳細情報を確認できる。

Why this priority: このシステムの主要な価値提供は「記録の共有・公開」であり、閲覧機能がなければアプリケーションの存在意義がありません。最も基本的で重要な機能です。

Independent Test: 管理者が事前に登録した店舗データ(少なくとも1件)をブラウザで閲覧し、一覧ページと詳細ページが正しく表示されることを確認できます。登録・編集機能がなくても、サンプルデータで独立してテスト可能です。

Acceptance Scenarios:

  1. Given システムに10件の飲食店が登録されている、When 訪問者がトップページにアクセスする、Then 10件すべての店舗が一覧表示され、各店舗のサムネイル、店名、総合評価(星表示)、市町村が表示される
  2. Given 一覧ページに複数の店舗が表示されている、When 訪問者が特定の店舗をクリックする、Then その店舗の詳細ページに遷移し、店名、全写真、感想、4種類の評価、タグ、住所、食べログURLが表示される
  3. Given システムに25件の飲食店が登録されている、When 訪問者がトップページにアクセスする、Then 最初の20件が表示され、ページネーション(1, 2)が表示される
  4. Given 訪問者が2ページ目を表示している、When 訪問者がページネーションの「1」をクリックする、Then 1ページ目(最初の20件)が表示される

User Story 2 - 店舗情報の検索 (Priority: P2)

訪問者が、店名、タグ、市町村、感想テキストをキーワードで検索し、特定の条件に合う飲食店を素早く見つけられる。

Why this priority: 店舗数が増えると一覧表示だけでは目的の店舗を見つけにくくなります。検索機能は利便性を大幅に向上させますが、店舗数が少ない初期段階では必須ではないため、P1より低い優先度です。

Independent Test: 既存の店舗データ(タグや市町村などの検索可能な属性を含む)に対して、検索フォームからキーワードを入力し、該当する店舗のみが一覧表示されることを確認できます。

Acceptance Scenarios:

  1. Given システムに「ラーメン」タグを持つ店舗が3件、持たない店舗が5件登録されている、When 訪問者が検索ボックスに「ラーメン」と入力して検索する、Then 「ラーメン」タグを持つ3件のみが一覧表示される
  2. Given システムに「千代田区」の店舗が2件、「新宿区」の店舗が3件登録されている、When 訪問者が「千代田区」で検索する、Then 千代田区の2件のみが表示される
  3. Given ある店舗の感想に「個室でゆっくりできた」というテキストが含まれている、When 訪問者が「個室」で検索する、Then その店舗が検索結果に含まれる
  4. Given システムに複数の店舗が登録されている、When 訪問者が検索条件に一致する店舗が存在しないキーワードで検索する、Then 「該当する店舗が見つかりません」というメッセージが表示される

User Story 3 - 店舗情報の登録と管理 (Priority: P1)

管理者(あなた)が、訪問した飲食店の情報(写真、感想、評価など)を新規登録、編集、削除できる。

Why this priority: データがなければ閲覧も検索もできないため、P1として「閲覧」と同等に重要です。管理者が記録を作成・維持できることがシステムの前提条件です。

Independent Test: 管理者としてログインし、新しい店舗情報を登録フォームから入力・保存し、その後編集・削除ができることを確認します。閲覧機能とは独立してテスト可能です。

Acceptance Scenarios:

  1. Given 管理者がログインしている、When 管理者が「新規登録」ボタンをクリックし、必須項目(店名、写真1枚、感想、総合評価)を入力して保存する、Then 新しい店舗がシステムに登録され、一覧ページに表示される
  2. Given 管理者がログインし、既に3枚の写真をアップロード済みの店舗を編集している、When 管理者がさらに2枚の写真を追加する、Then 合計5枚の写真が保存される
  3. Given 管理者がログインし、5枚の写真をアップロード済みの店舗を編集している、When 管理者が6枚目の写真をアップロードしようとする、Then 「写真は最大5枚までです」というエラーメッセージが表示され、アップロードが拒否される
  4. Given 管理者がログインし、既存の店舗情報を編集画面で開いている、When 管理者が感想テキストを修正して保存する、Then 修正内容が反映され、詳細ページに新しい感想が表示される
  5. Given 管理者がログインし、特定の店舗の詳細ページを表示している、When 管理者が「削除」ボタンをクリックして確認する、Then その店舗情報がシステムから削除され、一覧ページに表示されなくなる
  6. Given システムに既に100件の店舗が登録されている、When 管理者が101件目の店舗を登録しようとする、Then 「登録上限(100件)に達しています」というエラーメッセージが表示され、登録が拒否される

User Story 4 - 管理者認証 (Priority: P1)

管理者だけがデータの登録・編集・削除を行えるよう、安全な認証機能が提供される。

Why this priority: データの整合性とセキュリティを保つため、管理機能へのアクセス制御は必須です。認証がないと誰でもデータを改変できてしまうため、P1です。

Independent Test: ログイン画面で正しい認証情報を入力してログインし、管理機能(登録・編集・削除ボタン)にアクセスできることを確認します。また、ログインしていない状態では管理機能が表示されないことを確認します。

Acceptance Scenarios:

  1. Given 管理者が未ログイン状態でトップページにアクセスしている、When 管理者がログインボタンをクリックし、正しい認証情報を入力する、Then ログインに成功し、管理機能(新規登録、編集、削除ボタン)が表示される
  2. Given 管理者がログインしている、When 管理者がログアウトボタンをクリックする、Then ログアウトし、管理機能が非表示になる
  3. Given 訪問者(非管理者)がトップページにアクセスしている、When 訪問者がページを閲覧する、Then 管理機能(新規登録、編集、削除ボタン)は表示されず、閲覧のみが可能である
  4. Given 管理者が未ログイン状態である、When 管理者が管理機能のURLに直接アクセスしようとする、Then ログイン画面にリダイレクトされる

Edge Cases

  • システムに0件の店舗が登録されている場合、トップページには「まだ店舗が登録されていません」というメッセージが表示される
  • 写真が1枚も登録されていない店舗の場合、一覧ページのサムネイルにはデフォルト画像(例:「No Image」プレースホルダー)が表示される
  • 店舗の感想が非常に長い場合(例:3000文字以上)でも、詳細ページで全文が正しく表示される
  • 検索キーワードに特殊文字(例:引用符、アポストロフィ、SQL構文など)が含まれていても、システムは安全に処理し、エラーが発生しない
  • 管理者が複数のブラウザタブで同じ店舗を編集している場合、最後に保存された内容が優先される(先に保存した内容は上書きされる)
  • 画像ファイルのサイズが5MBを超える場合、「ファイルサイズは5MB以下にしてください」というエラーメッセージが表示され、アップロードが拒否される
  • ページネーションの最後のページが20件未満の場合(例:全23件の場合、2ページ目は3件のみ)、正しく表示される
  • 同じ店名の店舗が複数登録されている場合でも、個別の詳細ページとして区別して表示される

Requirements (mandatory)

Functional Requirements

認証・権限管理

  • FR-001: システムは管理者のみがログインできる認証機能を提供しなければならない
  • FR-002: システムはログイン済みの管理者にのみ、データの登録・編集・削除機能へのアクセスを許可しなければならない
  • FR-003: システムは未ログイン状態のユーザーが管理機能にアクセスしようとした場合、ログイン画面にリダイレクトしなければならない
  • FR-004: システムは管理者がログアウトできる機能を提供しなければならない

店舗情報の登録・編集・削除(管理者専用)

  • FR-005: システムは管理者が新しい飲食店情報を登録できる機能を提供しなければならない
  • FR-006: システムは管理者が既存の飲食店情報を編集できる機能を提供しなければならない
  • FR-007: システムは管理者が飲食店情報を削除できる機能を提供しなければならない
  • FR-008: システムは店舗の登録上限を100件に制限しなければならない
  • FR-009: システムは登録上限(100件)に達した場合、新規登録を拒否し、エラーメッセージを表示しなければならない

店舗データ項目

  • FR-010: システムは各店舗に対して、店名(テキスト)を必須項目として保存しなければならない
  • FR-011: システムは各店舗に対して、写真を最大5枚まで保存できなければならない
  • FR-012: システムは6枚目以降の写真アップロードを拒否し、エラーメッセージを表示しなければならない
  • FR-012a: システムは1ファイルあたり5MBを超える画像のアップロードを拒否し、「ファイルサイズは5MB以下にしてください」というエラーメッセージを表示しなければならない
  • FR-013: システムは各店舗に対して、感想(複数行テキスト)を保存しなければならない
  • FR-014: システムは各店舗に対して、以下の4種類の星5段階評価を保存しなければならない:総合、味、コスパ、面白さ
  • FR-015: システムは各店舗に対して、複数のタグを保存できなければならない
  • FR-016: システムは各店舗に対して、住所(市町村単位まで)を保存しなければならない
  • FR-017: システムは各店舗に対して、食べログURL(リンク)を保存できなければならない

一覧表示(公開)

  • FR-018: システムは登録されたすべての店舗を一覧形式で表示しなければならない
  • FR-019: 一覧の各店舗は、サムネイル(写真1枚目)、店名、総合評価(星表示)、市町村を表示しなければならない
  • FR-020: 一覧は1ページあたり20件ずつ表示しなければならない
  • FR-021: システムは店舗が登録されていない場合、「まだ店舗が登録されていません」というメッセージを表示しなければならない
  • FR-022: システムは写真が登録されていない店舗に対して、デフォルト画像を表示しなければならない

ページネーション

  • FR-023: システムは全100件のデータを20件ずつ、最大5ページに分けて表示しなければならない
  • FR-024: システムはページネーションコントロール(前へ、次へ、ページ番号)を提供しなければならない
  • FR-025: システムは最後のページが20件未満の場合でも正しく表示しなければならない

検索機能(公開)

  • FR-026: システムは訪問者が店名、タグ、市町村、感想テキストを対象にキーワード検索できる機能を提供しなければならない
  • FR-027: システムは検索結果を一覧形式で表示しなければならない
  • FR-028: システムは検索条件に一致する店舗が存在しない場合、「該当する店舗が見つかりません」というメッセージを表示しなければならない
  • FR-029: システムは検索キーワードに特殊文字が含まれていても安全に処理しなければならない

詳細表示(公開)

  • FR-030: システムは訪問者が一覧から店舗をクリックした際、その店舗の詳細ページを表示しなければならない
  • FR-031: 詳細ページは、店名、全写真(最大5枚)、感想、4種類の評価(総合、味、コスパ、面白さ)、タグ、住所、食べログURLを表示しなければならない

デバイス対応

  • FR-032: システムはPCとスマートフォンの両方から閲覧できなければならない

Key Entities

  • 店舗 (Restaurant): 訪問した飲食店を表すエンティティ。属性:店名、写真(最大5枚)、感想、評価4種類(総合/味/コスパ/面白さ、各5段階)、タグ(複数)、住所(市町村単位)、食べログURL
  • 写真 (Photo): 店舗に関連付けられた画像。1店舗あたり最大5枚
  • タグ (Tag): 店舗を分類・検索するためのラベル(例:#ラーメン、#ランチ、#個室あり)。1つのタグは複数の店舗に関連付けられる
  • 管理者 (Administrator): データの登録・編集・削除権限を持つ唯一のユーザー(あなた)

Success Criteria (mandatory)

Measurable Outcomes

  • SC-001: 訪問者が一覧ページにアクセスしてから、特定の店舗の詳細ページを表示するまで3クリック以内で完了できる
  • SC-002: 管理者が新しい店舗情報(店名、写真1枚、感想、評価)を登録する作業が5分以内に完了できる
  • SC-003: システムは最大100件の店舗データと各店舗5枚の写真(合計最大500枚の画像)を保存し、一覧ページが3秒以内に表示される
  • SC-004: 検索機能を使用した場合、キーワード入力から検索結果表示まで2秒以内に完了する
  • SC-005: PCとスマートフォンの両方でレイアウトが適切に表示され、すべての機能が問題なく動作する
  • SC-006: 管理者が既存の店舗情報を編集し、変更を保存する作業が3分以内に完了できる
  • SC-007: 訪問者の90%が、検索機能を使わずに一覧ページから目的の店舗を見つけられる(店舗数が20件以下の場合)
plan.md(/planで生成)

Implementation Plan: 飲食店訪問記録ポートフォリオシステム

Branch: 001-restaurant-portfolio | Date: 2025年10月18日 | Spec: spec.md
Input: Feature specification from /specs/001-restaurant-portfolio/spec.md

Note: This template is filled in by the /speckit.plan command. See .specify/templates/commands/plan.md for the execution workflow.

Summary

管理者が訪問した飲食店の記録(写真、感想、評価など)を登録・管理し、友人や一般の人に公開するWebアプリケーションを構築します。管理者のみがデータのCRUD操作を行い、訪問者は一覧表示・検索・詳細閲覧が可能です。

技術アプローチ: Jamstackアーキテクチャを採用し、Next.js (TypeScript) フロントエンド、Python FastAPI バックエンド、Supabase (PostgreSQL + Storage) でデータ・画像を管理します。認証はJWT方式を実装し、Vercel (フロントエンド)、Render (バックエンド)、Supabaseの無料枠で運用します。

Technical Context

Language/Version:

  • Frontend: TypeScript (Next.js 14+)
  • Backend: Python 3.11+

Primary Dependencies:

  • Frontend: Next.js, React, Tailwind CSS または Chakra UI
  • Backend: FastAPI, Pydantic, python-jose[cryptography] (JWT), python-multipart (画像アップロード)
  • Database Client: supabase-py

Storage:

  • Database: Supabase (PostgreSQL)
  • Image Storage: Supabase Storage

Testing:

  • Frontend: Jest, React Testing Library
  • Backend: pytest, pytest-asyncio

Target Platform:

  • Frontend: Vercel (静的デプロイ + SSR)
  • Backend: Render (コンテナデプロイ)
  • Database/Storage: Supabase Cloud

Project Type: Web application (frontend + backend分離)

Performance Goals:

  • 一覧ページ読み込み: 3秒以内(最大100件、ページあたり20件)
  • 検索結果表示: 2秒以内
  • 画像アップロード: 5MB以下の画像、1枚あたり10秒以内

Constraints:

  • 店舗登録上限: 100件
  • 画像枚数上限: 1店舗あたり5枚、全体500枚
  • 画像ファイルサイズ: 1ファイルあたり5MB以下
  • 無料枠制約: Vercel無料枠、Render無料枠(スリープあり)、Supabase無料枠(Storage 1GB)

Scale/Scope:

  • 想定ユーザー数: 管理者1名、閲覧者数十名程度
  • データ件数: 最大100店舗、最大500画像
  • 画面数: 約6画面(一覧、詳細、検索結果、登録フォーム、編集フォーム、ログイン)

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

Status: ✅ PASS (Phase 0 & Phase 1 完了、設計原則に準拠)

Applied Principles:

  • Separation of Concerns: フロントエンドとバックエンドを明確に分離、APIコントラクト(OpenAPI 3.0)で契約を定義
  • API-First Design: RESTful APIでフロントエンド・バックエンド間の契約を明確化、Swagger UI自動生成
  • Test Coverage: 各レイヤー(UI: React Testing Library、API: pytest、データモデル: DB制約)で適切なテスト戦略を採用
  • Security: JWT認証、入力検証(Pydantic)、SQLインジェクション対策(ORM使用)、bcryptパスワードハッシュ化を実装
  • Simplicity: 学習目的のため、過度な抽象化を避け、直接的な実装を優先
  • Documentation: データモデル、API仕様、クイックスタートガイドを完備

Phase 1 Deliverables:

  • research.md: 技術選定の根拠と実装パターン(12セクション)
  • data-model.md: PostgreSQLスキーマ定義、エンティティ関係図、マイグレーションSQL
  • contracts/openapi.yaml: RESTful API仕様(8エンドポイント、OpenAPI 3.0)
  • quickstart.md: ローカル開発環境セットアップガイド
  • .github/copilot-instructions.md: GitHub Copilotコンテキスト更新

No violations: 標準的なJamstackパターンに従い、不要な複雑性は導入していません。

Project Structure

Documentation (this feature)

specs/[###-feature]/
├── plan.md              # This file (/speckit.plan command output)
├── research.md          # Phase 0 output (/speckit.plan command)
├── data-model.md        # Phase 1 output (/speckit.plan command)
├── quickstart.md        # Phase 1 output (/speckit.plan command)
├── contracts/           # Phase 1 output (/speckit.plan command)
└── tasks.md             # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)

Source Code (repository root)

backend/
├── src/
│   ├── models/              # データモデル(Restaurant, Photo, Tag, User)
│   ├── schemas/             # Pydantic schemas(リクエスト/レスポンスの型定義)
│   ├── services/            # ビジネスロジック(restaurant_service, auth_service, storage_service)
│   ├── api/                 # FastAPI routers(restaurants, auth, search)
│   ├── database.py          # Supabase接続設定
│   ├── auth.py              # JWT認証ミドルウェア
│   └── main.py              # FastAPIアプリケーションエントリーポイント
├── tests/
│   ├── unit/                # 単体テスト(services, modelsのロジック)
│   ├── integration/         # 統合テスト(API + DB連携)
│   └── contract/            # APIコントラクトテスト(OpenAPI準拠)
├── requirements.txt         # Python依存関係
└── Dockerfile               # Render用コンテナ定義

frontend/
├── src/
│   ├── app/                 # Next.js App Router
│   │   ├── page.tsx         # トップページ(一覧表示)
│   │   ├── restaurants/
│   │   │   └── [id]/
│   │   │       └── page.tsx # 詳細ページ
│   │   ├── search/
│   │   │   └── page.tsx     # 検索結果ページ
│   │   ├── admin/
│   │   │   ├── login/
│   │   │   │   └── page.tsx # 管理者ログイン
│   │   │   ├── new/
│   │   │   │   └── page.tsx # 新規登録フォーム
│   │   │   └── edit/
│   │   │       └── [id]/
│   │   │           └── page.tsx # 編集フォーム
│   │   └── layout.tsx       # 共通レイアウト
│   ├── components/          # Reactコンポーネント
│   │   ├── RestaurantCard.tsx    # 一覧表示用カード
│   │   ├── RestaurantDetail.tsx  # 詳細表示
│   │   ├── SearchBar.tsx         # 検索バー
│   │   ├── Pagination.tsx        # ページネーション
│   │   └── RestaurantForm.tsx    # 登録・編集フォーム
│   ├── services/            # API通信ロジック
│   │   ├── restaurantApi.ts      # レストランCRUD操作
│   │   ├── authApi.ts            # 認証API
│   │   └── searchApi.ts          # 検索API
│   ├── types/               # TypeScript型定義
│   │   └── restaurant.ts         # Restaurant, Photo, Tag型
│   └── lib/                 # ユーティリティ
│       ├── auth.ts               # JWT保存・取得ロジック
│       └── apiClient.ts          # Axios/Fetchラッパー
├── tests/
│   └── components/          # コンポーネントテスト(React Testing Library)
├── package.json
├── tsconfig.json
└── next.config.js

.github/
└── workflows/               # CI/CD設定(将来的に)
    ├── backend-ci.yml
    └── frontend-ci.yml

Structure Decision: Web applicationパターンを採用。フロントエンド(Next.js)とバックエンド(FastAPI)を完全に分離し、RESTful APIで通信します。各プロジェクトは独立してテスト・デプロイ可能です。

Complexity Tracking

No violations - standard web architecture pattern applied.

この後は、/tasksで具体的な作業タスクに分割し、/implementで実装を行います。

/tasksでは、下記のような100タスクに分割されました。

/implementでは、人間がコードを書くことはなく、全てAI任せです。
ここに関しては、Webプログラミングの知識がない自分にとって、非常に助かりました。
人間はデータベースやデプロイの設定と実行テストを行い、必要に応じて問題点を指摘していきます。
前者の設定(Supabase、Render、Vercel)に関しても知識ゼロでしたが、AIが設定用マニュアルを作ってくれるので、特に問題はありませんでした(不明点はチャットで質問も可能)。

設定用マニュアルの例(Vercel)

また、コマンド別ではここがダントツで時間を要しました。

  • /specify/plan/tasks: 各10分程度
  • /implement: 約6時間

完成したサイトの画面

最終的に以下のようなサイトが完成しました。

店舗登録画面(管理者のみログイン可)





店舗閲覧画面


まとめ

タイトルの「Spec KitでWeb初心者がアプリを作ることは可能か?」ですが、結論は「可能」でした。

大規模開発となるとまた話は別かもしれませんが、個人開発レベルであれば上記のようにモダンな構成で実際に動くアプリを作ることができます。

皆様もSpec Kitを利用して実際にアプリを作ってみてはいかがでしょうか?

とある通信会社の有志

Discussion