uwu

プログラミングの備忘録を書いています。誰かの為になれば幸いです

Firestore Cloud firestoreの公式動画まとめ3

前回の記事の続きです。12まであるのでサクッと終わらせたいところですが
こうして文字に起こしてると結構な時間がかかりますね。
今回の記事はFirestoreの料金についての動画のまとめになります。




www.youtube.com
※この記事で使われている全ての画像はこの動画内から切り抜いたものです。




・Real time databaseの料金設定



DBへダウンロード、アップロードしたデータの量で決まる



・Firestoreの料金設定



CRUD操作を実行した回数によって決まる



C、U操作はwritesになります。



30個の異なるフィールドを一度に変更しても1回としてカウント
20個のドキュメントを読み取ったとき、読み取り回数は20回とカウント
(Firestoreでは全ての検索をドキュメントそのものではなくインデックスを通して行われるため、
日本食レストランのトップ20を検索したときに、検索対象が3000万件あったとしても読み取り回数は20回)



利用料が高価になる例

まずは株式相場の表示をするアプリを例として話していきます。


そのアプリでは全ての銘柄の価格を15秒ごとに更新していて、
誰かがアプリを3分間開きっぱなしにしていた場合、12回の更新(12Read)を受けることになります。



この人が10種類の銘柄をフォローしていた場合、読み取り回数は120回となり
もし1000人のユーザーがアプリを使っていた場合、その3分間だけで12万回の読み取りが発生します。
このペースだと1日平均5750万回読まれることになります。これは約34ドル/日ほどで、それほどの価格ではありません。




では、グループで使えるホワイトボードアプリがあるとして、リアルタイムに絵を描く感覚を得るために、
1秒間に数回の更新を行っていたら、1秒間に多くのRead / Writeが発生する→つまり価格が高くなります。




このように一般的にはリアルタイム性が高く、
アクティブユーザーが多いアプリほど、Cloud Firestoreのコストは高くなります。


予期しないreads/writesにより料金が発生する例


・データベース内の任意のドキュメントをチェックするには、get()やexists()の呼び出しで行うが、この呼び出しは通常読み取りとしてカウントされる



・Cloud Functionsを使った場合



Cloud Functionsとは?
Cloud Functions と Firebase  |  Cloud Functions for Firebase



またレストランのアプリを例にとります。
Colud Functionを使って特定のレストランの評価の平均を計算し、結果の数値を更新するとします。
この場合、ユーザーがレビューを変更する度に全てのレストランのレビューが読み込まれ多くのreadが発生してしまい、予期せぬ価格になる場合があります。



・セキュリティルールを使った場合


セキュリティルールとは?
基本的なセキュリティ ルール  |  Firebase セキュリティ ルール



Google Cloudコンソール


console.cloud.google.comにアクセスして
Google Cloudコンソールから以下のことが確認/設定できます。



・データベースの使用状況
・1日の使用量の上限を設定
・バジェットの設定とアラート



データベースの使用状況は
「App Engine」→「Quotas」をクリック
チェックする項目→Firestore Read Operations/Firestore Entity Writes/Firestore Entity Deletes/Firebase Stored Data



1日の使用量の上限を設定するには
「Setting」→「Edit」


バジェットとアラートの設定は
「Billing」→「Budgets & Alerts」→「Create Budget」



また、Google Cloud のオペレーション スイート(旧称 Stackdriver)機能を使えば、
リクエスト数が多すぎたり少すぎたりする場合や、Cloud Functionsの実行に時間がかかりすぎている等を教えてくれます




Google Cloud のオペレーション スイートの概要 | Google Cloud Blog




4へ続きます。

Firestore Cloud firestoreの公式動画まとめ2

前回の記事の続きになります。


www.youtube.com
※この記事で使われている全ての画像はこの動画内から切り抜いたものです。



今回の動画では、
「クエリがどのように働くのか」
について詳しく説明してくれています。
では早速見ていきましょう!



DBからデータを取得する際、クエリと呼ばれるものを使います。



クエリを使ってデータを取得する時は、一つのコレクション
またはサブコレクションのドキュメントしか取得できません。


これはどういう意味か、
前回の動画同様レストランのレビューアプリのDBを例として見ていきます。



「レストランコレクション」の中に「レビューサブコレクション」があります。
レストランコレクションにはレストランの郵便番号などの情報が格納されているとします。
レビューサブコレクションにはレストランの評価が格納されているとします。







このようなDB構成の場合、「郵便番号ドキュメント」を指定して
その郵便番号と同じ場所にあるレストランを見つけることができます。



他にも、特定のレストランの4スターがついたレビューを取得したい場合、
「レストラン」コレクションの中にあるサブコレクションの、「レビュー」の中の
「評価」ドキュメントを指定することでデータを見つけることができます。



しかし、全てのレストランから4スターのレビューを取得することはできません。
複数のサブコレクションをまたがることになるからです。




2019年からできるようになった!

2019年から「コレクショングループクエリ」を
使用することでできるようになりました。



コレクショングループはFirestoreのコンソールから
どのフィールドを、どのコレクションから検索したいかを指定することで使用できます。





コレクショングループクエリには注意したいことが2つあります。


1.2019年時点では、インデックスは200個までという制限がある


端的に言うと、コレクショングループ=インデックスです。



2.コレクショングループクエリは同じ名前の全てのコレクションを探しにいく



つまり、もし同じ名前のサブコレクションが
別のコレクションに存在する場合、そのサブコレクションも含まれるということです。


インデックスの種類について:
Cloud Firestore のインデックスの種類  |  Firebase



クエリのルール


・実行するクエリは等価比較、大小比較を元に作成するべきである



どういうことかまたレストランのレビューアプリのDBを想定して説明します。



例えば、サンフランシスコにあるすべてのレストランを取得したい場合、
name = "San Francisco"
評価が星4以上あるすべてのレストランを取得したい場合
rating => 4
のような感じです。クエリに計算式を含めることはできません。



・クエリを実行するのに掛かる時間は、取得される結果の数に比例する


ある郵便番号でその地域にあるすべてのレストランを取得したい場合、レストランが
60件であっても60万件であっても600万件であってもクエリにかかる時間は一緒です。



なぜそのようなことが可能なのか??




Firestoreではドキュメントを追加する時に
ドキュメントの全てのフィールド、マップに自動的にインデックスを貼ります。
インデックスとは辞書でいう索引みたいなものです。




マップとは、以下のJSONみたいなフィールドのことです




上の例の場合、address.zipのインデックスが作成されます。
このインデックスがクエリを早くするポイントです。



クエリでできないこと

テキスト検索ができません。
フルテキスト検索のような検索をしたい場合、サードパーティを使う必要があります。



以下のようなORも使うことができません。
name = "italian" || name = "french"



ORは二つのクエリを使って得た情報を合体させることで実現できます。
name = "italian"
name="french"
また、DBを見直すこともできます。
例えば、イタリアンとフレンチに共通するヨーロッパ料理というフィールドを作り
true/falseで判定するといったこともできます。



!=も使用できません。


NoSQLは値に決まったデータ型を入れる必要がありませんが、
違うデータ型を値に入れることを推奨しません。




画僧のように評価を数値と、文字列で入れてしまった場合、
数値に対するクエリと文字列に対するクエリの2つを使わなければならなくなるからです。


複合クエリについて

例えば、サンフランシスコにある全ての日本食レストランでファミリー向けのレストランのデータを取得したいとします。
Cuisine = "Japanese"
City = "San Francisco"
KidFriendly = true



こういった全てが等価評価「=」で取得できるクエリなら問題ありません。



例えば、郵便番号○○番のエリアにある星4以上のレストランという条件で検索をする場合、Rating => 4という大小評価で取得しなければならないため勝手が変わります。




この問題をFirebase Real-time databaseではこのようにドキュメントに
「zipcode_rating:94045_4.76 」を追加することで解決します。





この複合されたフィールドが変更されるたびに更新をしたりと管理するのは大変です。
そこで、Firestoreでは複合クエリ(composite indexes)というものを使います。


複合クエリは自動的に複合したフィールドを作成し、値が変わるたびに自動で更新をしてくれます。



複合クエリはFirestoreのコンソールから作れるほか
複合クエリが必要なクエリを実行するとエラーを返してくれそのエラーに乗っているURLをクリックして作成することができます。(推奨)


複合クエリの詳細

・大小評価をするクエリは一番最後に持ってくるのがおすすめ
・大小評価をする2つのクエリを複合クエリにすることはできない

例)
rating >= 4.0
noise >= 3

noise_raitng >= ??? ←できない

rating >= 4.0
sort by rating
sort by noise

rating_noise >= 4.0 ←できる



3へ続きます。

Firebase Cloud firestoreの公式動画まとめ1

こちらの記事は公式のColud Firestoreの動画を見たただのまとめになります。
この動画は4年前のものにはなりますが、
現在作っているアプリでFirebaseのDBを使いたいと思っているので勉強してみました!
気付かずずっと英語で見てましたがこの動画のみ日本語字幕もあるみたいです。


公式の動画↓
www.youtube.com
※この記事で使われている全ての画像はこの動画内から切り抜いたものです。



MySQLSQLなどのリレーショナルDBとの違い
  • リレーショナルDB

それぞれのテーブルにそれぞれのスキーマがある
それぞれのカラムで制限されたルール(各フィールドのデータ型やデータの大きさ、主キーの選択など)がある
'Users'、'Projects'など1つのオブジェクトにつき1つのテーブルがある
他のテーブルと紐づけするにはもう一つの紐づける用のカラムを作成する(紐づけをリレーションと呼ぶ)



例として、レストランのレビューサイト用のDBを見ていきます。






レビューがどこのレストランのものなのかを知るためには
「Restaurants」と「Reviews」を紐づける必要があります。
この例では、「Reviews」のカラムResturants_FK(foreign key)に
レストランのIDを登録することで紐づけをしています。



また、レビューを書いたのが誰なのかを知るには
「Reviews」テーブルのAuthor_FK(foreign key)カラムに
レビューを書いた人のIDを保存することで可能にしています。




リレーショナルDBでは欲しい情報を手に入れるために
SQLのJOINを使って情報を取得します。



  • NoSQL DB

NoSQL DBはスキーマレスDBです。つまり、データベースの階層がなく、
各フィールドのデータ型やデータの大きさ、主キーの選択などの情報も必要ないことを意味します。
オブジェクトが必ず同じフィールドをを持つ必要もありません。
リレーションを持たないのでスケールアウトやスケールアップがしやすいです。




また、レストランのレビューサイトのDBを例に見ていきます。







スキーマレスDBの良いところは簡単にDBの構造を加えたり、変えたりすることができるところです。
例えば以下の画像のように「Noise_level」という値を新たに加えたいとします。





これを加えても、今までのレストランには値を加える必要がありません。
下の画像のようにまったく違う情報をいれることもできます。







NoSQL DBはSQLがありません。



下記の画像のようにレビューを取得したい場合、
reviews.where("Rest", '==", 24(レストランID))とすることで取得できます。





しかし、ここから更にレビューを書いたのが誰なのか、ユーザーの情報を取得したいとしましょう。
その場合このように何度にもわけて取得するのはよくありません。






この問題はユーザーの情報を「Reviews」にコピーして保存することで解決します。





でも、もしユーザーが名前を変えたりしたら、「Reviews」に
コピーしたユーザーの情報を変えなければいけなくなります。



そういったデメリットもありますが
それ以上に1つのテーブルの中に全ての必要な情報が入っていることは物事を簡単にします。
JOINを使って複数のテーブルからデータを取得しなくていい分データベースの読み込みが早いです。


Cloud Firestoreのドキュメント、コレクションモデル

Cloud Firestoreはドキュメントとコレクションで成り立っています。




ドキュメントはJSONオブジェクトと似ており、key:valueの値で構成されています。
valueには文字列、数値、バイナリデータ、JOSNのmap型などなんでも入れることができます。



コレクションはドキュメントを入れるコンテナーです。



Colud Firestore4つのルール
  1. コレクションの中にはドキュメントのみ入れることができます。
  2. ドキュメントのサイズは1MBまでで、これを超える場合分割する必要があります。
  3. ドキュメントの中にドキュメントを入れることはできません。ドキュメントの中にサブドキュメントを入れることはできます。
  4. Firestore Rootにはコレクションのみが入ることができます。

以上のルールがあるので、データを取得する時は
コレクション→ドキュメント→サブコレクション→ドキュメントと
このように欲しいデータを含むドキュメントにたどり着くまで指定を繰り返します。





これだとわかりづらいのでこのように取得したい
データまでのパスを指定して書くこともできます。






2へ続きます。

FirebaseのDB(Firestore)を使ってみたのでやり方まとめ

前回、前々回とFirebase Authenticationの
記事を書きましたが今回はFirebaseのDBを触っていきたいと思います。



FirebaseでDBを扱うためのサービスは2種類あります。



- Cloud Firestore
Cloud Firestore は、Firebase のモバイルアプリ開発用の最新データベースです。
直感的な新しいデータモデルで Realtime Database をさらに強化しています。
Cloud Firestore は、Realtime Database よりも多彩で高速なクエリと高性能なスケーリングが特長です。


- Realtime Database
Realtime Database は従来からある Firebase のデータベースです。
リアルタイムのクライアント間同期が必要なモバイルアプリのための、効率的でレイテンシが低いソリューションです。



詳しい違いは以下のDocを参考にしてください。
データベースを選択: Cloud Firestore または Realtime Database  |  Firebase Realtime Database



今回はCloud FirestoreをReactで使用してみたいと思います!


今回作るもの

メールアドレスとパスワードを入力できるフォームを使って
データを登録、データを取得、そのデータのIDを元に更新、削除を行っていきます!



今回はお試しなので見た目本当にシンプルです





データ構造

始める前にこれだけは覚えてほしいことがこのFirestoreのデータ構造です!
Firestoreは「コレクション」と「ドキュメント」によって構成されます。



コレクションは、ドキュメントを内包するための「フォルダ」のような概念。
ドキュメントは、フォルダ内にある「ファイル」のような概念。



MySQLで例えるなら、コレクション→テーブル、
ドキュメント→レコードといった感じです。



↑これだけわかっていたら今回の内容理解できます。
では早速作っていきましょう!


アカウントの登録


Firebaseに登録するには、Googleのアカウントが必要です。
あらかじめGoogleアカウントを作っておきましょう。登録は無料です。
Firebase利用料金は
Firebase Pricing
今回は無料のSparkプランを使っていきます。

まずはFirebaseの公式ページにアクセスします。
Firebase

アクセスしたら、Googleアカウントでログインします。

プロジェクトの作成


ログインしたらコンソールからプロジェクトを作成します。


プロジェクト名を入力しFirebaseの規約を読んだら
チェックボックス両方にチェックをいれ続行をクリックします。



Googleアナリティクスを使うか聞かれます。
今回は使わないのでoffにします。

そして「プロジェクトを作成」をクリックします。



しばらく待つとプロジェクトの作成が完了します!

アプリの登録


次に作成したプロジェクトにアプリの登録を行っていきます。

タグマークのボタンをクリック。



アプリのニックネーム(後で変更可)を入力しアプリを登録をクリック。





この画面になったらコードをコピペして控えておき
コンソールに進むをクリック。





(APIキーなど載せてますが消去済みです!)

Firestoreの設定

Firestoreにログインしてプロジェクト選択後、
こちらのコンソール画面でFirestoreをクリック。





「データベース作成」をクリック。






今回は「テストモードで作成」にチェックを入れ「次へ」をクリック。





ロケーションを選択して「有効にする」をクリック。
日本ならasia-northeast1→東京、asia-northeast2→大阪です。



Firestoreのロケーションについての詳細は以下のDocを参考にしてください。
Cloud Firestore のロケーション  |  Firebase






しばらく待ってこの画面になったらOKです!





Reactアプリの作成


今回はcreate-react-appを使ってReactアプリを作成します。

npx create-react-app react-test-firebase


作成できたらfirebaseをインストールしていきます。

npm install firebase


インストールしたらsrcディレクトリ内にconfigファイル「firebaseConfig.js」を作成し、
アプリを作成した時に控えておいたコードをコピペします。



コピペしてきたコードに以下を追記します。
以下は、Firestoreの初期化とサービスを使えるようにするためのものです。
exportしてどこでも使えるようにしています。

import { getFirestore } from 'firebase/firestore';
export const databse = getFirestore(app)

最終的にconfigファイルは以下のようなコードになります。



// Import the functions you need from the SDKs you need
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
// 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: 'AIzaSyBTSHwkPMgl9tyRaR2lUpozh6vQTppVLOM',
  authDomain: 'fir-test-e3b9d.firebaseapp.com',
  projectId: 'fir-test-e3b9d',
  storageBucket: 'fir-test-e3b9d.appspot.com',
  messagingSenderId: '805344665134',
  appId: '1:805344665134:web:b0b9c47a4f76533e3d49bf',
};

// Initialize Firebase
export const app = initializeApp(firebaseConfig);
export const database = getFirestore(app)


App.jsを以下のように書き換えます。

import './App.css';
import { useState } from 'react';
import { app, database } from './firebaseConfig';
import { collection, addDoc } from 'firebase/firestore';

function App() {
  const [data, setData] = useState({});
  const collectionRef = collection(database, 'users');

  const handleInputs = (event) => {
    let inputs = { [event.target.name]: event.target.value };

    setData({ ...data, ...inputs });
  };

  const handleSubmit = () => {
    addDoc(collectionRef, { email: data.email, password: data.password })
      .then((responce) => {
        alert('データ登録完了');
      })
      .catch((err) => {
        alert(err.message);
      });
  };

  return (
    <div className='App-header'>
      <input
        placeholder='Email'
        name='email'
        type='email'
        className='input-fields'
        onChange={(event) => handleInputs(event)}
      />
      <input
        placeholder='Password'
        name='password'
        type='password'
        className='input-fields'
        onChange={(event) => handleInputs(event)}
      />
      <button onClick={handleSubmit} />
    </div>
  );
}

export default App;

App.csscssを少し変えます。不要な方は飛ばしてください。


.App-header {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
}

.input-fields {
  margin: 10px;
}

これでデータを登録する準備が整いました。
あれ、コレクションとドキュメントは事前に作成しなくていいの??
と思われてる方もいるかもしれませんが(私)、



Firestore は、ドキュメントに初めてデータを追加するときに
コレクションとドキュメントを自動で作成してくれます。



これでEmailとパスワードを入力してみると
きちんと登録されました!!






ざっくりコードの説明


import { collection, addDoc } from 'firebase/firestore';

collection→CollectionReferenceです。コレクションの参照をしてくれるもの
addDoc→ドキュメントを作成するもの。IDが自動で生成される。



  const collectionRef = collection(database, 'users');

このコードはコレクションusersを参照するためのコードです。


  const handleSubmit = () => {
    addDoc(collectionRef, { email: data.email, password: data.password })
      .then((responce) => {
        alert('データ登録完了');
      })
      .catch((err) => {
        alert(err.message);
      });
  };


addDocに参照したコレクションとフォームに入力された
データを渡してドキュメントを作成しています。


データの取得

データの登録ができたので今度は取得してコンソールログに出力してみます。
データの取得にはgetDocs()を使います。
useEffectでブラウザ表示時に取得するようにしました。



import { collection, addDoc, getDocs } from 'firebase/firestore';

  useEffect(() => {
    getDocs(collectionRef).then((responce) => {
      console.log(
        responce.docs.map((user) => {
          return { ...user.data(), id: user.id };
        })
      );
    });
  }, []);

データの取得ができました!
次はデータのIDを元に更新・削除を行うコードも書いていきます。



データの更新・削除

データの更新


データの更新にはupdateDoc()を。
ドキュメントを参照するのにdocを使います。



こんな感じのコードになりました。


import {
  doc,
  updateDoc,
} from 'firebase/firestore';

 const handleUpdateData = () => {
    const docToUpdate = doc(database, 'users', 'KBiUwNse7gvE1PzMgwhy');

    updateDoc(docToUpdate, {
      email: 'aaa@aaa.com',
      password: '999999',
    })
      .then(() => alert('データが更新されました'))
      .catch((err) => {
        alert(err.message);
      });
  };

データの更新をしてくれるupdateDocに更新したいコレクション、
ドキュメントのIDを渡して更新したい値を渡しています。

const docToUpdate = doc(database, 'users', 'KBiUwNse7gvE1PzMgwhy');


の「KBiUwNse7gvE1PzMgwhy」がドキュメントのIDになります。




データの削除



データの削除にはdeleteDocを使います。


import {
  doc,
  deleteDoc,
} from 'firebase/firestore';

  const handleDeleteData = () => {
    const docToUpdate = doc(database, 'users', 'KBiUwNse7gvE1PzMgwhy');

    deleteDoc(docToUpdate)
      .then(() => alert('データが削除されました'))
      .catch((err) => {
        alert(err.message);
      });
  };

削除したいデータの情報をdeleteDocに渡してあげるだけでOKです。



以上でデータの登録、取得、更新、削除ができました。
こんな感じでざっくりまとめましたが、いかがでしょうか。



今回はほぼFirestoreの使い方のみしか勉強してないので
FirebaseでのDB設計の仕方やクエリ、Realtime databaseの使い方も勉強していきたいと思います!

Firebaseでソーシャルログイン!

前回の記事でFirebaseを使ってEmail/Password認証を作りました。
今回はFirebaseでGoogle認証とTwitter認証を作っていきたいと思います。



導入の部分だったり、コードは前回のものに継ぎ足して
実装していっているので前回の記事もぜひ参考にしてください。


前回の記事↓
2022年版!Firebase認証のやり方まとめ - 🤎uwu🤎



グーグル認証


まずはFirebase側でグーグルでの認証を使えるようにします。


Firebase コンソールで、Authenticationのセクションを開きます。
signinメッソドの 画面で「新しいプロバイダを追加」をクリック、




Googleサインイン方法を有効にし、自分のメールアドレスを入力した後「保存」をクリックします。





Firebaseの準備は以上です。次にApp.jsを下記のように変えます。


import './App.css';
import { useState, useEffect } from 'react';
import { app } from './firebaseConfig';
import {
  getAuth,
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
  signOut,
  onAuthStateChanged,
  signInWithPopup,
  GoogleAuthProvider,
} from 'firebase/auth';

function App() {
  const auth = getAuth();
  const googleProvider = new GoogleAuthProvider();

  const [data, setData] = useState({
    email: '',
    password: '',
  });
  const handleInputs = (event) => {
    let inputs = { [event.target.name]: event.target.value };

    setData({ ...data, ...inputs });
  };

  const handleSignin = () => {
    createUserWithEmailAndPassword(auth, data.email, data.password)
      .then((response) => {
        console.log(response.user);
      })
      .catch((err) => {
        alert(err.message);
      });
  };

  const handleLogin = () => {
    signInWithEmailAndPassword(auth, data.email, data.password)
      .then((response) => {
        console.log(response.user);
      })
      .catch((err) => {
        alert(err.message);
      });
  };

  const handleSignInWithGoogle = () => {
    signInWithPopup(auth, googleProvider)
      .then((response) => {
        console.log(response.user);
      })
      .catch((err) => {
        alert(err.message);
      });
  };

  const handlelogout = () => {
    signOut(auth);
  };

  useEffect(() => {
    onAuthStateChanged(auth, (user) => {
      if (user) {
        console.log(user);
        alert('ログインしています');
      } else {
        alert('ログインされてません');
      }
    });
  }, [auth]);

  return (
    <div className='App-header'>
      <input
        placeholder='Email'
        name='email'
        type='email'
        className='input-fields'
        onChange={(event) => handleInputs(event)}
      />
      <input
        placeholder='Password'
        name='password'
        type='password'
        className='input-fields'
        onChange={(event) => handleInputs(event)}
      />

      <button onClick={handleSignin}>会員登録</button>
      <button onClick={handleLogin}>ログイン</button>
      <button onClick={handlelogout}>ログアウト</button>
      <button onClick={handleSignInWithGoogle}>
        グーグルで会員登録・ログイン
      </button>
    </div>
  );
}

export default App;

前回のコードから変わったのは以下の部分になります。

import {
  getAuth,
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
  signOut,
  onAuthStateChanged,
  signInWithPopup,
  GoogleAuthProvider,
} from 'firebase/auth';
  const googleProvider = new GoogleAuthProvider();

  const handleSigninWithGoogle = () => {
    signInWithPopup(auth, googleProvider)
      .then((response) => {
        console.log(response.user);
      })
      .catch((err) => {
        alert(err.message);
      });
  };

      <button onClick={handleSigninWithGoogle}>
        グーグルで会員登録・ログイン
      </button>

まず最初に、signInWithPopup, GoogleAuthProviderを
firebase/authからインポートしています。


それぞれは以下のようになっています。
signInWithPopup→ポップアップウィンドウで会員登録する為の関数
GoogleAuthProvider→グーグル用の認証プロバイダーオブジェクト



ちなみにポップアップウィンドウを表示するのではなく
リダイレクトしたい場合にはsignInWithRedirect関数が用意されています。



ボタンを押すとhandleSignInWithGoogleが呼ばれ、
handleSigninWithGoogleの中でポップアップウィンドウで会員登録をする関数を呼んでいます。
引数にはauthと、Google プロバイダーオブジェクトのインスタンスを渡しています。



とまあ、こんな感じで以上です。
なんとこれだけでグーグルでの会員登録・ログインができちゃいました;





Twitter認証


事前にTwitter Developerへの登録とAPIの利用申請をする必要があります。
まだやってない方は先に行いましょう!



登録と利用申請、ProjectとAppの作り方を探してみたら
とてもわかりやすい記事が見つかったので
リンクを貼らせていただきます。ありがとうございます。



TwitterAPIを利用するための事前準備 その1「Twitter developerの認証」 | 猫西ン家
TwitterAPIを利用するための事前準備 その2「プロジェクトとAppの作成」 | 猫西ン家



それぞれが終わったら
Appを作った時に表示される、API KeyとAPI key Secretを控えておきます。


(APIキーなど載せてますが消去済みです!)






次にFirebase コンソールで、Authenticationのセクションを開きます。
signinメッソドの 画面で「新しいプロバイダを追加」をクリック、




Twitterサインイン方法を有効にし、先ほど控えておいた
APIキーとAPIシークレットをコピペします。
ここに表示されているコールバックURLも控えておきます。






これで準備OKです。コードに以下を追加します。


import {
  TwitterAuthProvider,
} from 'firebase/auth';

  const twitterProvider = new TwitterAuthProvider();

  const handleSignInWithTwitter = () => {
    signInWithPopup(auth, twitterProvider)
      .then((response) => {
        console.log(response.user);
      })
      .catch((err) => {
        alert(err.message);
      });
  };

    <button onClick={handleSignInWithTwitter}>
        Twitterで会員登録・ログイン
      </button>

追加したコードの内容はグーグル認証の時と同じです。
これで実行してみると、、、






きちんと登録できました。




Twitterの認証はややこしかった覚えがあったのでこんなに
簡単にできるなんてびっくりです。



ログアウトは前回の記事と同じで、

  const handlelogout = () => {
    signOut(auth);
  };

でOKです。



みなさんもぜひFirebaseでの認証やってみてください!



参考:
JavaScript による Google を使用した認証  |  Firebase
Authenticate Using Twitter in JavaScript  |  Firebase

2022年版!Firebase認証のやり方まとめ

以前Laravel Sanctumを使ってAPI認証を作成した時から
ずっと気になっていたFirebase、やっと触ることができました😊嬉しいです!


今回はFirebaseについて勉強したのでその中からReactを使った
Firebaseによる認証のやり方をまとめていきたいと思います。


今回はEmailとパスワードでの認証を作成していきます。

Firebaseとは

Googleが提供しているモバイル・Webアプリケーション向けのプラットフォームです。
認証機能、DB機能、ホスティング、ストレージ機能、メッセージ通知機能など
全ての機能がGoogleのインフラ技術に支えられており、大規模なアプリケーションも構築することができます

Firebase認証とは

Firebase Authentication は、安全な認証システムの構築を容易にし、
エンドユーザーのログインや初期登録のエクスペリエンスを向上させることを目的としています。
メールアドレスとパスワードの組み合わせ、電話認証、
GoogleTwitterFacebookGitHub のログインなどに対応したエンドツーエンドの ID ソリューションです。



では早速やっていきましょう!

アカウントの登録


Firebaseに登録するには、Googleのアカウントが必要です。
あらかじめGoogleアカウントを作っておきましょう。登録は無料です。
Firebase利用料金は
Firebase Pricing
今回は無料のSparkプランを使っていきます。

まずはFirebaseの公式ページにアクセスします。
Firebase

アクセスしたら、Googleアカウントでログインします。

プロジェクトの作成


ログインしたらコンソールからプロジェクトを作成します。


プロジェクト名を入力しFirebaseの規約を読んだら
チェックボックス両方にチェックをいれ続行をクリックします。



Googleアナリティクスを使うか聞かれます。
今回は使わないのでoffにします。

そして「プロジェクトを作成」をクリックします。



しばらく待つとプロジェクトの作成が完了します!

アプリの登録


次に作成したプロジェクトにアプリの登録を行っていきます。

タグマークのボタンをクリック。



アプリのニックネーム(後で変更可)を入力しアプリを登録をクリック。





この画面になったらコードをコピペして控えておき
コンソールに進むをクリック。




Authenticationメソッドの追加


こちらの画面のAuthenticationをクリック。


次にSign-in methodをクリックしEmail/Passwordを追加します。


Email/Passwordを有効にするをクリックして保存します。



Firebaseの準備が整ったのでReactアプリを作成していきます。



Reactアプリの作成


今回はcreate-react-appを使ってReactアプリを作成します。

npx create-react-app react-test-firebase


作成できたらfirebaseをインストールしていきます。

npm install firebase


インストールしたらsrcディレクトリ内にconfigファイル「firebaseConfig.js」を作成し、
アプリを作成した時に控えておいたコードをコピペします。


const appの前にexportをつけてエクスポートできるようにします。


// 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: 'AIzaSyBTSHwkPMgl9tyRaR2lUpozh6vQTppVLOM',
  authDomain: 'fir-test-e3b9d.firebaseapp.com',
  projectId: 'fir-test-e3b9d',
  storageBucket: 'fir-test-e3b9d.appspot.com',
  messagingSenderId: '805344665134',
  appId: '1:805344665134:web:b0b9c47a4f76533e3d49bf',
};

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

(APIキーなど載せてますが消去済みです!)


会員登録機能の実装


App.jsを下記のコードに変更します。


import './App.css';
import { useState } from 'react';
import { app } from './firebaseConfig';
import {
  getAuth,
  createUserWithEmailAndPassword,
} from 'firebase/auth';

function App() {
  const auth = getAuth();
  const [data, setData] = useState({
    email: '',
    password: '',
  });
  const handleInputs = (event) => {
    let inputs = { [event.target.name]: event.target.value };

    setData({ ...data, ...inputs });
  };

  const handleSubmit = () => {
    createUserWithEmailAndPassword(auth, data.email, data.password)
      .then((response) => {
        console.log(response.user);
      })
      .catch((err) => {
        alert(err.message);
      });
  };

  return (
    <div className='App-header'>
      <input
        placeholder='Email'
        name='email'
        type='email'
        className='input-fields'
        onChange={(event) => handleInputs(event)}
      />
      <input
        placeholder='Password'
        name='password'
        type='password'
        className='input-fields'
        onChange={(event) => handleInputs(event)}
      />

      <button onClick={handleSubmit}>会員登録</button>
      <button onClick={handleLogin}>ログイン</button>
      <button onClick={handlelogout}>ログアウト</button>
    </div>
  );
}

export default App;


コードについて説明します。

import {
  getAuth,
  createUserWithEmailAndPassword
} from 'firebase/auth';

getAuth→認証済みのユーザーかどうかをチェックする関数です。
createUserWithEmailAndPassword→emailとpasswordでアカウントを作る関数です。


これらの関数をfirebase/authからインポートしています。

  const auth = getAuth();


authにgetAuth()関数を格納しています。


  const handleSubmit = () => {
    createUserWithEmailAndPassword(auth, data.email, data.password)
      .then((response) => {
        console.log(response.user);
      })
      .catch((err) => {
        alert(err.message);
      });
  };


handleSubimitの中でcreateUserWithEmailAndPassword()関数を渡し、
引数にauth, email, passwordを入れて渡しています。



詳しくはこちらのDocを参考にしてください。
Authenticate with Firebase using Password-Based Accounts using Javascript




少し見た目を変えるためのコードをApp.cssに記述します。
不要な方は記述しなくても大丈夫です。

.App-header {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
}

.input-fields{
  margin: 10px;
}
<||



*** 会員登録の実行


会員登録ボタンを押して実行してみます。
FirebaseのAuthentication→Usersで確認してみるときちんと登録されていました!



[f:id:sukithewestie:20220818112210p:plain]



メールアドレスの形式が違うとエラーをはいてくれたり
同じユーザーを登録しようとするとエラーをはいてくれたりかなり便利です。



*** ログイン・ログアウトの実装



ログイン・ログアウト機能を実装した後のコードはこんな感じです。

>||
import './App.css';
import { useState, useEffect } from 'react';
import { app } from './firebaseConfig';
import {
  getAuth,
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
  signOut,
  onAuthStateChanged,
} from 'firebase/auth';

function App() {
  const auth = getAuth();
  const [data, setData] = useState({
    email: '',
    password: '',
  });
  const handleInputs = (event) => {
    let inputs = { [event.target.name]: event.target.value };

    setData({ ...data, ...inputs });
  };

  const handleSubmit = () => {
    createUserWithEmailAndPassword(auth, data.email, data.password)
      .then((response) => {
        console.log(response.user);
      })
      .catch((err) => {
        alert(err.message);
      });
  };

  const handleLogin = () => {
    signInWithEmailAndPassword(auth, data.email, data.password);
  };

  const handlelogout = () => {
    signOut(auth);
  };

  // ログインしているか確認用
  useEffect(() => {
    onAuthStateChanged(auth, (data) => {
      if (data) {
        alert('ログインしています');
      } else {
        alert('ログインされてません');
      }
    });
  }, []);

  return (
    <div className='App-header'>
      <input
        placeholder='Email'
        name='email'
        type='email'
        className='input-fields'
        onChange={(event) => handleInputs(event)}
      />
      <input
        placeholder='Password'
        name='password'
        type='password'
        className='input-fields'
        onChange={(event) => handleInputs(event)}
      />

      <button onClick={handleSubmit}>会員登録</button>
      <button onClick={handleLogin}>ログイン</button>
      <button onClick={handlelogout}>ログアウト</button>
    </div>
  );
}

export default App;

新たに3つの関数をfirebase/authよりインポートしています。


import {
  getAuth,
  createUserWithEmailAndPassword,
  signInWithEmailAndPassword,
  signOut,
  onAuthStateChanged,
} from 'firebase/auth';


signInWithEmailAndPassword→EmailとPasswordでログインする為の関数
signOut→ログアウトする為の関数
onAuthStateChanged→現在ログインしているユーザーの監視・情報を取得する関数


onAuthStateChangedで取得できる情報↓

// uid
user.uid

// 名前
user.displayName

// プロフィール画像
user.photoURL

// メール
user.email

// 認証済みのメールアドレス化
user.emailVerified

// 電話番号
user.phoneNumber

// 匿名認証かどうか
user.isAnonymous

signInWithEmailAndPassword関数とsignOut関数部分の実装は
もはや言うまでもないので省略します。



今回はApp.js内で会員登録・ログイン・ログアウト全て完結させた
横着実装なのでuseEffectを使って今の状態をわかりやすくしています。

  useEffect(() => {
    onAuthStateChanged(auth, (data) => {
      if (data) {
        alert('ログインしています');
      } else {
        alert('ログインされてません');
      }
    });
  }, []);


これでログインとログアウトボタンを押して実行してみると上手くいきました!


おわりに

Firebase認証最高です。次はFirebaseを使ってのソーシャルログイン認証だったり、
DBを使ってみたいと思います。

【Reactテスト】getBy~、findBy~、queryBy~の違い、要素の選択の仕方まとめ

1つの要素を選択する3つの方法


getBy~


要素がある場合→一致するノードを返す。
要素がない場合→エラーをthrowする。


findBy~



要素がある場合: 一致するノードのPromiseを返す。
要素がない場合、1000ms以内に見つからな場合: 拒否される。


getByとwaitForを組み合わせたメソッド。よって下記のコードは同じ。

await waitFor(()=> expect(getByRole('button')).toBeInTheDocument())

expect(await findByRole('button')).toBeInTheDocument()


queryBy~


要素がある場合: 一致するノードを返す。
要素がない場合: nullを返す。


要素がない場合getBy~と違ってnullを返すので存在しないノードに対して使うと便利です。
条件によって表示・非表示の切り替えや、一定時間で消えるアラートなどに対して
期待したとおりにノードが消えるかどうかをテストする際に活躍する。


複数の要素を選択する3つの方法


getAllBy~


要素がある場合→一致する配列を返す。
要素がない場合→エラーをthrowする。


findAllBy~



要素がある場合: 一致する配列を返す。
要素がない場合、1000ms以内に見つからな場合: 拒否される。



queryAllBy~


要素がある場合: 一致する配列を返す。
要素がない場合: 空の配列を返す。


要素の選択の仕方


getByText()


インタラクティブな要素 (div、span、段落など) を見つけるために使用できる。
見つからない場合はエラーを返す。


getByRole()


aria-label属性で要素を取得するのに使用する。利用できないroleを指定すると、
利用可能な全てのroleを表示する。screen.getByRole("")で利用可能なすべてのrollを表示できる。
複数ある要素を(button要素など)指定するには下記のように記述する

getByRole('button', {name: /submit/i})

getByLabelText()


for属性の要素を取得するのに使用する。


getByPlaceholderText


placeholder属性の要素を取得するのに使用する。


getByAltText()


alt属性の要素を取得するのに使用する


getByDisplayValue()


value属性の要素を取得するのに使用する。




参考URL: https://testing-library.com/docs/queries/about