PromptStock ができるまで — 企画から公開までの全記録
ChatGPTやClaudeを使っていて、こんな経験はありませんか?
「なんかイマイチな回答しか返ってこない…」 「プロンプトの書き方次第で変わるらしいけど、具体的にどう書けば?」
実は、同じAIでもプロンプトの質で出力の精度は劇的に変わります。 PromptStockは、その課題を解決するために生まれたプロンプトテンプレート辞典です。
この記事では、企画の背景から技術選定、実装の裏話まで、開発の全過程をお見せします。
🎯 解決したかった課題
開発のきっかけは、生成AIの「使いこなし格差」でした。
| ユーザー層 | 典型的な使い方 | 結果 |
|---|---|---|
| 初心者 | 「メール書いて」 | 汎用的で使えない文面 |
| 中級者 | 「ビジネスメールを丁寧に書いて」 | そこそこ使える |
| 上級者 | 役割・条件・出力形式を明示 | ほぼ完璧な出力 |
この差は知識の問題であり、良いテンプレートさえあれば誰でも上級者と同じ結果を得られるはずです。 そこで「目的別にすぐコピーして使えるプロンプト集」というコンセプトが生まれました。
🏗️ 技術選定 — なぜこの構成にしたのか
採用技術の全体像
| 技術 | 選定理由 |
|---|---|
| Next.js 15 (App Router) | Server Componentsで SEO と速度を両立 |
| TypeScript | 型安全性。プロンプトデータの構造を厳密に管理 |
| PostgreSQL + Prisma | リレーショナルデータ + 型安全なORM |
| Tailwind CSS v4 | CSS変数ベースのテーマ管理。ダークモード対応が容易 |
| Claude Haiku | 高速・低コスト。プロンプト改善に最適 |
| Framer Motion | 軽量アニメーション。カード表示をリッチに |
不採用にした技術と理由
迷った選択肢もありました。正直に振り返ります。
| 候補 | 不採用の理由 |
|---|---|
| Elasticsearch | プロンプト数が数百件規模。PostgreSQLのLIKE検索で十分 |
| GPT-4o | AI改善機能のコスト。Haikuの方が10倍以上安い |
| Redis | いいね数キャッシュを検討したが、現時点では過剰設計 |
| i18n (多言語化) | PV獲得が最優先。日本語特化の方がSEO効果が高い |
教訓: 「将来必要になるかも」で技術を入れない。必要になってから足すのが正解。
📐 データベース設計
プロンプトのデータ構造はシンプルに保ちつつ、拡張性を確保しました。
PromptStockPrompt
├── id (主キー)
├── title (プロンプトのタイトル)
├── content (プロンプト本文)
├── description (説明文 — 一覧表示用)
├── category (カテゴリ名)
├── tags (タグ配列 — PostgreSQLのネイティブ配列)
├── isOfficial (公式プロンプトフラグ)
├── likeCount (いいね数 — 非正規化)
├── copyCount (コピー数 — 非正規化)
└── createdAt (作成日時)
なぜ likeCount を非正規化したのか
正規化するなら、いいね数は毎回 COUNT(*) で集計するのが正しい設計です。
しかし、一覧ページで8件のプロンプトそれぞれに対してCOUNTクエリを投げるのは明らかに非効率。
-- ❌ 正規化: 毎回8件分のCOUNTが走る
SELECT p.*, (SELECT COUNT(*) FROM likes WHERE prompt_id = p.id) FROM prompts p;
-- ✅ 非正規化: 1クエリで完結
SELECT * FROM prompts ORDER BY like_count DESC;
トレードオフ: いいね/取り消し時にカウントの整合性を保つ必要があるが、トランザクション内で処理すればOK。一覧の表示速度を優先した。
⚡ 機能の実装ハイライト
1. カテゴリ検索 — URLパラメータ駆動
検索とカテゴリフィルタはURLパラメータで管理しています。
/services/prompt-stock?q=メール&category=ビジネス
この設計のメリット:
- ブックマーク可能 — フィルタ状態をURLで共有できる
- SEO対応 — 検索エンジンがカテゴリページをインデックス可能
- Server Component互換 —
searchParamsをサーバー側で受け取れる
stateではなくURLで状態管理することで、リロードしてもフィルタが保持されます。
2. ワンクリックコピー — Clipboard API
const handleCopy = async () => {
await navigator.clipboard.writeText(content)
// コピー数をバックグラウンドでインクリメント
fetch("/api/services/prompt-stock/prompts", {
method: "PATCH",
body: JSON.stringify({ id, action: "copy" }),
})
}
ポイント:
- コピー自体は即座に完了(APIレスポンスを待たない)
- カウント更新は
fire-and-forgetで非同期実行 - ユーザー体感は0ms
3. AIプロンプト改善 — ストリーミング応答
AI改善機能は、入力されたプロンプトを分析して改善版を提案する機能です。
ユーザー入力 → API (Claude Haiku) → ストリーミング応答 → リアルタイム表示
通常のAPI呼び出しだと、全文生成完了まで数秒間の空白が生まれます。 ストリーミングにすることで、文字が生成されるそばから画面に表示されるため、体感待ち時間が大幅に短縮。
AIへの指示(システムプロンプト)では、改善の5つの観点を明示しています:
| 観点 | 内容 |
|---|---|
| 目的の明確化 | 何を達成したいのかを具体的に |
| 出力形式の指定 | 箇条書き・表・コードなど |
| コンテキスト追加 | 前提条件や背景情報 |
| 制約条件の明示 | 文字数・トーン・禁止事項 |
| 具体例の追加 | 期待する出力のサンプル |
4. いいね機能 — トグル式
いいねはログインユーザー限定で、同じプロンプトへの二重いいねは自動的に「取り消し」になります。
1回目のクリック → いいね追加 (♥ 0 → ♥ 1)
2回目のクリック → いいね取り消し (♥ 1 → ♥ 0)
DB上では (promptId, userId) のユニーク制約で二重登録を防止。
存在チェック → 存在すれば削除、なければ作成、という単純なロジックです。
🎨 デザインの意思決定
ダークグラデーション・ヒーロー
PromptStockのヒーローセクションには、紺〜紫のグラデーションを採用しました。
| 検討案 | 却下理由 |
|---|---|
| 白背景 + カラフルアクセント | 他サービスと差別化しにくい |
| 全面パーティクル演出 | テキストの可読性が落ちる |
| ダークグラデーション | 採用: 高級感 + テキスト映え + ダークモード親和性 |
カードUI — 情報の優先度設計
プロンプトカードには多くの情報を詰め込む必要がありました。 「一目で何のプロンプトかわかる」を目標に、情報の優先度を整理しました。
┌─────────────────────────────────┐
│ [カテゴリ] コピー数 │ ← 分類 + 人気度
│ │
│ タイトル(太字) │ ← 最重要: 何ができるか
│ 説明文(2行まで) │ ← 補足: もう少し詳しく
│ │
│ ┌─────────────────────────┐ │
│ │ プロンプト本文プレビュー │ │ ← 中身のチラ見せ
│ └─────────────────────────┘ │
│ │
│ [タグ1] [タグ2] [タグ3] │ ← 関連キーワード
│ │
│ [コピー] [♥ いいね] │ ← アクション
└─────────────────────────────────┘
レスポンシブ対応
| 画面サイズ | カラム数 | 備考 |
|---|---|---|
| モバイル (~640px) | 1列 | カード全幅表示 |
| タブレット (~1024px) | 2列 | 程よい密度 |
| デスクトップ (1024px~) | 3列 | 一覧性を最大化 |
🔒 セキュリティ対策
小規模サービスでも最低限のセキュリティは必須です。
| 対策 | 実装 |
|---|---|
| レート制限 | AI改善API: 10回/分(IP単位) |
| 入力バリデーション | プロンプト本文: 3000文字上限 |
| 認証チェック | いいね機能: NextAuth セッション必須 |
| CSRF対策 | Next.js のビルトイン対策に依存 |
レート制限の実装: 外部サービス(Upstash等)は使わず、インメモリのMap + タイムスタンプで実装。VPS単体構成なのでこれで十分。スケールアウト時にRedisに切り替える想定。
📊 SEO戦略
プロンプト辞典はSEO勝負のサービスです。検索流入を最大化するための施策:
ターゲットキーワード
| 優先度 | キーワード | 月間検索ボリューム(推定) |
|---|---|---|
| 高 | ChatGPT プロンプト テンプレート | 5,000+ |
| 高 | プロンプト 例文 | 3,000+ |
| 中 | AI プロンプト 書き方 | 2,000+ |
| 中 | Claude プロンプト おすすめ | 1,000+ |
技術的SEO対策
- Server Components: 全ページSSR。クローラーがJSを実行せずにコンテンツ取得可能
- generateMetadata: 全ページに個別のtitle/description設定
- JSON-LD: WebApplicationスキーマで構造化データを提供
- canonical URL: 重複コンテンツ防止
- サイトマップ: 動的生成で全プロンプトページを網羅
😅 苦労したポイント
開発は順調ではありませんでした。正直に振り返ります。
1. Prismaのバージョン問題
最初Prisma 7を使おうとしたところ、設定方法が大幅に変更されていて互換性問題が多発。 結局Prisma 6にダウングレードして安定動作を確保しました。
学び: 最新バージョンが常に最善とは限らない。安定性を優先すべき場面がある。
2. Next.js の Middleware 廃止
PV計測をMiddlewareで実装しようとしたら、Next.jsの新バージョンでMiddlewareの仕様が変更。 クライアントコンポーネント + APIルートの構成に切り替えました。
Before: Middleware → DB更新(❌ 動かない)
After: Client Component → API Route → DB更新(✅)
3. ビルド時のDB接続エラー
prisma.findMany() を使うページがビルド時にプリレンダリングされ、DB接続エラーに。
全ページに export const dynamic = "force-dynamic" を追加して解決。
学び: Server Componentsでデータベースアクセスするページは、プリレンダリングの挙動に要注意。
📈 今後のロードマップ
| フェーズ | 内容 | 目的 |
|---|---|---|
| Phase 1 | プロンプト数を50件以上に拡充 | コンテンツ量でSEO強化 |
| Phase 2 | 人気ランキング機能 | エンゲージメント向上 |
| Phase 3 | カテゴリ拡充(画像生成・動画等) | 新規ユーザー獲得 |
| Phase 4 | プロンプトの効果測定機能 | 差別化・リテンション |
💡 学んだこと
この開発を通じて得た教訓をまとめます。
- YAGNI原則は正しい — 多言語化やキャッシュなど「将来必要かも」の機能は全部後回しにした。結果、コア機能の品質に集中できた
- 非正規化は怖くない — いいね数・コピー数の非正規化で一覧の表示速度が劇的に改善。整合性はトランザクションで担保すれば問題ない
- ストリーミングUXの効果は絶大 — AI応答の体感速度が全く違う。ユーザーが「動いている」と感じられることが重要
- URLパラメータ駆動の設計 — フィルタ状態をstateではなくURLで管理することで、ブックマーク・共有・SEOの全てが解決した
PromptStockは「AIをもっと使いこなしたい」すべての人のためのサービスです。 ぜひPromptStockを使ってみてください。