【第3回】Firebase AuthenticationとFirestoreの「親子関係」設計
前回のReact実装編に続き、今回はバックエンド(Firebase)の設定と、データの持ち方について。
1. Firebase Authentication(Googleログイン)
ユーザーごとにデータを管理するため、Googleログインを導入しました。ここでは「認証状態をどう監視し、画面をどう切り替えるか」がポイントです。
認証状態のリアルタイム監視
onAuthStateChanged を使用して、ユーザーのログイン・ログアウトを常に監視しています。
function App() {
const [user, setUser] = useState(null);
useEffect(() => {
// ログイン状態が変わるたびに実行される「監視役」
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
setLoading(false);
});
// コンポーネントが消える時に監視を止める(メモリリーク防止)
return () => unsubscribe();
}, []);
return (
<Router>
<Navbar user={user} />
<Routes>
{/* ユーザーが存在すればHome、いなければLoginを表示(条件付きレンダリング) */}
<Route path="/" element={user ? <Home user={user} /> : <Login />} />
{/* ...他のルート */}
</Routes>
</Router>
);
}
自分だけのデータを取得する
データを保存する際に uid: auth.currentUser.uid を付与し、取得時にはそのIDでフィルタリング(where句)することで、他人のデータが混ざらないようにしています。
const q = query(
collection(db, "trips"),
where("uid", "==", auth.currentUser.uid), // 自分のIDと一致するものだけ!
orderBy("createdAt", "desc")
);
2. Firestoreのデータ構造(親子関係:サブコレクション)
支出データ(transactions)を管理する際、「どの支出が、どの旅行に関連するものかを明確にするため、Firestoreの階層構造(サブコレクション)を利用しました。
データ構造のイメージ
-
親 (Parent):
tripsコレクション(旅行の基本情報) -
子 (Child):
transactionsサブコレクション(その旅行内での支出明細)
サブコレクションへの保存実装
React Routerの useParams で取得した id(旅行プランのドキュメントID)をパスに含めることで、親子関係を構築しています。
const addItem = async (newTitle, newPrice, newCategory) => {
if (!rate) return alert("為替レートを取得中です...");
const priceInJPY = Math.round(Number(newPrice) / rate);
try {
// 保存先の参照:trips -> {現在の旅行ID} -> transactions という階層を作る
const transactionsRef = collection(db, "trips", id, "transactions");
await addDoc(transactionsRef, {
name: newTitle,
priceLocal: Number(newPrice),
priceJPY: priceInJPY,
category: newCategory,
createdAt: new Date(),
});
} catch (e) {
console.error("保存失敗: ", e);
}
};
{isModalOpen && (
// 「項目を追加」のモーダル部分。
<AddTransactionModal
setIsModalOpen={setIsModalOpen}
addItem={addItem}
categories={categories}
/>
)}
第3回記事の後半部分ですね!セキュリティとデプロイは、アプリを「個人の練習台」から「社会に出せるサービス」へと引き上げる重要なステップです。
Qiitaで読者がそのまま真似できるように、具体的なルールコードを載せつつ、エンジニアらしい「なぜこれが必要か」という視点を補強して整えました。
3. セキュリティルール(Firestore Rules)
FirebaseはAPIキーがフロントエンドに露出するため、それだけでは誰でもデータベースを操作できてしまうリスクがあります。その「最後の砦」となるのがセキュリティルールです。

「自分以外のデータは絶対に見せない」鉄壁の守り
Firestoreのルールタブを以下のように書き換えることで、ログインユーザー本人以外のアクセスを完全に遮断しました。
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// tripsコレクションのルール
match /trips/{tripId} {
// データのuidと、操作しようとしている人のuidが一致する場合のみ許可
allow read, update, delete: if request.auth != null && request.auth.uid == resource.data.uid;
// 作成はログインしていればOK
allow create: if request.auth != null;
// 支出(transactions)サブコレクションも親ドキュメントの所有者のみ
match /transactions/{transactionId} {
allow read, write: if request.auth != null &&
get(/databases/$(database)/documents/trips/$(tripId)).data.uid == request.auth.uid;
}
}
}
}
ここで使っている resource.data.uid は、DB内に保存したデータの持ち主。一方 request.auth.uid は、今アクセスしている本人のIDです。この一行が一致しない限り、たとえAPIキーが知られてもデータが盗まれることはありません。
4. Firebase Hosting で世界へ公開(デプロイ)
最後は、作成したアプリを世界中からアクセス可能なURLとして発行します。Firebase Hostingを利用することで、インフラ管理を意識せずスムーズに公開できました。
デプロイの手順
-
Firebase Tools のインストール
CLIツールをインストールして、PCからFirebaseを操作できるようにします。
npm install -g firebase-tools
-
初期化設定(
firebase init)
プロジェクトの紐付けを行います。Viteを使用している場合、公開ディレクトリの設定には注意が必要です。
-
Public directory:
dist(Viteのデフォルト出力先) -
Single-page app:
Yes(React Routerを正しく動作させるために必須)
-
本番用ビルドとデプロイ
コードを最適化された静的ファイルに変換し、サーバーへアップロードします。
npm run build
firebase deploy
おわりに
5日間という短期間でしたが、Reactの基礎からFirebaseによるバックエンド構築、そしてデプロイまでを一通り経験することができました。
「自分で課題を見つけ、それを解決するためのツールを技術で形にする」というプロセスは、4月からエンジニアとして働く上で大きな自信になりました。
今後はこのアプリを実際に旅行で使いつつ、さらなるスキルアップに励みたいと思います!
実際に公開してるサイトはこちら!:https://tabimoney-64c36.web.app/


