この広告は、90日以上更新していないブログに表示しています。
技術同人誌の『りあクト!』3部作と続編も読んだので、5作目を読みました。
今回はこれまでのReact開発の知見を活かし、BaaSあるいはmBaaSの代表格Firebaseにバックエンドをお任せし、世の中に公開していく実際のサービスをサーバーレスで開発していく本となっています。今回もまたまた本文は会話形式で読みやすいです。
Reactをマスターして作中で無事に一人前のフロントエンジニアに成長できた秋谷さん。しかし仕事で回ってくるのは既存プロダクトのフロントエンドのモダン化だったりサーバーサイドは別エンジニアにお伺いを立てねばならなかったりで、理想通りには行ってない模様。(このへん、自社サービス中心の会社さんだと実際どうなのでしょうね)
そこへ、エライ人をノせるのが上手い柴埼先輩が持ってきたのがサービスとして当たれば大きい試験的プロジェクト。メンバーはこの2人で自由、バックエンドはBaaSの代表格Google Firebaseに全部お任せ。サービス名はMangarel「マンガレル」。→よし、やろう! という事で物語が始まります。
柴埼先輩は漫画好きという属性がここで明かされています。だから「~つらくないReact」開発3部作でコード内にあれだけネタが仕込んであった訳ですね……

1-1. 基本環境を作る
Google Cloud FunctionはNode.jsの12系に対応していないので10系も使うため、anyenv,nodenvを使って複数バージョンに対応。(Windowsならnvm-windowsでしょうか。)Create React AppでTypeScriptのプロジェクトを新規生成。tsconfig.jsonはパイセン式の設定を追加。functions/というディレクトリも除外設定に追加。ESLint とPrettierでコードフォーマットを統一。typesyncというライブラリを追加、> typesnyc で自動で必要なTypeScriptの型ファイルをpackage.jsonのdevDependenciesに追加してくれる。(これ便利そうですね).eslintrc.js等々を準備。柴埼先輩スペシャル版が用意済み。husky、コミット前にESLintを実行できるlint-staged をインストール、コマンドから実行できるようにpackage.jsonに設定追加。1-2. Firebase プロジェクトの作成と初期化
Firebaseの管理コンソールにログイン。この時URL末尾の/u/{0からの数字}/ がログインした順番になっているので、アカウントが複数ある場合は注意。(こういう話もありがたいです)Firebase上で一意のプロジェクトIDも一緒に決まるので注意。誰かと被っていると末尾に乱数のポストフィックスがついてしまう。asia-northeast1。Create React Appで作成済みのローカルのプロジェクトフォルダに行ってnpmからfirebase-tools をインストール。これがFirebase CLIでログイン後にいろいろできる。> firebase init で対話形式でいろいろ選んでいくとプロジェクトが初期化。Reactの初期画面はもう表示可能。1-3. Cloud Functions の環境整備
functions/ の中がCloud Functionsの仕事場。サポートしているNode.jsのバージョンは最新が8だが10もベータ版サポート。ほぼ動くとのこと。tsconfig.jsonを変えたりテストフレームワークのJestを入れたりLintとPrettierを入れたり.eslintrc.js を整備したり。このへんも柴埼先輩スペシャル版が用意済み。functions/のように作ったフォルダは上の階層とは設定が別々になるのに注意。1-4.独自ドメインを設定する
Google Cloud Platformのプロジェクトとして存在しているので、GCPのコンソール画面からもAPIキーが再作成できたりする。 さらっと流されていますが、柴埼先輩スペシャルの準備済み設定ファイル群はここまで揃うのにずいぶん苦労したのではないかなあと……
Firebaseコンソールの管理画面はもう日本語されていて分かりやすいです。作中で秋谷さんが言っているように用語がけっこう独特ですね。
2-1. データベースの作成と Admin 環境の整備
Realtime Databaseは古くCloud Firestoreが今。functions/配下に置くので、これはGitHubなどに置かないように注意。2-2. データ投入スクリプトの作成
Commander.jsを使って自作していきます。dbseed.tsの中ではCSVファイルをパースするライブラリcsv-parseも利用。秘密鍵を使ってFirestoreに接続、CSVの中身をパース、1件ごと登録……というのをNode.js 10から使える新しい構文を活用したサンプルコードになっています。DynamoDBだと、RDBのテーブル→テーブル、1レコード→1アイテム)2-3. npm scripts として登録する
Command(Ctrl) + shift + b で立ち上がるビルドを使ってウォッチタスクで実行する方法。CLIからやるのが普通ということで、オプションを付けると子供のコレクションも再帰的に削除可能。3-1. 簡単な HTTP 関数を作ってみる
Google Cloud FunctionはAWSのLambda相当で後を追ってリリースされたGCPのサービス。その後、Firebaseの機能のひとつとして改めてリリースされたのがCloud Functions for Firebaseで、よりFirebaseの機能と統合・最適化されている。Cloud Functionsの関数の本体はsrc/index.tsなどに書いたJS(TS)コード。firebase-functions,firebase-adminをimportして、初期化→リージョン指定→コレクションのこれを取得します→レスポンスに設定 を書く。Lambda関数と似た感じですね。> firebase serve を使うとローカル上でコマンド実行でURLが出力されるので、そこをブラウザで見てもローカルホスト上で結果が確認できる。3-2.クローラーをスケジュール設定関数にする
Puppeteerが一番人気。プロジェクトフォルダのfunctions/の下でインストール、index.tsの中で定期実行するコードを書いていきます。crontabと同じなのが面白い。タイムゾーンでAsia/Tokyoを指定することに注意。Lambda関数と同じで、Puppeteerが動くには1-2GBはいるとのこと。PuppeteerのAPIもいろいろあるそうですが、本書のサンプルにある基本的な関数を使えばDOM要素の取得には足りるとのことです。Functions Shellというコマンドから行えるツールあり。 クラウドとサーバーレス全般で「冪等性」はよく登場しますが、Firebaseでも同じく重要なのだなと。
スクレイピングといえば言語的にはPythonなイメージがあったのですが、JS界隈だとPuppeteerがデフォルトなんですね。
細かいネタとしては秋谷さんは『炎炎ノ消防隊』が好きなのが判明します!
3-3. スケジュール設定関数をデプロイする
index.tsをデプロイしていきます。Functions Shellから関数を実行するとまずインデックスがないと怒られる→> firebase deploy のコマンド実行でデプロイ。3-4. Cloud Functions 中上級編 Tips
> firebase functions:config:set {キー}={"値"} で環境変数を指定。> firebase functions:config:get で.runtimeconfig.json と指定すると環境変数をファイルに落とせる。firebaseのコマンド群はこのファイルを見てくれる。関数内からも参照可能。変数名は大文字不可。process.env の中を見るとNODE_ENV: 'production'が入っているが、ローカルサーバーだとundefinedになってしまう。date-fns-timezoneライブラリを使う手がある。functions/配下のCloud Functionsで関数を共通化して使いたい場合は、柴埼先輩がいろいろ試してたどり着いた邪道テクではシンボリックリンクを張る手法で解決。Reactアプリ本体側のコードが本体、functions/配下はそこへリンクを張る。Cloud Functionsは最初の起動時にindexにある関数をすべてロードしてしまうので、index.tsが巨大化していくと遅くなる。分割しておくとよい。また起動時にprocess.env.FUNCTIONS_TARGET という変数に関数名が入るので、その対象の関数だけexportsするというテクがある。 大まかにはAWS Lambdaでサーバーレス開発をしていく時と似たような雰囲気ですね。
準備された設定ファイルや細かなTipsなどなど、柴埼先輩のこれまでの試行錯誤(と、その裏にある本書の作者さんの苦労)が入っているので本としてはスラスラ進んでいます。でもFirebaseは進化中のサービスであるだけにけっこう細かいところにまだまだ躓きポイントや罠があるんだなあという感じです。
4-1. Firestore とRDB の違いと各種制限について
Partial型を使ったテクを使用。Cloud Functionで更新するなど。count()ができない弱点は…作中の2人の予想通り、後からBigQueryへのエクスポートや連携ができるようになった。firebase.google.comsupport.google.com
4-2. Firestore のクエリーとインデックス
in 、array-contains-any が後から提供された。create index しなくても元から全フィールドにインデックスがある。昇順と降順が別。配列用はまた別インデックス。範囲の比較と等価の比較を同時に行うときはまた別インデックス。drop tableで一緒に消える)4-3. Firestore の配列の取り扱い
array-contains, 配列要素追加のarrayUnion(), 要素削除のarrayRemove() がコード中から書ける。array-contains はひとつのクエリーで1回しか使えない。booksコレクションの中でauthorIdsの配列を見てauthorIdがA先生、は検索できるがA先生かB先生両方、は検索できない。(!)authorMap: {'A先生のID': true, 'B先生のID': true}==,trueを2回書けば実現できる。4-4. Firestore で日時を扱う際の注意
Date型でデータを突っ込むと、Firebase独自のfirebase.firestore.Timestamp型に変換されて保持、その後検索時もそのまま。TypeScriptを使うときはモデル定義で最初からこのTimestamp型で定義しておいたほうがよい。FieldValue.serverTimestamp()の返す型がTimestamp型でなかったり、そのままでは値の比較ができずDate型やミリ秒に変換する必要がある、などの罠もある。4-5. Firestore のデータモデリング
Cloud Functionで更新するなどして検索を工夫する。1:1のリレーションには{コレクション名}/{IDの値} で表される「疑似リレーション」を両方のコレクションに持たせるのがよい。1:Nのリレーションにはふつうに外部IDをフィールドに持たせる。N:Nのリレーションでは前述のTrueマップを使う方法が健在。RDBならID同士の関係を持たせる別途のテーブルを用意するところだが、FirestoreだとJOINができない。自分はRDBはよく使ってきたのでこういう話題になるとテーブル構造とかSQLが頭に浮かんでくるのですが、Firestoreだとけっこう独特で制限が多いですね…! 作中で秋谷さんが言っているように、気を付けないとハマるポイントがあちこちにありそうです。
4-6. Firestore だけで全文検索を実現する
Algoliaのような外部の検索サービスを使うこと。しかし高く連携設定が面倒。他にはElasticsearchを使う事例も。天才だ…天才がおる…!ということでこの4章の目玉はこの自前の全文検索エンジン実装です。簡単に言うと、
-単語の形態素解析にもいろいろあるがその中の一つ、Javaで開発されたOSSの日本語形態素解析エンジンKuromoji がある。これのJavaScript版のkuromoji.jsがnpmで公開されているのでこれを使う手もあるが、辞書ファイルが巨大になるデメリットあり。
N-gram(エヌグラム)の方法を柴埼先輩は採用。漫画の名前が「七つの大罪」だったら、JSのライブラリn-gramを活用してドキュメントの登録時に名前を"七つ""つの""の大""大罪"のように分解して、1ドキュメントごとにTrueマップで格納するようにする。N-gram分解して配列に変換、先ほどのTrueマップと等価比較すると実質全文検索になる。コード例も一緒に載っているのですが、よくこんなアイデア思いつくな……!と感嘆しました。作中の柴埼先輩も成功したとき自分で感動したと言っていますが、こういういろんな工夫を駆使して開発していくのですね。
5-1. フロントエンド環境の整備
Reduxは使わず、react-routerとCSSのEmotion回りなどの最小構成。.env の中のREACT_APP_* の項目に記述。5-2. Context でグローバルな State を持つ
<FirebaseApp>でくるんだ中に<ThemeContext>、その中に<App>。CSS的なテーマなどのコンテキストは別ファイルで定義、React HooksのcreateContext()で定義。firebase.firestore()して取得したFirestoreオブジェクトを持ち、こちらもコンテキストで使いまわす。5-3. Hooks で副作用処理を行う
useBooks()を定義。戻り値はオブジェクトにして本の配列、ローディング中か、エラーを持つ。useState()を活用して本のリストなどは保持。useEffect()の副作用を活用して初期表示時にFirestoreと接続、発行日が未来日のもうすぐ発売の本一覧を検索。useBookSearch()を定義。useEffect()の第2引数に検索条件のテキストボックスの値を入れているので、1文字でも変わった瞬間ごとにクエリーが走ってくれる。 Reduxを使わない選択をした話で、柴埼先輩がHooks登場でReactが完全にいらない子になったのではなくて、「使わない選択を正しくできるようになった」のではないかと述べているのが興味深いです。習得も難しくコード量も増えるReduxで問題を解決しようとすると、解決できるけどオーバーキルになることが多かったとのこと。
確かに本章のReact Hooksの副作用を活用したコード、ぱっと見でもきれいに分割されてスッキリしている印象です。今後はこちらの路線が増えていくのかなあと思います。
6-1. 認証機能を導入するための準備
API KeyとAPI secret keyの2つを取得、Firebaseコンソール側に反映する手順がスクショ入りで詳しく解説されています。6-2. ログイン機能の実装
createContext()するファイルの中で、Firestoreオブジェクトのほかに認証情報、ユーザ情報も追加。useState()を使っているので中身が保持、さらに外側ではContextを使っているので、子供のコンポーネント群からもこのユーザ情報が参照できる。react-firebaseuiというライブラリを使うと、JSXに所定のタグを書くだけでログインボタンの表示をやってくれる。この整ったコードにたどり着くのに柴埼先輩はかなりハマったそうなので、その裏の作者さんもかなり試行錯誤されたのでは…と思います。
6-3. Firestore にセキュリティルールを適用する
Mangarelサービス用のFirestoreに悪意ある第三者もアクセスできてしまうのでセキュリティルールを適用する話。match /{コレクション名}/{ドキュメントのID項目} の形でパス指定、1ドキュメント取得/ドキュメントのリスト取得/作成/更新/削除 を定義できる。usersコレクションのidが自分のものと等しい1ドキュメントしか更新不可、などが細かく定義できる。null不可、などのバリデーションルールもコードからかなり細かく設定可能。ドキュメント数、1ドキュメントの中のフィールド数や正規表現などなども使える。もうほとんどバリデーション処理をクライアントサイド/サーバサイドの何らかの言語でコードで書いているのと同じような感覚ですが、Firebaseはバックエンド不要のBaaSなのでこういうことも設定画面からできるわけですね。突き詰めるとかなり細かくいろんなことができそうです。
今回も内容はかなり濃く密度の高い本でした。設定ファイルのサンプルやコード例、問題解決の手法など作中では柴埼先輩が過去ハマったことをチラ見せしつつ割とさらっと正解を示していますが、その裏では作者さんはかなり試行錯誤されて調べながら実際のサービスまで完成させたのでは思います……
技術同人誌などでもFirebaseはキーワードとしてよく登場しますし、本書のあとがきでも「フロントエンドエンジニアをエンパワーする技術」だという一節があります。個人開発ベースやスモールスタートな事業での開発、バックエンドのエライい人やインフラを気にせず少人数でスピード優先で作っていくような開発に向いていそうですね。
一方Firebase自体については、他の書籍類でも見てきたのですがけっこうクセが強い技術だなと改めて思いました。これのよりRDBライクな版が出たら丸く収まりそうですが、そういう訳にもいかないんでしょうね……笑
今回も100ページちょっとですしお値段もお手軽、気軽に読める技術同人誌です。読んでみるとReact+Firebaseのサーバーレス開発のイメージが湧くのではないでしょうか。
なおネタバレですが最後の著者紹介のところに、今回はデスクツアー的に作者さんの執筆環境の机の写真が載っています。あっ確かにモニターの色合いが目に優しいSolarized Lightっぽい。そしてキーボードが2つに割れててトラックボールも左右に2つある! これ一体どうやって操作しとるんぢゃ……

本書のサンプルコードのリポジトリ。
正誤表。github.com
りあクト! TypeScriptで始めるつらくないReact開発 第3.1版【Ⅰ. 言語・環境編】oukayuka.booth.pmりあクト! TypeScriptで始めるつらくないReact開発 第3.1版【Ⅱ. React基礎編】oukayuka.booth.pmりあクト! TypeScriptで始めるつらくないReact開発 第3.1版【Ⅲ. React応用編】oukayuka.booth.pm
りあクト! TypeScriptで極める現場のReact開発booth.pmりあクト! Firebaseで始めるサーバーレスReact開発booth.pm
id:iwasiman引退した元TRPGゲーマー。COBOLでもなくPL/Iの金融系レガシー紙駆動開発から脱出→国産メーカー系総合ITベンダーのITエンジニア。所謂SIerのSEだけど仕事はほぼソフトウェアエンジニア/ソフトウェアアーキテクトとして、Web開発でコードを書いたり技術を追ったり時々イベントに行ったり、楽しいエンジニアリングを目指しています。Views are my own.
お気軽にどうぞ~
リンク集:https://lit.link/iwasiman
AIイラスト関連の活動はこちら↓
https://www.chichi-pui.com/users/iwasiman/
https://pixiv.me/iwasiman
引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。