0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React × Firebase でポートフォリオ作成アプリを作った話

Posted at

TL;DR

  • GitHub / Google でログイン → プロフィール編集 → プレビュー → 公開リンク発行まで すべて 1 画面で完結する “PortfolioBuilder” を開発した
  • React 18 + Vite + Tailwind(+ shadcn/ui)で編集体験を重視した UI、Firebase(Auth / Firestore / Storage / Functions)で安全にデータ管理
  • App Check + Cloud Functions v2 callable API で 認証必須のバックエンドを構築しつつ、公開ページは /p/:slug から誰でも閲覧可能

なぜ作ったのか

私は研究開発系の仕事をしているのですが、転職界隈では

  • 「GitHub リンクありますか?」
  • 「ポートフォリオ見せてください」

がいまや常識。一方で 自前で静的サイトを作ってメンテするのが面倒という問題があります。

  • 更新のたびにコード修正が必要
  • 公開 / 非公開切り替えが手間
  • 編集と公開の見た目がズレていく

結果、ポートフォリオ準備だけで疲弊するという本末転倒な状態に。そこで

✨「編集 UI」と「公開ページ」をまとめて提供するアプリがあれば、誰でも即座に “伝わるアウトプット” を作れるのでは?

と思い、PortfolioBuilder を作りました。これ自体もポートフォリオとして提示でき、一石二鳥です。


作ったもの(デモ)

PortfolioBuilder
🔗 https://portfoliobuilder-4188f.web.app/
スクリーンショット 2025-12-08 22.51.49.png

  • サインインすると Firestore に空ポートフォリオを自動生成
    スクリーンショット 2025-12-08 22.55.26.png

  • 左がエディタ / 右がプレビュー の常時同期レイアウト

  • 「公開する」ボタンでランダム slug を払い出して https://.../p/{slug} を即共有
    スクリーンショット 2025-12-08 22.56.39.png

  • 再度押せばワンクリックで 非公開(下書き) に戻せる


画面構成と UX の工夫

ダッシュボード(エディタ × プレビュー)

  • 2 カラム構成で「編集したら右側で即反映」
  • ヘッダにメールアドレス / 保存状況 / 共有 URL を表示
  • 編集中は hasUnsavedChanges をバッジ表示して 保存し忘れ防止

編集タブ

shadcn/ui の Tabs を使い、以下を横断的に編集可能:

  • プロフィール
  • 技術スタック
  • プロダクト
  • 職務経歴

プロダクトや職務経歴は Dialog(モーダル)から CRUD。画像は Storage にライブアップロードし、プログレスも表示する ImageUpload コンポーネントを自作。

公開ページ /p/:slug

  • Cloud Functions の REST API からポートフォリオを取得
  • エディタと同一コンポーネントで表示するため 編集と公開の見た目が完全一致
  • 存在しない slug / 非公開の slug ならエラーページを返す

アーキテクチャと技術スタック

[React (Vite)] --Auth Token--> [Firebase Auth]
     │
     ├─ httpsCallable ───> Cloud Functions v2 (get/update/publish)
     │                      │
     │                      └─ Firestore (portfolios, viewCount)
     │
     ├─ Storage SDK ──────> Cloud Storage (avatars / products)
     │
     └─ HTTP fetch ───────> Cloud Functions v2 (publicPortfolio / incrementView)
                             │
                             └─ Firebase Hosting (/p/:slug)

技術スタック

フロントエンド

  • React 18 + TypeScript + Vite
  • Tailwind CSS + shadcn/ui
  • React Query(取得・更新の非同期制御)

Firebase

  • Authentication(GitHub / Google)
  • Firestore
  • Cloud Storage
  • Cloud Functions v2
  • Hosting
  • App Check(reCAPTCHA v3)

開発環境

  • Firebase Emulator Suite(Auth / Firestore / Storage / Functions)

実装詳細(ポイント解説)

1. 認証と初期データ生成

  • useAuth フックで onAuthStateChanged を購読し、サインイン後に callable API getPortfolio を実行
  • Firestore にデータが無ければ空のポートフォリオを自動生成
  • auth/account-exists-with-different-credential も捕捉し、ユーザーに適切なプロバイダを案内

2. 編集体験の最適化

  • Firestore データは一度だけ取得し、以降の編集はローカル state (draft) で完結
  • 保存ボタン押下時のみ updatePortfolio を実行し、無駄な通信を削減
  • 「公開する」ボタンに保存処理を内包し、未保存の変更があれば 保存 → 公開 を自動で直列実行
  • ImageUpload コンポーネントは 2MB 超を即バリデーションし、uploadBytesResumablestate_changed で進捗 UI を更新、完了後に downloadURL をフォームへ反映

3. プレビュー & 公開ページ

  • PortfolioPreview をエディタと公開ページで共用し、デザイン差分を物理的に排除
  • /p/:slug では REST API (publicPortfolio) に X-Firebase-AppCheck ヘッダ付きで fetch を実行
  • 取得成功後に incrementPortfolioView を fire-and-forget で呼び出し、トランザクションで閲覧数を正確に更新

4. Cloud Functions(バックエンド)

  • v2 Functions (onCall) で getPortfolio / updatePortfolio / publishPortfolio を実装し、Auth + App Check を必須化
  • sanitizePortfolio で入力バリデーションを統一し、同じロジックをバックエンドに集約
  • 公開 slug は randomUUID() の 8 文字スライスを採用し、Firestore で重複チェックしてリトライ
  • 公開 API は publicPortfolio / incrementPortfolioView を提供し、後者はトランザクションで view を更新

セキュリティと運用

Firestore Rules

  • portfolios/{id}request.auth.uid == resource.data.uid の場合だけ読み書き可
  • 公開状態では isPublic == true の場合のみ読み取り許可
  • views コレクションは Functions だけが書き込み

Storage Rules

  • /avatars/{uid}/** /products/{uid}/** は本人のみアップロード可
  • 表示は公開して問題ない階層に限定

App Check

  • フロントで initializeAppCheck を実行
  • Functions 側は enforceAppCheck: true またはヘッダ検証で強制

ログ / 監視

  • functions.logger を各所で活用し、Cloud Logging / Error Reporting で状況把握

実装しての学び・Tips

  • UI と公開ページを 同一コンポーネント で構成すると品質が安定する
  • App Check は最初から導入すると後で泣かない
  • React Query × ローカル状態は「即時反映 × Firestore 負荷削減」の最適解
  • バックエンドにもバリデーションを書くと安心感が段違い

今後追加したい機能

  • 公開ページデザインのテンプレート機能
  • バージョン履歴とロールバック
  • PDF / Markdown エクスポート
  • 公開時の Slack / Discord 通知
  • カスタムドメイン対応

おわりに

PortfolioBuilder は、「編集と公開で見た目がズレる問題」「公開 / 非公開切り替えが面倒問題」を解消したくて作ったプロダクトです。React × Firebase のシンプル構成でも UI/UX をここまで作り込めるという実感を得られました。

もし「ポートフォリオ作らないとな…」と感じている方がいたら、ぜひ触ってみてください。フィードバックをいただけると嬉しいです!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?