Firebase初心者がFirebaseで詰まったところまとめ


# はじめに

こにゃにゃちは！ganariyaです！

本記事では、「Firebaseを初めて触った人間が、**初めて触るときに躓きそうな点**について」まとめようと思います。
そのため、Firebase全体に関する説明ではないのでご容赦ください＞＜

Q&A形式でまとめていきます！

# Firebaseってまず何よ？

Firebaseは、Googleが作っている「Webアプリやスマホアプリを作るときに便利であるようなツールを全部まとめたもの」です。

Firebaseの初期画面は

<img width="857" alt="スクリーンショット 2020-07-02 15.55.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/f49af883-2183-4f67-6912-e225e6bb1cb8.png">

のようになっており、プロジェクトごとの画面は

<img width="1015" alt="スクリーンショット 2020-07-02 15.55.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/44964668-2eb3-80a5-aa15-2818211ad1ce.png">

になっています。

Firebaseでは、**プロジェクト**ごとに作成していきます。
各プロジェクトには

- Authentication（認証）
- データベース（データを格納）
- Storage（使ってないけど多分静的コンテンツを入れれる）
- Hosting(作ったサイトをデプロイできる)
- Functions（セキュリティ周りなど、フロントから直接触らせるのいやなときにかますための関数）
- Machine Learning(たぶん機械学習、それはそう)

の機能があります。


また、各プロジェクトにはさらに「アプリ」の概念があります。
例えば、仮にQiitaだとすると「Qiita」「Qiita　Jobs」「Qiita　Teams」という感じでしょうか･･･。アプリがあるメリットとして、データベースや認証を共通したいけれど、アプリとしては分けたい･･･　という意味で存在しているのだと思います。

とりあえず、Firebaseは**認証からデータベース・ホスティングまでサーバーサイドをすべて肩代わりしてくれるサービス**と思うと良さそうです。

# データベースって？

まずは、データベースから見ていきます。
データベースは現在「[Cloud Firestore](https://firebase.google.com/docs/firestore?hl=ja)」が標準のようです。昔はRealtime Databaseでしたが、こちらのCloud Firestoreがより高機能で最新っぽいです（機能が違うので、Realtime Databaseのほうが良いこともあります）


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/e10f2e0a-d360-ce85-49a8-2c38e47b6730.png)

Firestoreは、NoSQL型のデータベースです。
上記のように、コレクションの中にドキュメントを入れる形になります。

コレクションがSQLデータベースのテーブル名、ドキュメントが各行、ドキュメントの属性が各列に値します。

<img width="734" alt="スクリーンショット 2020-07-02 16.03.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/442f7777-dd3b-7997-8f68-3bf0a771851d.png">

上記だとguidesというコレクション（フォルダ）があり、ドキュメントが4つ存在します。
そのうち、１つ目のドキュメントにはcontent, titleが格納されています。

**ドキュメント自体にも名前がある**ことに注意してください。
イメージ的には`document.json`というdocument名がついており、それをエディタで開くと`content`, `title`のような実際の中身が入っているイメージです。


そもそも、なぜ「データベース」をGoogleのサーバーでやるのでしょうか？
これは、**サーバー側のデータベース作成をしなくてよい**ためです。これまでは、VPSサーバなどを借りてApacheやNginxを入れて、さらにMySQLのソフトウェアをインストール、設定などをいちいち行う必要がありました。また、クエリが多く飛んで落ちる可能性もあります。

そこで、Googleが代わりにデータベースを作ってくれれば、僕たちはそれに「アクセス」するだけで良くなります。

ここで、「アクセス」は「**クライアントサイド**」からされることに注意が必要です。
これまでのデータベースでは、サーバー側のPHPプログラムといった、サーバー側のプログラムがデータベースサーバにアクセスするという形をとっていました。クライアント側のJavaScriptやHTMLのPOST通信が、サーバーにアクセスして、サーバーがプログラムを実行し、データベースまで取りに行っていたのです。
しかし、これではサーバー側のPHPプログラムなどの実行がボトルネックになってしまいます。

一方、CloudFireStoreは、「クライアントサイド」から**直接**データベースにアクセスします。そのため、サーバーサイド側のプログラムが必要なくなり、フロントエンド側でJavaScriptやTypeScriptで完結することになります。手間が省けますね＞＜

ただし、その分セキュリティには注意する必要があります。

# Webアプリでの「初期コード」ってどこにあるん？

QiitaなどでFirebaseチュートリアルなどをやると、

```javascript
        var firebaseConfig = {
            apiKey: "",
            authDomain: "",
            databaseURL: "",
            projectId: "",
            storageBucket: "",
            messagingSenderId: "",
            appId: "",
            measurementId: ""
        };
    firebase.initializeApp(firebaseConfig);
```

のfirebaseを使用するために必要な初期コードが求められます。

これ、どこにあるか探すのに時間がかかりました。

このコードは
<img width="1015" alt="ClouldFireProject_-_ClouldFireProject_-_Firebase_コンソール.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/ee7bc47a-6cd3-1464-c685-4dedfb5a4da0.png">

初期画面のこのコードのところにあります。

ここで、Webアプリを作りますか？というのが出たら作る！を選んであげてください。


# 認証ってどうやるの？

ユーザ認証をするときはどうすればいいのでしょうか？
Webアプリなので、多くの人間が使うことを想定すると、「ある人間のデータは他の人間に見れないようにログイン機能を作る」必要があります。



これは
<img width="1014" alt="スクリーンショット 2020-07-02 16.16.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/eebe4044-c3d6-a1de-c7df-eeb6341aa183.png">

AuthenticationのSign−in methodからログインで使いたいものを選択すればいいです。
僕はとりあえずメール・パスワードを選択しました。

ログインをユーザがすると

<img width="801" alt="スクリーンショット_2020_07_02_16_17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/557f3601-0b85-a4fb-5fbe-9468951c8ee1.png">


Usersに認証データが追加されます。
ここで、**Authenticationの下のDatabase（Firestore）には、ユーザの認証データは入っていません。**
Authenticationで一括管理されているようです。

Googleが全部認証をやってくれるので、パスワードの暗号化し忘れみたいなやばいことを避けられますね＞＜


# 実際にどう登録・ログイン・ログアウトするねや？

実際に登録やログアウトを実装するときは、Firebaseが提供しているライブラリを用います。
今回は、javascriptでやりますが、PythonやPHPなど他のライブラリでも基本は同じだと思います。

今回はnpm, yarnなどは使わずhtmlで直接cdnにアクセスする形にします。

```html
    <script src="https://www.gstatic.com/firebasejs/7.15.5/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.15.5/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.15.5/firebase-firestore.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.15.5/firebase-functions.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.15.5/firebase-analytics.js"></script>
```

ここらへんをとりあえずインポートしてあげます。
その後、先程の初期化をします。

```javascript
        var firebaseConfig = {
            apiKey: "",
            authDomain: "",
            databaseURL: "",
            projectId: "",
            storageBucket: "",
            messagingSenderId: "",
            appId: "",
            measurementId: ""
        };
    firebase.initializeApp(firebaseConfig);

      const auth = firebase.auth();
        const db = firebase.firestore();
```

authは、認証のライブラリ、firestoreはfirestoreのライブラリです。

登録をするときは

```javascript
signupForm.addEventListener('submit', (e) => {
    e.preventDefault();

    const email = HTMLから取り出す
    const password = HTMLから取り出す

    // Userを作成する
    auth.createUserWithEmailAndPassword(email, password)
        .then(cred => {
            // 登録成功
            console.log(cred);
        })
})

```

などとすればいいです。今回はEmailAndPasswordでメールとパスワードを使用していますが、Googleアカウントなどの他の登録メソッドもあります。

認証が成功すると、次からは**自動でログイン状態**になっています。
一回登録・ログインすれば、特にクライアント側でコードを書かなくてもライブラリが自動でログイン状態をcookieなどで安全に保ってくれるのでうれしいですね＞＜


ログインをするときは

```javascript
loginForm.addEventListener('submit', (e) => {
    e.preventDefault();

    const email = HTMLから取り出す
    const password = HTMLから取り出す

    auth.signInWithEmailAndPassword(email, password)
        .then(() => {
            console.log('success')
        })
}
```

のようにすればいいです。ログインができ、ページ遷移することなく自動でログイン状態になります。

ログアウトでは、`auth.signOut();`をすればいいです。


このとき、**ログイン状態が変わったらコードを実行したいんだよね**（UIを変えたり）などがしたいと思います。
このようなときは

```javascript
auth.onAuthStateChanged(user => {
    if (user) {
        // login state
    } else {
        // ログインしてない
    }
})
```

のように、ログイン状態が変わったら自動発火するイベントが用意されているため、コールバックを登録してあげればいいです。

# ログインしているユーザのID知りたいんだけど

ログインしているとき、ユーザ認証では各ユーザにIDが割り振られています。
ユーザIDは



<img width="801" alt="スクリーンショット_2020_07_02_16_17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/557f3601-0b85-a4fb-5fbe-9468951c8ee1.png">


の最も右欄で確認できます。

ユーザIDは

```javascript
    let uid = auth.currentUser.uid;
```

で取得できるため、ユーザのログイン状態は間接的にこれがnullかどうかでもチェックできそうですね＞＜

このuidを使えば、データベースもうまく設計できそうです。


# ユーザごとにデータベース作りたいんだけど

この設計が正しいかわからないため、ご意見をいただければと思います。


通常のMySQLなどのリレーショナルデータベースでは、

- Usersテーブル
    - ユーザーID
    - パスワード
- Todosテーブル
    - todo ID
    - name
    - **ユーザーID**

みたいになっています。
こうすることで、今ログインしているユーザのIDを用いて、Todosテーブルと関連付けてユーザーIDが一致している行だけを引っ張ってきます。

ただ、これはNoSQL（Firestore）だと適切か分かってないです（アドバイスいただければと思います）。

SQLと異なり、Firestoreはjsonに近い設計であるため

- **ここにこのデータしか入れれない！**
- **テーブルごとの設計にしろ！**

というのがありません。

これらはクライアントサイドで注意して行う必要があります。
良く言えばリレーショナルデータベースよりも柔軟で変化させやすいというのがありますが、ミスすると変な設計になる　という裏返しみたいな状況になります。
リレーショナルデータベースは、これを設計手法でガチガチに固めています。


そのため、今回は以下のようにTodosを設計しました。

<img width="740" alt="スクリーンショット 2020-07-02 16.36.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/53461536-ef97-3330-c94a-d5b785460ba6.png">

Todosには現在2個のドキュメントが入っています。
この２個のドキュメントの名前はユーザIDになっています。`auth.currentUser.uid`で取り出せるものですね。

この名前でドキュメント名をつけることで


`db.collection('users').doc(auth.currentUser.uid)`で
ユーザIDに関するTodoｓを取れるようにしています。

つまり、今回はユーザごとにTodoのデータベースを作っているイメージです（これが正しい設計なのかは分かりません･･･）

この`uid.document`には`user_todos`というコレクションを入れています。

これは**サブコレクション**であり、`コレクションのドキュメントにコレクション`を入れています。


<img width="1378" alt="スクリーンショット_2020_07_02_16_40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/4138677d-8b70-c2ff-0ca2-6a6ed03e6325.png">


上記のように、

todosコレクション > ユーザIDドキュメント > user_todosドキュメント > ランダム生成ドキュメントID > {content, title...}

のようになっています。


こうすることで、ユーザIDに関係するデータのみ持ってこれるようにはなります。


# データベースが変化したときにメソッド実行したいんだけど

データベースにデータが登録・削除など、データベースに変更があったときに、なにかUIを変化させたい・表示内容などを変えたいなどがあります。
これも認証と同じように、イベントが設定できます。

```javascript
    db.collection('todos').doc(user.uid).collection('user_todos').onSnapshot(snapshot => {
            let docs = snapshot.docs;
        })

```


上記は、todosコレクションのuserIDのドキュメントのuser_todosコレクションに変更が会ったときに実行されるイベントです。
snapshotには、変更されたときのuser_todosコレクション全体のデータが入っています。


https://firebase.google.com/docs/firestore/data-model?hl=ja

Firestoreには、「reference」と「実体」があるようです。
感覚が全然つかめていないのですが、`let todosRef = db.collection('todos')`などは、todosコレクションへの参照状態を変数としており、まだデータを取ってきていない？ようです。
実際に撮ってくるときは.get()などをつけるようです。

もっと勉強しようと思います（アドバイスいただければと幸いです）


# セキュリティってどう担保するの？

データベースには、フロントエンド（ユーザ）から直接アクセスされます。
そのため、かなり念入りに「他のユーザからデータを好き勝手触られないように設定する」必要があります。

これはデータベースのルールから設定できます。

<img width="1012" alt="スクリーンショット 2020-07-02 16.53.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/b6bf2876-3ffc-9d9f-983a-f2064fe6efc8.png">

上記のように、**アクセスするドキュメント**ごとに誰がアクセスしていいのか？などの設定をします。（コレクションではないです）

`/users/{userId}`ですが、`{userId}`はドキュメント名`{userId}`を変数として持っています。
そのため、`request.auth.uid != null`などをチェックすることで、ドキュメントへのアクセスを制限できます。

理解が非常にあやしいので[ルール](https://firebase.google.com/docs/firestore/security/get-started?hl=ja)を参考にしてみてください。

# ホスティングってどうやるの？

[ホスティング](https://firebase.google.com/docs/hosting/quickstart?hl=ja)を参考にしました。

Firebase CLIを入れた後、ステップにしたがってdeployしたら、簡単にpublicフォルダ以下をデプロイできました。すごいですね（すごい）

今回、public以下で作業をしていなかったので、カレントディレクトリから小ディレクトリのpublicフォルダにhtmlやjsを移しました。

Nuxtなどで作業すれば、ビルドしたものをpublicとしてフォルダに出力すれば簡単にホストまで行けそうです！

# 良いチュートリアルない？

- https://www.youtube.com/playlist?list=PL4cUxeGkcC9itfjle0ji1xOZ2cjRGY_WB
- https://www.youtube.com/watch?v=-OKrloDzGpU
- https://www.youtube.com/playlist?list=PL4cUxeGkcC9jUPIes_B8vRjn1_GaplOPQ
- https://www.youtube.com/playlist?list=PLl-K7zZEsYLluG5MCVEzXAQ7ACZBCuZgZ (今度見る)

# 最後に

だいぶ簡潔にまとめました。
分かりづらい・間違っている・伝わりづらい点がありましたら、ご指摘いただければ嬉しいです！
ありがとうございました！！