個人開発ログ 📔 MBTI × 適職診断ボットを作ってみたが、最後で 404 に泣いた

AIに壁打ちしながら、学習＋サービス化を目指しました。
が、途中でガス欠＆挫折。
そこで**備忘録**として転んだ場所を全部メモしておくことにしました。

この記事もAIに手伝ってもらいながら書いていますが、正直まだ**問題の全体像も解決策もモヤッと**しています。だからこそ、  

> **数ヵ月後に読み返して「お、あの頃よりレベル上がってる！」とニヤけたい。**

さらに、これから同じ坂道を登る人が **同じ地雷を踏まずに済むよう**、私が踏み抜いたポイントをしっかりマーキングしておきます。


---

## ざっくり何を作りたかったか

> **「MBTI タイプ × 職業」を掛け合わせて、  
>  あなたに合ったキャリアを即答する Web ボットを作りたい！**
### 企画フェーズ
MBTI 16タイプごとに相性の良い職業を紐付け、対話形式で診断できるボットを構想。  
「質問に答えるだけで、自分のタイプと適職が一気にわかる」をコンセプトに設計しました。

| 目的 | 中身 |
|---|---|
| **企画** | MBTI タイプと職業を紐付けて診断してくれる Web ボット |
| **MVP** | Streamlit だけで完結する簡易版 |
| **Advanced** | - JSON → ベクトル化 → RAG 検索<br>- FastAPI で API 化<br>- Next.js＋Tailwind で UI<br>- Railway & Vercel で本番公開 |



---
## 技術スタック

開発スピードを保ちつつ、後々のスケールにも耐えられる “軽量 × モダン” 構成を選定しました。

| レイヤ | 採用技術 | Why / メモ |
|:--|:--|:--|
| **フロント** | **Next.js (App Router)** / React / Tailwind CSS | *SSR／SPA をシームレスに切替*。Tailwind でデザイン試行→即反映。最近の案件で “触っておきたい” 技術を採用。 |
| **バックエンド** | **FastAPI** + Uvicorn | 型ヒントで自動ドキュメント（Swagger）生成が神。シンプル構成でも高パフォーマンス。 |
| **RAG** | **LangChain** / Chroma / OpenAI Embeddings | JSON → ベクトル化 → VectorStore 検索。LangChain の抽象化で実装コストを削減。 |
| **インフラ** | **Railway**（API） / **Vercel**（Front） | 両方とも無料枠が手頃。Git push → 自動デプロイで “デプロイ恐怖症” を回避。 |

---
## やったことタイムライン

1. **アイデア決定**  
   - MBTI × 適職はニッチで勉強題材によさそう  
   - Streamlit なら最短 1 ファイルで動く
2. **MVP (`app.py`) 完成**  
   - OpenAI SDK v0 系 → v1 系で *ChatCompletion* 廃止にビビる  
3. **RAG 強化**  
   - `build_vectorstore.py` で JSON → Chroma  
   - `rag_chain.py` に RetrievalQA 作成
4. **FastAPI 化 & Railway デプロイ**  
   - `railway.toml` に `root = "backend"` を書かないと Nixpacks 迷子  
   - `.env` を Service Variables へ
5. **Next.js + Tailwind 化**  
   - `"use client"` を忘れる → React Hook エラー  
   - `@tailwindcss/postcss` 追加しないと Build Error
6. **Vercel デプロイ**  
   - **最後に躓いた！**  
     - Deployment Protection で Login 画面 → OFF  
     - Alias が旧プロジェクトに残っていて 404 祭り

---

## 詰まったポイント

|  | 症状 | 原因 
|---|---|---|
| ① `openai.ChatCompletion` が無い | SDK v1 から API 変更 | `openai.chat.completions.create()` に書換え |
| ② `tiktoken` ImportError | langchain 依存 | `pip install tiktoken` |
| ③ `ModuleNotFoundError: rag_chain` | FastAPI ルートと PYTHONPATH | `backend` を root に設定 |
| ④ Railway Nixpacks build failed | ルート検出失敗 | `railway.toml` で `root` 明示 |
| ⑤ `chromadb` ImportError on Railway | requirements に抜け | `chromadb` 追加 |
| ⑥ Next.js build 404 | `"use client"` 忘れ | App Router の client component ルール |
| ⑦ Vercel 404 / Login | 1) Deployment Protection ON<br>2) Alias が別プロジェクト | Protection を Disable → 正プロジェクト側の **Domains** に Alias 登録 |

---

## まだ残っている課題

- **Alias 競合を完全解決して公開 URL を安定させる**  
  - 旧プロジェクト削除 or カスタムドメインで逃がす
- LangChain の **Deprecation** を全置換  
  - `langchain_openai`, `langchain_chroma` へ
- **SEO / OGP** 対策 (Next.js で `<meta>` 設定)
- ベクトルストアの **永続化**  
  - Chroma を外部 RDS or S3 に置き換え
- **テスト & CI**  
  - pytest で API テスト → GitHub Actions で自動化

---

## 反省 & 学び

| 反省 | 次回の行動 |
|---|---|
| 機能を増やす前に **デプロイ基盤を固める** べきだった | **“Hello World を先に本番へ”** を徹底 |
| `.env` を Git に push → Secrets 検知でブロック | **dotenv-linter** / **git-secrets** を導入 |
| 新 API リリース情報を追っておらず後手 | **Release note ウォッチ自動化** (GitHub RSS) |
| ブランチ / プロジェクトが増え Alias 迷子 | **命名規則** と **削除ルール** を決める |

---

## ここまでで得たスキル・実績

- RAG パターン実装 & LangChain 0.2 系移行経験
- FastAPI × Railway デプロイ
- Next.js (App Router) + Tailwind で SPA
- CI/CD（GitHub → Vercel/Railway）パイプライン
- 落とし穴（OpenAI SDK v1 / Protection / Alias）まとめ知見


> - 「RAG ボットを 0→1 でフルスタック構築」  
> - 「無料枠だけで CI/CD まで回した経験」  
> - 「SDK v0→v1 移行、LangChain Deprecation 処理」

---

## 次にむけて

1. Alias 問題を解消して **公開 URL を貼れる状態** にする  
2. Qiita 連載 **第2回：ベクトルストア編** を書く  
3. 社内 FAQ や PDF 文書でも試し、**RAG 汎用テンプレ** に昇華  
4. OSS テンプレとして GitHub テンプレートリポジトリ化

---

### さいごに（2025/06/02）

いや〜、正直めちゃくちゃ悔しい。  
AI 全盛のいま、道具は山ほどあるのに **自力で最後まで作り切れなかった**。

思い返せば、

- 手探りで突っ走りすぎて、**正解／不正解が見えなくなった**  
- 基礎があいまいなまま実装に飛び込み、**自爆**
 
要するに **勉強不足** でした。はい。
それでも諦めたくないのは、つくりたいものがハッキリしているから。

- **暮らしをちょっとラクにする**  
- **「これ欲しかった！」と誰かの役に立つ**  
- **友だちにワクワクしながら「触ってみて！」と言える**

そんなサービス／アプリを、いつか本当に届けたい。

これからもコツコツ学び、転んで、また立ち上がる。  
**押忍！** の気合いで前進あるのみ。  

次こそは「やった！できた！」と胸を張って報告できるように。
頑張ります！！！押忍！！