アプリの通信回数を減らすためにやったこと

# はじめに
CYBIRDエンジニア Advent Calendar 3日目担当の[@kyorokyoro](https://qiita.com/kyorokyoro)です。
2日目は[@march_f](https://qiita.com/march_f)の[ラズパイで居留守を極める](https://qiita.com/march_f/items/63e9a465083f07a59070)でした。
楽をするために開発するエンジニアならではの為になる楽しい記事でした！

# 概要
**アプリのサーバとの通信回数を減らしたい！**
と思ってやったことについてまとめました。

# 目次
- 1.なぜ減らす必要があるのか
- 2.どうやって減らすか
- 3.やってみて困ったこと

## 1.なぜ減らす必要があるのか

基本的にデータはDBに保存しているため通信して色々なデータを取得して画面に表示することになります。


例)
**プレゼントボックス画面に遷移**
<font color='red'>アプリ</font>「ユーザーのプレゼントボックスになにがあるか通信して取得しよう！」
<font color='blue'>サーバ</font>「このユーザーのプレゼントボックスにはこんな未受け取りのアイテムが有るよ！」
**もう一回プレゼントボックス画面に遷移**
<font color='red'>アプリ</font>「ユーザーのプレゼントボックスになにがあるか通信して取得しよう！」
<font color='blue'>サーバ</font>「えっ……さっき教えたじゃん……」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/372006/4f19a50a-ba97-c42f-4567-c58d6b51de77.png)

という感じで2回目の通信は必要ないですよね。

通信回数が多いことで問題になるのは

**UXが悪い**
通信をしてレスポンスを待つ必要があり画面遷移に時間がかかる。
ユーザーの通信状況によっては更に時間がかかる。

**サーバの負荷になる**
DBにアクセスしたりサーバ側で計算したりと色々。
1回の通信なら問題ないがたくさんのユーザーが通信をすれば通信量が増えて負荷になります。

## 2.どうやって減らすか
通信の種類として大きく分けて更新系と検索系があります。
**更新系**
　プレゼントを受け取るなどユーザーデータの状態が変わるもの。
**検索系**
　プレゼントの未受け取りデータを取得などのユーザーの状態を取得するもの。

更新系に関してはDBの更新など通信が必要になるため、通信回数を減らすことはなし。

検索系も2つに分けてマスターデータ取得とユーザーデータ取得がある。
**マスターデータ**
　全ユーザー共通のデータ
**ユーザーデータ**
　各ユーザーごとのデータ

マスターデータは基本的にデータの更新は少ないので一度通信して取得して以後はしないのでそもそも通信回数は少ない。

**問題はユーザーデータ**
　こちらはユーザーがなにかアクションするたびにいろいろなデータが更新されていく。
　そのため最新データを取得するには通信する必要がある。
　が、先述したとおり2回目以降では通信の必要がない箇所もある。
　この場合にどうすればよいかというと1回目の通信をアプリ側に保存しておけば良い。

例)
**プレゼントボックス画面に遷移**
<font color='red'>アプリ</font>「ユーザーのプレゼントボックスになにがあるか通信して取得しよう！」
<font color='blue'>サーバ</font>「このユーザーのプレゼントボックスにはこんな未受け取りのアイテムが有るよ！」
<font color='red'>アプリ</font>「他の画面に遷移しても忘れないで覚えておこう！」
**もう一回プレゼントボックス画面に遷移**
<font color='red'>アプリ</font>「ユーザーのプレゼントボックスになにがあるか通信して取得しよう！」
<font color='red'>アプリ</font>「と思ったけど前のデータが残ってるから通信しなくていいや！」

となる。

ただユーザーデータは事あるごとに更新されるものである。
プレゼントボックスの例でいうと、プレゼントを受け取ったりガチャを引くことでプレゼントボックスにアイテムが追加されたり。
なにもしないとアプリ側に保存したデータを使い続けるため画面が更新されません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/372006/911dd9c4-09a2-ad77-7fff-5ed8b099f9dc.png)


そのためプレゼントボックスが変更する通信をする際にデータを更新する必要がある。

**1. プレゼントを受け取る**
　プレゼントを受け取る通信と同時にプレゼントボックスを取得する通信を行う。
　受け取り後にすぐプレゼント一覧を表示するため同時に行う。

**2. ガチャを引く**
　ガチャを引く通信後に保存していたプレゼントボックスの情報を削除する。
　ガチャ画面ではプレゼントボックスの情報は必要ないのでここでは通信せず情報の削除だけ行う。
　プレゼントボックス画面に遷移した際に情報がないため通信を行うことでガチャによって更新されたプレゼントボックスの情報を取得される。

**3. 運営側がアイテムを付与する**
　この場合ユーザー側からはタイミングがわからない。
　なので運営側が更新した時間とユーザーが保存した時間を比較する。
例)
**プレゼントボックス画面に遷移**
<font color='red'>アプリ</font>「ユーザーのプレゼントボックスになにがあるか通信して取得しよう！」
<font color='red'>アプリ</font>「と思ったけど前のデータが残ってるから通信しなくていいや！」
<font color='red'>アプリ</font>「でも保存した時間より運営側が更新した時間の方が新しいぞ、更新されてるから通信しよう！」


## 3.やってみて困ったこと
#### どの更新系の通信で検索系の通信を更新する必要があるかの洗い出しが大変

すべての通信を確認して下記をまとめる(アプリ側・サーバ側ともに知識が必要)

* 保存したデータを使ってもよいか

* 同時に呼ぶ必要のある通信はなにか

* 保存したデータを削除する通信はなにか

→プレゼントボックス画面に遷移する前にバッジで未受け取りの数を表示する必要があるため、バッジ数を取得する通信は回数が多くなる。
　(プレゼント以外のバッジ数も取得するため管理が大変……色々なテーブルを参照するから処理が重いし……)
　どこまで最新のデータを通信で取得して表示するかはゲーム仕様との相談。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/372006/4e96d051-4585-7dbf-ced1-3c2279c4f5ed.png)



#### テスト時に漏れが気づきにくい
更新があった場合でもエラーなく表示をされてしまうのでデバッガーが気づかない。
→New Relicなどの観測ツールで通信回数を確認して多くなっていないかチェック。

#### パラメータ付きの通信の保存をどうするか
パラメータでページ数を渡してページごとに表示されるプレゼントボックスだけを取得する場合、同じ通信でも各パラメータごとに情報を保存しなければいけないためメモリ使用量が増え管理も大変になる。
→パラメータありの場合通信するようにしているがここは今後改修したいところ。

## さいごに
通信回数を減らして快適なUXを目指しましょう！
今回はコードは書きませんでしたが需要があれば別記事であがるかも

明日の[CYBIRDエンジニア Advent Calendar 2021](https://qiita.com/advent-calendar/2021/cybird) 4日目は、
 @chikako_ikedaさんによる「TargetSDK 30対応中にハマったこと」です。
毎年TargetSDKをあげる作業が必要なのでどんなことで苦労したのか参考にさせていただきます！

ありがとうございました。