【個人開発】「後で」返事ができる文通チャットアプリ「レイター」を作った話：技術編

最近、個人開発として世にリリースしたものとしては3つ目となるアプリ、「レイター」をリリースしたので、それについて紹介しようと思います。この記事では主に
- レイターで採用した技術
- 採用の理由
- その技術を使ってみての感想
- 作ってみた感想

あたりについて書こうと思います。レイターがどういうものか、なぜ作ろうかと思ったかの話はNoteの方に書きましたので、ぜひそちらもご覧ください
[「後で」返事ができる文通チャットアプリ「レイター」を作った話](https://note.com/0ba/n/n10ea38136202)


# レイターの軽い紹介
詳しい紹介は[Note]（リンク）のほうに任せるとして、ここでは簡単に紹介をします。
ざっくりはストア用のスクリーンショットをご覧いただければ伝わるかと思います。
![スクリーンショット 2022-12-06 10.07.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268579/dc04bb5e-d76a-aead-d3b2-ad397940f41d.png)

レイターを一言でいえば「後で」返事ができる文通チャットアプリです。
文通友達の募集を公開し、それに興味を持った人が返事をすることで二人の間でチャットができるというサービスです。雑誌の文通相手募集のコーナー＋そのままアプリ上で文通ができるアプリだと思うとよいかもしれません。
ここまでで言えば割とよくあるアプリなのですが、大きな特徴は送ったメッセージがあえてランダムで数日後に届くという部分です。
あえて時間差で届くことが、従来のメッセージアプリでおきがちな、「早く返事をしなくてはならない」というプレッシャーや「返事がこないな・・」という不安に対するアンチテーゼとして機能すると考えました。
そもそも遅れて届くわけなので返事するのは後で気の向いた時で良いと思えるし、返事も気長に待とうと思えるという狙いです。

興味を持ってくれた方は是非一度インストールお願いします。
アカウント作成なども必要ないのでサクッと試せます。

iOS
https://apps.apple.com/us/app/%E3%83%AC%E3%82%A4%E3%82%BF%E3%83%BC/id1644506915

android
https://play.google.com/store/apps/details?id=com.obata.myPaceTalk


# 採用した技術とその理由、使ってみての感想
## Expo(React Native)
- iOS android両方リリースしたかったためReact Nativeを選びました。
- Expoは何度か使っているので手に馴染んでいたのもあります。
    - [React Native(Expo)とFirebaseでカップル向けアプリを作った](https://qiita.com/0ba/items/1e894d308db5951fb575)
- またReactも直近でよく書いてたので手に馴染んでおりました。
    - [【個人開発】推し曲を動的に生成したOGP付きで共有できるサービス「推し曲.com」を作りました](https://qiita.com/0ba/items/1e894d308db5951fb575)
- 今回は技術の習得ではなくリリースを優先したかったので慣れている技術を使いました
    - 個人開発のコツとしては、新たに挑戦する技術は1つか2つかに抑えることがコツだと思ってます（リリースを目的としている場合）
- また、今後も継続的に新しいアプリを作りたいと思ってるので、Expoにとことん慣れて自分の中に資産を積み重ねようと思いました。
    - Flutterにも浮気したい気持ちがあったり、SwiftUIを書きたいと思う瞬間もあるのですが、手広くいくと、個人開発の限られた時間内でキャッチアップに占める時間が増えてしまうのでグッと堪えてます。
- 個人的にはExpoは1年半くらいまえに前に使った時よりさらに使いやすくなってる気がしました。
    - 特にビルド周りです。
    - また、マネージドワークフローでできることがどんどん増えてる印象があります。
    - Expoに元々慣れてるだけあって、開発中に大きく困ることはありませんでした。
- 個人的に今回やっといてよかった設定は、buildNumberの自動ナンバリングです。buildするたたびに手動でbuildNumberをインクリメントする必要がないのがとても楽でした
    - [ExpoでiOSアプリをeas buildする時はbuildNumberをapp.config.tsで動的に設定すると楽だよって話](https://qiita.com/0ba/items/22fb052e1c3737295b2a)

## Firestore(Firease)
- こちらもリリースまでのスピードを重視したかったため選択しました。
    - Firestoreにも同様に慣れてるのですが、ほんとにクライアント側に集中できると感じます。
- 後ろ向きな理由としてサーバサイドが好きではないし、得意ではないというのもあります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268579/02455a77-2005-a7ce-7fa0-49dad4d82ed2.png)
 「かくいう私もサーバサイドが苦手でね」

- 今回の規模ですとCloud Functionと組み合わせることで不自由さはあまり感じませんでした。
- ただ、複雑なクエリを発行するには複合インデックスを設定する必要があり、そのためにコンソールポチポチが必要だったのでめんどくさかったです。
    - 特に今回はFirebaseを開発、検証、本番と3つ使っていたので、全てに同じ設定をするのが大変でした。
    - 手動での設定はミスの可能性も高まるのでちゃんとやるならFirebase CLIで設定するべきでした。
- 複雑すぎるクエリは実現できない
    - Firestoreで可能な範囲のクエリで可能な限り機能要件を満たすにはパズルみたいな思考が求められました。
    - ときには機能要件自体を変える必要もありました。
- ドキュメント数のカウントが運良くリリースされて助かった
    - 未読数をカウントするために条件に当てはまるドキュメントを数える必要があったのですが、これまでは100件ドキュメントがあったら、100読み込みする必要があり料金的に非現実的でした。それがレイターを作っている最中にCount関数が運良くリリースされたので、気兼ねなくドキュメント数をカウントできるようになりました。本当に運がよかった。
- TTLポリシーで削除を予約することで節約につながるのがいい感じ
    - できるだけお金をかけたくないので、とあるコレクションの中身は時間経過で古いものは削除したいと思いました。
    - 最初はCloud Fuctionで定期的に削除スクリプトを実行しようと思ったのですが、有効期限を設定するだけで勝手に削除してくれる機能に気がついたのでそちらを使いました。
    - 注意として、削除は期限切れから72時間以内のどこかで実行されるがいつ削除されるかはわからない、有効期限順に削除されていくわけではないという点があります。割とアバウトな削除で良い時は問題ないですが、厳密に有効期限でデータを削除したい時にはやはりCloud Functionなりを使う必要があるようです。
- リレーションを考えるのが難しい
    - 読み取り回数で課金されるので、自前のRDBみたいにとりあえず関連するテーブルをジョインして返すみたいなことをすると読み取り数がちょっとまずいことになりそうです。
        - 例えば誰かとのメッセージのやりとりの画面で、メッセージごとに送信ユーザのドキュメントを取得するようにすると単純に読み取り数が倍になります。メッセージなどは一度画面を開くだけで十数個は読み取りが恥じるので、それが倍になるのは結構大きいと感じます。
        - こういう時は送信ユーザーは別で一回だけ読み取るようにするなどの工夫を考える必要がありました。
- しかし、総じて言えばFirestoreは最高です。今後も使い続けたいと思います。

## Cloud Function
- 活用しているのは以下です。
    - プッシュ通知を送る部分
    - 時間差でメッセージを届ける部分
    - ユーザーをブロックした時の処理
    - ユーザアカウントを削除する処理
- 今回初めて利用しました。
- Clouf FunctioというかFirestore　の[Node clientのドキュメント](https://googleapis.dev/nodejs/firestore/latest/index.html)が見づらいのがつらかったです。（検索機能がないのは辛い・・・）
- typescriptで書きましたが、FirestoreのNode clientは型が適用されなかったのが謎です
    - ここらへん、設定をちゃんとやれば解決する気がします。
    - 今回はコード量が多くなかったのでそのままにして作り切ってしまいました。
    - 全てをindexにベタ書きしてたりするので、割と雑です。

## Cloud Task　
- アプリのコアとなる時間差でメッセージを届ける機能のために、非同期タスクを登録するために使いました。
- 同期のタスクを登録するのにはCloud Schedulerも候補に上がりましたが、大量の非同期タスクを登録するにはCloud Schedulerだと金額がとんでもないことになる（だいたい1メッセージ1円もかかってしまう！）のでCloud Taskにしました。
- Cloud Functionからタスクを簡単に登録できるので難しさは特にありませんでした。


# 推しポイント
- アプリ全体の色使いが気に入ってます。
    - 大人っぽく落ち着いた雰囲気になるように心がけました。
    - Twitterで見かけた女性向け商品の配色というツイートを参考にさせてもらってます。
        - https://twitter.com/TaeLifeBlog/status/1568350893921415170?s=20&t=_Mkld_kRVdbZgz8f4RF5Bw
- スクショもかなり気に入ってます。
    - 伝えたいメッセージを厳選していく作業が楽しかったです。

# 大変だったポイント
- 時間差で届くという機能をどう実装するかで頭を悩ました
    - いくつか実現方法は考えましたが、できるだけシンプルに実装するにはという点で結構試行錯誤（手は動かしてないので正確にいうと検討）を行いました。
    - 最終的にClod Taskにたどり着けてよかったです。
    - 時間差で届くとチャットアプリというのは前例がないので、チャットアプリの基本的な体験が流用できない部分があったので、そこの体験設計が難しかったです。
    - 具体的にいうと、普通のチャットアプリは連投ができるのが普通ですが、時間差で届くという都合上連投はできないようにするしかありませんでした。
    - その代わり、相手を指定して別のトークルームで会話を始められる形にしました。これはちょっと苦肉の策ではあります。
- 利用規約、プライバシーポリシーを作るのが骨が折れた。
    - 利用規約とかは軽いサービスでは作らないこともありますが、今回は割とリスクがありそうなアプリだったのでちゃんとつくりました。
    - 色々なサービスを参考にさせてもらいなんとかつくりました。
    - 正直リスクヘッジすべきことは無限にあるため、最終的に完璧な規約をつくることは不可能だと判断し、そこそこで妥協しました。

# まとめ
振り返ってみると、だいだい3ヶ月くらいでリリースまで辿り着きました。
前回作った推し曲.comの1週間に比べると長めですが、この規模のアプリとしては結構早く作れ他のではないかと思います。
というより、時間をかけてるとモチベーションが落ちてしまうかもしれないという不安から、リリースまで走り抜けた節はあります。
本当に個人開発はモチベーションが命だと思います。思いついたアイデアはモチベーションが新鮮なうちに作り切るべし！

アプリの狙いや、なぜ作ろうと思ったか、今後の展望などはNoteに書いてるのでこちらも是非ご覧ください
[「後で」返事ができる文通チャットアプリ「レイター」を作った話](https://note.com/0ba/n/n10ea38136202)

ダウンロードはこちらから↓
iOS
https://apps.apple.com/us/app/%E3%83%AC%E3%82%A4%E3%82%BF%E3%83%BC/id1644506915

android
https://play.google.com/store/apps/details?id=com.obata.myPaceTalk

Twitterでは主に個人開発について呟いてます。よければフォローお願いします。
→https://twitter.com/ObataGenta

このプロダクトは、株式会社mofmofの「水曜日の個人開発」にサポートされています。
https://indie-dev.mof-mof.co.jp