20
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

はじめに

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

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

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

Firebaseってまず何よ?

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

Firebaseの初期画面は

スクリーンショット 2020-07-02 15.55.13.png

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

スクリーンショット 2020-07-02 15.55.37.png

になっています。

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

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

の機能があります。

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

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

データベースって?

まずは、データベースから見ていきます。
データベースは現在「Cloud Firestore」が標準のようです。昔はRealtime Databaseでしたが、こちらのCloud Firestoreがより高機能で最新っぽいです(機能が違うので、Realtime Databaseのほうが良いこともあります)

image.png

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

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

スクリーンショット 2020-07-02 16.03.26.png

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

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

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

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

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

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

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

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

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

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

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

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

このコードは
ClouldFireProject_-_ClouldFireProject_-_Firebase_コンソール.png

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

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

認証ってどうやるの?

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

これは
スクリーンショット 2020-07-02 16.16.10.png

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

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

スクリーンショット_2020_07_02_16_17.png

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

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

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

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

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

    <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>

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

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

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

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

登録をするときは

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などで安全に保ってくれるのでうれしいですね><

ログインをするときは

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

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

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

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

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

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

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

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

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

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

スクリーンショット_2020_07_02_16_17.png

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

ユーザIDは

    let uid = auth.currentUser.uid;

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

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

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

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

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

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

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

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

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

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

というのがありません。

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

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

スクリーンショット 2020-07-02 16.36.48.png

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

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

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

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

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

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

スクリーンショット_2020_07_02_16_40.png

上記のように、

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

のようになっています。

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

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

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

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

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

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

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

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

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

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

スクリーンショット 2020-07-02 16.53.35.png

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

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

理解が非常にあやしいのでルールを参考にしてみてください。

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

ホスティングを参考にしました。

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

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

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

良いチュートリアルない?

最後に

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

20
19
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
20
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?