Next.jsでFirestoreのデータをSSGする

Firebaseを使ったアプリを作成する際、
Firestoreのデータをクライアント側でFetchすることが多かったのですが、

- リアルタイムでの更新は不要
- Firebase料金の見積もりを簡単にしたい（リクエスト量に応じて変動されたくない）

という要件での開発を求められそうだったので、
**「これはSSGしかない!！」**ということで勉強したことを備忘としてまとめます。

# Next.js内でのFirebaseの初期化

### 【訂正： 2021/06/14】
**初期化方法を訂正いたしました。**
**クライアント側で必要な初期化方法を掲載していましたが、**
**今回はSSGをするのでfirebase-adminでの初期化が必要でした。大変失礼いたしました。**

**詳しくはこちらで説明しています。**
https://qiita.com/centerfield77/items/799f2840f48221c2f84d

---

初期化は[firebase-admin](https://firebase.google.com/docs/admin/setup?hl=ja)を使います。
firebase-admin は特権環境から Firebase を操作するため、自分で設定したFirestoreのルールに関係なくサーバー側から Firestore にアクセスできます。

firebase-admin を使用するにはまず`プロジェクトの設定`の`サービスアカウント`から`新しい秘密鍵の生成`をクリックし、秘密鍵（json ファイル）をダウンロードします。

※このファイルが外部に漏れたら事故です。

![スクリーンショット 2021-06-12 16.22.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631600/426ad870-e667-5378-4112-40d515b1af4b.png)

ダウンロードした json ファイルの中に`project_id`, `client_email`, `private_key`があるので、それを`.env`に追加します。

```..env
FIREBASE_PROJECT_ID="***************"
FIREBASE_CLIENT_EMAIL="***************"
FIREBASE_PRIVATE_KEY="***************"
```

初期化します。

```/lib/db.js
import * as admin from 'firebase-admin';

if (!admin.apps.length) {
  admin.initializeApp({
    credential: admin.credential.cert({
      projectId: process.env.FIREBASE_PROJECT_ID,
      clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
      privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n'),
    }),
  });
}

export const db = admin.firestore();
```


今回はfirestoreしか使わないのでfirestoreを`db`としてエクスポートしています。

# getStaticProps()内でFirestoreのデータを取得する

Firestoreのデータはとてもシンプルなものを用意しました。
![スクリーンショット 2021-02-21 18.30.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631600/9f3598b7-e0e4-bf88-9c37-788ef4086335.png)

これを`getStaticProps()`内で取得し`tasks`という変数で返して、
それを上に書いてある`function Firebase()`で受け取りレンダリングします。


```/pages/Firebase.js
import { db } from '../lib/db';
import Link from 'next/link';

export default function Firebase({ tasks }) {
  return (
    <>
      <h1>Firebaseのページ</h1>
      <ul>
        {tasks.map((task) => (
          <li key={task.id}>{task.title}</li>
        ))}
      </ul>
      <Link href={`/`}>
        <a>戻る</a>
      </Link>
    </>
  );
}

export async function getStaticProps() {
  const tasks = [];
  const ref = await db.collection('tasks').get();
  ref.docs.map((doc) => {
    const data = { id: doc.id, title: doc.data().title };
    tasks.push(data);
  });
  return {
    props: {
      tasks,
    },
  };
}
```

# ブラウザで確認

![スクリーンショット 2021-02-21 18.39.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631600/44282b67-d21f-4f8b-165a-540d629e1880.png)

問題なくFirestoreのデータが表示されてることを確認できました。

# 補足：Vercelでデプロイする場合

Vercel上でも環境変数の設定をする必要があります。

プロジェクトを選択 → `Settings` → `Environment Variables`
`Plaintext`にチェックを入れて一つずつ追加していきます。
![スクリーンショット 2021-02-21 18.44.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631600/72734b50-862c-f17a-7dd1-cb49062aacab.png)

これでVercel上でビルドする際にもFirebaseの情報を取得できるようになります。
デプロイ後に本番環境でも問題なく閲覧できていることを確認できました。
![スクリーンショット 2021-02-21 18.47.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631600/5642eebf-ae37-e72c-1c72-1e08348204dc.png)