コロナ渦で恋愛できずFirebaseをフル活用して恋活アプリを作ってしまった #大学生 #LoveOps

こんにちは、普段は機械学習が専攻の大学生[^1]をしている者です。情報系学生なのと[^2]コロナ禍で出会いがなく悩み、**ないのであれば作れば良いのでは?**と大学の先輩と恋活アプリを作り、リリースしました。

![6.5インチ – 3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614389/3e2551e0-936a-714d-da33-b57d5a99bb09.jpeg)

https://apps.apple.com/jp/app/id1592092244

https://meeple.jp

# Firebaseに関して

## Firebaseの実例の知見が日本語圏で足りない!

まだ**実際の開発の知見が十分に見つからず、僕らも困りました。**
ですので僕たちの経験をFirebaseでの数少ない**具体事例**としてご参考にいただければ幸いです。

海外では大企業でもFirebaseを導入[^3]する実例は早くからありました。最近では日本ではドコモなどがFirestoreを使い始めたなど、今後BaaSがより一般的になりそうです。

https://cloud.google.com/blog/ja/topics/customers/ntt-docomo-google-cloud-platform-gke

## バックエンドエンジニアはいらないのか？

いえいえ、開発を通して**バックエンドの知見は必要だと痛感**しました。

Firebaseの各機能を上手に組み合わせるにはバックエンドの知見が必要だと感じました。特にCloud Functionsではサーバーサイドの人がやっていることを結局Nodeで実装することになります。特にチームの一人は元々backend guyだったのでいろいろな面で助かりました。

**バックエンドエンジニア VS Firebase**と煽る人もいますが、
**両者を組み合わせたらとても素敵な化学反応が起こるのでは？🔥**

実際、先のドコモの例でも**既存の枠組みとFirestoreを共存させている**そうです。

### サービス構成と技術スタック
**Firebaseバックエンド** Authentication, Storage, Firestore, Cloud Functionsなど
**フロントエンド** Swift, Vue(管理者用画面), ES6 JS
**バージョン管理** git/GitHub + 相互レビュー
**CI/CD** GitHub Actions, Firebase Emulator Suite
**コミュニケーション** Slack + Trello

### チーム構成: みんなWeb技術を触ったことがあった

- 吉野くん ( @ninjin8811 )  Web/Swift/XDデザイン 開発リーダー @東京 元社会人
- Bくん Web/Swift/Firebase DB設計とCIに強み @東京 社会人
- 私 Web/Firebase 管理画面とFunctionsなど + 利用規約など @大阪 大学3年生

Fun fact: 吉野君は阪府大の先輩です。
アプリ構想から実現まで時間かかりすぎて卒業しちゃいました🥺ぴえん。。。

https://qiita.com/ninjin8811/items/593c13d6e53eb3a04a50

**先輩の吉野くんもmeepleについて書いていますので、ぜひご覧ください！**

#### よかったこと: 全員FirebaseとWEB周りの経験があった
Swiftを除けばコーディングはJSばかりなのでWebの経験はFirebaseと親和性が高いと感じました。吉野君は働きながら時間を捻出するのは厳しいと感じ今年会社を辞めてmeepleに専念しています。

# Firebaseの選定の理由

### Google社員のリアルタイムデモに感動する
2019年に大阪のmeetupで、[当時のFirebaseのPMだったGoogle社員](https://twitter.com/asciimike)の発表を聞きました。SNSと連携するチャットアプリをFirebaseで30分以内に作るというものでした。Firebaseの各機能を組み合わせて爆速に実装されていく様子は、会場のバイブスが爆上げするのには十分でした🔥

その感動から時がたち、先輩にFirebaseを勧めると、既に先輩も使っていました。
当時はフロントエンドだけでも精一杯だったんで秒速でFirebaseを選びました。

後知恵ですが、この決定は僕らにとって大大正解でした🔥

**さて、ここから、ただひたすらFirebaseの各機能を利用して分かったことなどを述べていきます。**

# Authentication
会員登録の認証基盤として採用しました。当初はGoogle, Facebookなどのプラットフォームログインを用意しました。

## iOSアプリではApple IDログインを実装する必要があることもある
2019年の末からプラットフォームログイン実装にあたりAppleログインも必要になったとAppleの公式発表がありました。

>Starting today, new apps submitted to the App Store must follow these guidelines. Existing apps and app updates must follow them by April 2020.

https://developer.apple.com/news/?id=09122019b

このため、審査時にAppleログインがないという理由でリジェクトされました。
**Firebaseでも新たにApple IDでのログインができるように**なっていたので、とても助かりました。

https://firebase.google.com/docs/auth/ios/apple

## 注意:利用規約に忘れずにプラットフォームの記述

利用規約にログインプラットフォームに関する記述をする必要があるかもしれませんので、ご注意ください。
例えば「プラットフォームのアカウントが停止された場合に当社のサービスを利用できなくなることがあるが、その場合にサービスが利用不可になる」ことなどに対する免責事項を記述する必要もあります。よくわからない時は弁護士に相談してください。


### Tips: 法規に関しては気軽に弁護士に相談しよう!
僕たちはVenture Cafe Tokyoという非営利団体の"Office Hours"という制度で弁護士の方に無料で相談させていただきました。特にマッチングアプリなど認可が必要であったり規制がある事業に関しては専門家に聞くことを**強く**お勧めします。

マッチングアプリを作りたい人のために:
**個人情報の適切な管理**、**電気通信事業**、**インターネット異性紹介事業**、**特商法**
などは避けて通れないので覚えておいてください。

https://venturecafetokyo.org/office-hours/


## 認証基盤としてFirebase Authenticationは優秀
認証基盤をゼロから実装すると、多大な労力とセキュリティ上の懸念があります。
この点をGoogle側がある程度担保してくれるのは、我々としては大変助かりました。
特に各種プラットフォームログインを簡単にできる点は素晴らしいです。

先ほど述べたように Apple IDでのログインもAuthenticationで比較的簡単に実装できます。

# Firestore

Firebase Realtime Databaseは利用せず、代わりにFirestoreを利用しました。理由はFirestoreの方が新しく、Googleも新機能に力を入れているのではないかと感じたからです。

FirestoreからCloud Functionsからもシームレスに特定ドキュメントの監視などが行えることは実装上便利でした。また、Functionsと連携できることは、事実上Firebaseの各機能と幅広い自由度で連携できる可能性があります。

とはいえ、Firestoreも万能ではありません。開発リーダーの吉野君の言葉を借りると、データベース設計の観点からは以下のような問題もあります。

>FirestoreはRDBのようにテーブル同士を結合させてデータを取り出すことができず（ドキュメント同士の結合ができない）、新機能を追加するときなどドキュメント内の構造が変化しやすいのが難点。
ドキュメント内の構造が変化するたびに結構なコード修正が必要になったため、この辺りはどう設計したら良かったのか、未だに答えが見つかっていません。。

こうした問題からも、まだ**FirebaseRealtime Databaseは当面はFirestoreと共存する**のではないでしょうか。


また、**サブコレクションはセキュリティルールがややこしく**なるため、できるだけ使っていません。
Firestoreは**自由が高い反面、実用上好ましくないような設計にもできてしまう**ので、データベース設計に慣れた人がチーム内にいることが望ましいなと感じました。
あと、データベース設計が後々のセキュリティルールの書きやすさや、安全性に関わってくるので、**最初の段階で適切なデータベース設計が必要**な点も留意が必要です。

# Functions
主にメール・プッシュ通知の送信バッチや、制限時間を過ぎた場合のマッチ解消バッチのスケジュール実行に使用

### 外部APIや自作APIエンドポイントなど可能性は自由自在
Functionsは**Firebaseの中で最も自由度が高く、同時に、セキュリティの問題を孕みやすい**箇所です。
onRequestなどでhttpエンドポイントを作成するときは、きちんと認証をかけましょう。
このエンドポイントに認証をかける作業と、CORSポリシーへの対応が鬼門でした。

このような操作にはJavaScriptのExpressフレームワークのノリが必要です。なお、ES6のasync/awaitが使えるとコードは読みやすくなると思いました。ですので、WEB系エンジニアであればFunctions自体の学習コストは低いのではないでしょうか？

### APIコールなどで大量の処理が行われる場合には、処理に揺らぎを
これは外部APIによっては呼び出し制限のある場合もあるためです。
そうした非同期処理には"tiny-async-pool"という　npmパッケージを利用しています。おすすめです。

https://www.npmjs.com/package/tiny-async-pool

### APIのエンドポイントやトークンはハードコードしない
Functionsで外部サービスと連携する場合には秘密のトークンを扱います。
これをハードコードするのを避けるためには、Firebaseの環境変数を有効活用しましょう。

https://firebase.google.com/docs/functions/config-env?hl=ja


# Storage
画像の保存に使用

ここでは、FunctionsからStorageを監視して開発チームのSlackに特定の事象があったときに通知するなどしています。



### Google Cloud Vision APIによる不適切画像の検知

Storageでは、悪質ユーザが投稿しうる不適切な画像をGoogleが提供する学習済みモデルを用いて無害化する試みもしています。これは、恋活アプリの利用者の雰囲気を良好に保つためです。自前で機械学習モデルを準備するのは、少人数開発では難しいですが、Googleさんのおかげで巨人の肩に乗ることができました（圧倒的感謝）

https://cloud.google.com/vision/docs/detecting-safe-search

なお、繰り返しになりますが、利用するにはFunctionsとStorageを連携させる必要があります。

# Messaging
プッシュ通知に使用

プッシュ通知は実機でないと検証できない場合もあるため、吉野君やBくんは公式ドキュメント読みながら苦戦していました。
Firebaseコンソールから配信するタイプのプッシュ通知は簡単に送信できる反面、自由度がありません。
結局のところ、Functionsのロジックからプッシュ通知を送る仕組みが必要であり、ここは面倒なので覚悟をしておいてください。

# Security Rules
これは非常に重要ですので、Firebase Emulator Suiteでセキュリティルールをテストすることを**強く**お勧めします。もしわけないですが、セキュリティ上の理由からSecurity Rulesに関しては説明は割愛します。

Firebase Emulator Suiteに関しては後述します！

# Analytics
クラッシュレポートやユーザー解析。これから活躍予定。


# Hosting
管理画面用に使っています。
サービスのホームページなどは他のクラウドでWordpressなどでやっています。

# ステージングと本番環境の切り分け

これはプロジェクトを複数作ることにより実現しました。アプリ側などでプロジェクトをうまく切り替える必要があります。
デプロイに関しては、プロジェクトを扱うとFirebaseの環境変数がややこしくなって、ターミナルからのデプロイ時にうまくこの環境切り替えができず、手動でデプロイ作業をする必要があります。この問題に関しては解決策を模索中ですが、スマートなやり方を見つけたら記事を書こうと思います。

以下のようなフローでやっています
ローカルでのEmulator Suiteで動作確認
→プルリクとCIへてstagingブランチにマージ
→本番ブランチと本番環境へデプロイ

### 将来の機能追加に対する保険としてstaging環境を準備

今現在はstagingはそこまで役立ちませんが、ユーザーが増えてからは本番環境で実験するのはリスクが高すぎます。
ですので、ローカルエミュレータで扱えない複雑な動作確認はstagingと実機を組み合わせて行いたいと思います。
特に**プッシュ通知あたりは実機でないと検証ができない**ので、将来的にstagingに分けたメリットを享受するのではないでしょうか。

# Firebaseのテスト環境: Emulator Suite

Firebase Emulator Suiteはここ数年で徐々に機能がロールアウトされていきました。
最初は実際にステージング領域のFirebaseプロジェクトにデプロイして動作確認をしていましたが、デプロイに時間がかかることや、ある機能に必要な特定の環境を（例：マッチング機能を確認するためにFirestoreにマッチングしていないユーザを手動で追加する）再現するのが面倒であることなどが課題でした。
そこで、Firebase Emulator Suiteをローカル開発環境とGitHub Actionsに導入しました。
最終的には、ほぼ全てのサービスをEmulator上でテストすることができました。

https://firebase.google.com/docs/emulator-suite

## Emulatorが活躍したこと
このテストというのは、具体的には以下です。

- iOS側のシミュレータをエミュレータに接続してFirestoreやFunctionをローカルで一貫してテストする

- 管理画面の動作確認をある程度ローカルで完結させる
Cloud Functions上に実装したAPIエンドポイントのテストなどをローカルで動作確認できるのは非常に重宝しました。
ただし、エンドポイントがテストと本番環境では異なるので、アプリ側でもそちらをスイッチさせる必要がありました。

- GitHub Actions上でFirebase Emulator Suiteでのセキュリティルールの単体テスト
これにより、単体テストをパスしないコードはメインブランチにマージされないのでコード品質は爆上がりしました。
一方で、テストケース作成は非常に労力がかかるというデメリットがあります。開発は相互レビューで進められていたため、私はテストケースを書くというよりは、レビューする側でしたが、レビューする側としてもメンタルがいかれたこともあります。

## Emulatorのデータをimport/exportできる
エミュレータ上に手動でユーザーを複数追加し、Firestoreに情報を手動で追加するというのは非常に時間がかかります。
Firebase Emulator Suiteでは、全体のデータのimport/exportができるため、まとまったダミーユーザなどを含んだデータをあらかじめチームで共有しておき、時間を節約できました。

このようにインポートしたりエクスポートしたりできます。

```
firebase emulators:start # データなし
firebase emulators:start --import=fixtures/local # 個人seed利用
firebase emulators:start --import=fixtures/share # seed利用


firebase emulators:export fixtures/local # 個人利用
firebase emulators:export fixtures/share # 全体共有
```

特定の状況を再現するために、こうしてスナップショットを残しておくと、バグの再現などで非常に生産性が上がります。

## 余談：テスト駆動開発の良い側面とそうでない側面
GitHub Actionsを用いてFirebase Emulator Suiteによる単体テストなどを行いました。
セキュリティルールの記述がB君に偏り、負担が大きくなった時期もありました。
しかし、セキュリティルールが正しく実装されている限り、Emulator Suiteによるテストでセキュリティを確保できるため、今回のような顧客のプライバシーが最重要なサービスでは必要経費だったでしょう。
また、GitHub Actions上でテストを通過していないとマージをしないルールにしていたため、コードの品質の担保には特に重点を置いた開発が行われた点は良かったと思います。

### 顧客の重要な情報を扱う上でテストは避けて通れない
テストを書くのは大変な作業ですが、おそらく個人情報を多く扱う大規模IT企業では当たり前の慣習のようで「テスト駆動」とかいちいち言わないと聞いたことがあります。
ですので、個人情報を使う場合はEmulator Suiteで特にセキュリティルールの検証を行う必要があります。

# リージョン:初めから適切に設定しよう

プロジェクト作成時に決定するリージョンは何もしなければus-centralになります。**Firebaseの機能によっては、このリージョンが今後変更できない場合がある**ようです。Functionsで明示的にデフォルトリージョンと違うリージョンを指定できる場合もあるようですが、初めからリリース時に相応しいリージョンにしておく必要があります。間違えてus-centralがデフォルトになっていたので、新しくプロジェクト作り直してTokyoリージョンにした苦い記憶があります。なお、レイテンシなどを考えると日本でサービスをする場合は、基本的にTokyoリージョンが無難だと思います。

# Firebase Extension

## Trigger Email

メール配信にはsendgridなどではなく、Trigger EmailというFirebase Extensionを利用しています。
これはFirebaseが作成したもので、内部ではNodemailerがラップされています。
元々はFunctionsにNodemailerを実装しようとしていましたが、**車輪の再発明**を防ぐために拡張機能を使いました。

https://github.com/firebase/extensions/tree/master/firestore-send-email

Firestoreドキュメントを通してメールの配信ができる点が便利です。

#### SMTP URIとはなんぞや
ただし、**SMTP URI**というあまり聞いたことのないURIを設定する必要があります。
これは、SMTPの認証情報を一文で表したものです。`smtps://`みたいなやつです。
メールサーバーと繋ぐ際にはこのURIの表記方法でつまづきました。

このSOにもあるように、SMTP URIはほとんど使われないものなので、どのサイト見ても表記が曖昧でした。
普段はSMTP認証情報を別々のフィールドに入力するのですが、それを一つの文字列にまとめるというメリットがあるそうです。

https://stackoverflow.com/questions/4738348/is-there-an-accepted-syntax-for-smtp-server-and-how-to-parse-it

# 最後に: 先人の足跡に学ぶ

恋活アプリを作り始めたときに、京大工学部情報学科[^4]に当時在籍されていた @kot10n5tn さんの記事を見つけ、大変参考にさせていただきました。当時はFirestoreも正式リリースでなかったことや、Firebase Emulator Suiteなども今ほど充実していなかった中、同じ大学生が実際にFirebaseでサービスをリリースされていたということに大変感銘を受けました。

https://qiita.com/kot10n5tn/items/eeb2e81d9f0713f404dc

当時はFirebaseのいろいろな機能がベータだったにもかかわらず、ファーストペンギンとして有益な知見を提供していただき本当に感謝しています。同様に、僕たちの投稿も、将来誰かがFirebaseでアプリを作るときに参考になれば大変嬉しいです。

### 公式ドキュメントしか勝たん!

とはいえ、Firebaseでは新しい機能については依然情報が足りなかったり、仕様が変わったりする傾向にあります。そのため、Firebaseの公式ドキュメントは非常にお世話になりました。Googleのテクニカルライターの皆さん、わかりやすい記事をありがとうございます。

https://firebase.google.com/docs


# 先着1000ユーザーまで永久無料キャンペーン実施中!

Firebaseをふんだんに使ったアプリを皆さんもぜひ体感してみてください。
まだまだ、サービス自体はUI/UXなど、改善の余地は山ほどありますので、ぜひ感想などをコメント欄にどしどしお願いします！！！

![6.5インチ – 3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614389/3e2551e0-936a-714d-da33-b57d5a99bb09.jpeg)


https://apps.apple.com/jp/app/id1592092244

https://meeple.jp


お読みいただきありがとうございました！

# 脚注

[^1]: 本業の機械学習とこのプロジェクトは関係ありません。しかし、ユーザが十分に集まれば、いずれはMLベースのレコメンディングエンジンを導入したいです。この手のレコメンディングエンジンにはコールドスタート問題が予想されるので、まだまだ先になりそうです。
[^2]: ただでさえ工学の学部に異性はいないのに、情報工学課程に配属されたのでもうほぼいません。。
[^3]: firebaseの公式ユースケースを参照 https://firebase.google.com/use-cases
[^4]: どうでもいい話ですが、僕はこの学科に切実に行きたかったんですが、１浪してもいけませんでした。🥺