Firebase 認証情報をFirestoreに保存する方法

# 概要
Firestoreでユーザーのプロファイル（情報）を保存し、Authenticationで認証を行う方法が
まとまっていなかったので記事にしました！

**前提：Firebaseコンソールからプロジェクトの設定が終わっていること**


# 考え方
考え方としては、以下のようになります！

1. AuthでsignUpする
1. AuthのcurrentUserを取得する
1. 取得した情報をFirestoreに保存する

Firestoreに保存する情報は、Authenticationの一意なIDである`uid`を使います！

# 実装
## config
プロジェクト作成時に提示される内容をコピーします。
~~APIキーなどは漏れて不正利用されると大変なので~~
~~`.env`に保存しておきます。~~

→Firestoreのセキュリティルールを変更しましょう。

:::note warn
Nextで`.env`に保存するときは`NEXT_PUBLIC_`で始まる環境変数でないといけません
:::

```ts:src/utils/Firebase/config.ts
// Import the functions you need from the SDKs you need
import { initializeApp } from 'firebase/app'

// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
  databaseURL: process.env.NEXT_PUBLIC_DB_URL,
}

// Initialize Firebase
export const app = initializeApp(firebaseConfig)
```

## Firestoreにデータを保存する関数
こちらもほぼ[公式ドキュメント](https://firebase.google.com/docs/firestore/manage-data/add-data?hl=ja&authuser=0#add_a_document)通りにコピペします。
```ts:src/utils/Firebase/firestore.ts
import { app } from '~/utils/Firebase/config'
import { getFirestore, addDoc } from 'firebase/firestore'

// Initialize Cloud Firestore and get a reference to the service
const db = getFirestore(app)

export const insertData = async (table: string, data: Object) => {
  try {
    const docRef = await addDoc(collection(db, table), data)
    console.log('Document written with ID: ', docRef.id)
  } catch (e) {
    console.error('Error adding document: ', e)
  }
}
```

## signUp

### 関数の実装
ここでも[公式のドキュメント](https://firebase.google.com/docs/auth/web/password-auth?hl=ja&authuser=0#create_a_password-based_account)通りに実装します。
（エラーの検知用フラグを入れるなど、若干工夫をしています）

```ts:utils/Firebase/auth.ts
import {
  createUserWithEmailAndPassword,
  getAuth,
} from 'firebase/auth'
import { UserResponse } from '~/types/user'
import { app } from '~/utils/Firebase/config'

const auth = getAuth(app)

export const authSignUp = async (email: string, password: string) => {
  let response = undefined
  let isError = false
  await createUserWithEmailAndPassword(auth, email, password)
    .then((userCredential) => {
      // Signed in
      const user = userCredential.user
      response = user
    })
    .catch((error: ErrorEvent) => {
      response = error.message
      console.error(response)
      isError = true
    })
  return { isError, response }
}
```
フォーム部分は`react-hook-form`を採用しています。
UIの話は今回はしないので、サインアップボタン押下後に動作する関数を見てみましょう。

### サインアップボタン押下後の関数
#### コード
```ts:src/components/register/useProps.ts
  const signUp = useCallback(async () => {
    const values = getValues()

    const { isError, response } = await authSignUp(
      values['mail'],
      values['password']
    )

    if (isError) {
      alert(response)
      return
    }

    const currentUser = getAuth().currentUser
    const user = {
      name: values['name'],
      role: RoleType.student,
      uid: currentUser!.uid,
    }

    await insertData('users', user)
  }, [])
```

#### 解説
流れに沿って見てみましょう。

##### authのsignUp
`const { isError, response } = await authSignUp(`では
先ほど作成した関数を用いてAuthentication自体のサインアップを行います。
Firebase Authenticationの`signUp`関数はサインアップと同時にサインインも
行ってくれるので、ここで`signIn`の関数を実行する必要はありません🙌

##### currentUser
`const currentUser = getAuth().currentUser`では
アプリのFirebase Authenticationの情報を取得し、
現在サインインしているユーザーを取得します。
このcurrentUserには`uid`以外の情報も含まれているので
気になる方はドキュメントを見てみてください〜

##### userオブジェクト
```ts
const user = {
      name: values['name'],
      role: RoleType.student,
      uid: currentUser!.uid,
    }
```
ユーザーオブジェクトには、Firestoreに保存したい情報を格納します。
メールアドレスとパスワードはAuthenticationの方で保存しているので
Firestoreには保存せず、名前、役割、`uid`程度で留めておきます。

##### insertData
こちらは最初に作成した関数です。
Firestoreの任意のテーブル（コレクション）でドキュメント（データ）を追加するもので
今回は、`users`コレクションに名前と役割、`uid`を保存します。

# 終わりに
いかがでしたか？
流れを理解し、Authenticationの知識を持っていれば
意外にも簡単に実装できました！
みなさんももし使う機会があれば、遊んでみてください〜✨
それでは👋

# 参考文献

https://firebase.google.com/docs?authuser=0&hl=ja