データ通信量のモバイルアプリのUXに与える影響

1 モバイルアプリにおけるデータ通信量の削減の重要性

インターネットを使ってコンテンツを配信するアプリ(Web, Android, iOS)の開発をしてますが、データの通信量というのがユーザー体験に影響するというのは経験的に知っていても、なかなか迅速に改善できなかったりします。このあたりの事情や悩みを書いていきます。

前提としてコンテンツの閲覧が主な機能であり、サーバーとの通信を要するアプリを想定しています。カメラ、決済、ゲームなど全く事情が違うアプリでは当てはまらないことも多いと思います。

1.1 ユーザーの通信量は限られている

一般的なモバイル回線の例として、docomoのirumoというプランでは毎月の通信量を3GB、6GB、9GB から選べるようになっています。月に6GBだと考えると1日あたり200MBです。ユーザーはこの200MBの枠をどのアプリで使うのかを考えることになります。200MBといってもYouTubeやInstagramのような大手アプリは見たいでしょうし、OSやアプリのアップデートもありますから、それ以外のサービスに割り当て可能な通信量はもっと少ないでしょう。

通信量の制限に達した場合、速度制限された通信モードになります(irumoの場合は送受信時最大128kbps)。通信量が少ないアプリではかろうじてアプリを使用することもできるかもしれませんが、通信量が多いアプリでは使うことが難しいでしょう。

1.2 通信環境が悪い

都市部でも地下鉄などでインターネットに繋がりにくい環境がありますが、人間が生活しているのは都市だけではありません。スマートフォンは手軽に持ち運べますが、通信量が多くて通信に失敗するのでは意味がありませんよね。

このような要因から広いユーザー層を獲得するために、データ通信量を少なくすることが重要だと考えています。

2 計測

データ通信量を多くしているものはなにか、という要因はアプリによります。闇雲に改善を試みるのではなく、原因の調査をまず行うことが正攻法です。

主な原因としては、動画や画像などのサイズが大きい。データを取得する頻度が多い。1回あたりのデータ通信量、ペイロードが大きい。というようなことが想像できます。

2.1 Firebase Performance Monitoring

データ使用量に関する計測を簡単に行える一般的な方法として、Firebase Performance Monitoring を使用するという選択があります。FirebaseはGoogleの開発プラットフォームで、Androidアプリはもちろん、iOSアプリでも利用できます。Performance Monitoring は Firebase の数ある機能のなかの一つです。

Firebase SDKが自動でネットワークリクエストを測定して、時間がかかっていたり、ペイロードのサイズが大きいエンドポイントを見つける事ができます。

導入コストがかなり小さいのがメリットで、すでにFirebaseを利用しているならば何もしなくてもFirebaseのWebコンソールから確認する事ができます。

エンドポイントごとにまとめられるので、エンドポイントごとにAPIが別れているREST APIのようなものでは、どのエンドポイントが遅いのか、データ使用量がお多いのかを調べることができます。しかし最近使われることが多い GraphQLではエンドポイントが単一なので、エンドポイントごとの測定結果を見ても詳しいことはわかりません。GraphQL APIでは単一のエンドポイントにクエリを投げることで、データの取得や更新などの操作を行うからです。

GraphQL APIの測定については、Firebase Perfomance Monitoring 以外で頑張る必要があります。

また、Performance Monitoringでは平均やパーセンタイルのデータを見る事はできても、外れ値のユーザーがいた場合の調査には向いていないという印象があり、このサービスだけでは不十分だと感じています。

2.2 プライバシー

Performance Monitoring が不十分と感じた場合、より詳しい計測を行うには独自にユーザーのデータ使用量を計測する必要があります。

データ使用量を計測するためにはクライアント側、つまりAndroidやiOSアプリ側で計測をする必要があります。APIサーバーの計測をするだけではなく、画像などのメディアのロードや、広告のためのデータ通信があるからです。

各OSが提供している機能のなかで実現する必要があります。少し調べたところ、AndroidではTrafficStatsを使ってアプリの送受信のデータ量を取得することができるようです。一方iOSではURLSessionTaskMetricsを使ってURLごとのデータ通信量を取得することはできますが、アプリ全体の通信量を直接取得する手段は見つかりませんでした。

データ通信量を収集することが技術的にできたとしても、しかしアプリ側でこのようなデータを収集することにはプライバシー面での懸念があります。

配布する国や地域によってプライバシーや個人情報に関する法律があるので、それに違反しないようにする必要があります。プライバシーや個人情報に関する法律は、日本の個人情報保護法、EUのGDPR、カリフォルニア州のCCPAなどがあります。

また、アプリを配布するプラットフォームの規約やポリシーにも従う必要があります。AppleのApp StoreもGoogleのPlay Storeもプライバシーについての項目があります。通信量の計測が直接違反になることはなさそうですが、ユーザーに同意を取る必要があるようです。

規約やポリシーに違反すると、アプリ公開の審査が通らなかったり、アプリの公開停止となるリスクがあります。

3 技術的な解決策

以上のように計測が難しいという問題はありますが、具体的な改善案というのはいくつもあります。ここではどのような手法でデータ使用量の削減ができるのか、各手法について概略を並べてみます。

3.1 画像の最適化

画像や動画といったメディアファイルはテキストファイルよりもサイズが大きいです。なのでメディアファイルを必要十分な品質を保ったまま、サイズを削減することはデータ通信量の最適化に大きな効果があります。

モバイルアプリのデータ通信量の削減に効果のある画像最適化には以下のようなものがあります。

モバイルアプリで使う画像は比較的小さい表示領域なはずなので、表示領域にあわせたサイズにリサイズすることでデータ通信量を削減できます。

画像フォーマットについては、JPEGとPNGが一般的ですが、より高圧縮のWebPを採用することでファイルサイズは大幅に削減できます。新しい画像フォーマットはクライアント側で対応していないと表示することができませんが、WebPはすでに主要なブラウザでも対応済みで、モバイルアプリでも最新のバージョンでは表示可能ですが、古いバージョンに関しては表示できるか確認してください。WebPは十分に普及しているので採用に大きな障害はないと思います。AVIFについてはまだ時期尚早という印象です。

実際にどのようにして画像最適化を行うのかというのは、バックエンドのスタックによって異なるので省略します。

画像のロードタイミングを調整することも時には必要です。スクロールした先にあるコンテンツや別のタブにあるコンテンツを先にロードしておくことは、ユーザーの操作によって即座にコンテンツが表示されるというメリットがあります。しかし一方で、無駄なデータの通信をしてしまうデメリットもあります。バランスを考えて判断することが重要です。

画像の最適化がまだ行われていない場合、比較的着手しやすく確実に効果がでやすい改善策だと思います。

3.2 キャッシュ

キャッシュとはデータを使う場所とデータソースとの間に仮置きの場所を作ることで、データを取得するコストを削減したり、パフォーマンスを上げるための手法です。

キャッシュの難しさのひとつがデータの一貫性です。キャッシュを使うということは、サーバーにあるデータと異なる可能性が生まれます。どこまで許容できるのかのバランスを考えるというのが悩みどころです。

どこに、何を、どれくらいの期間キャッシュするのかというのはプロダクトやプロダクト内の機能によって異なります。

モバイルアプリでは、HTTPクライアント、画像ライブラリ、WebViewなどでキャッシュを利用する機能が存在します。デフォルトで有効であるかどうかは使用しているクライアントやライブラリに依存します。

キャッシュの期間は、APIや画像のレスポンスヘッダ含まれるCache-ControlやETagを参照して決まります。Cache-Control: max-age=3600というヘッダは3600秒すなわち1時間のキャッシュの有効期限という意味です。更新頻度が少ない場合には有効期限を長くし、更新頻度が多い場合は反対に有効期限を短くするといった調整が可能です。

ETagはリソースの特定のバージョンを表す識別子です。リソースに変更がないのにダウンロードしなおす必要はありませんよね。クライアントはHTTPリクエストにこの値を If-None-Match ヘッダにつけます。リソースの更新がない場合は、サーバーが304 Not Modifiedを返すので、キャッシュされたリソースを利用します。

HTTPクライアントや画像ライブラリのキャッシュ機能を使うだけでは不十分な場合もあります。その場合は独自にキャッシュを作る機能を作ります。特定のデータをアプリ側のDBに永続化したり、ファイルを保存する機能です。

このようにしてキャッシュを利用してデータ通信量の削減をできますが、キャッシュはデバイスのディスク容量を使うので、大量のキャッシュが溜まるのを避ける注意も必要です。

キャッシュの扱いは難しいですが、効果的に利用すれば大きな効果が期待できるので取り組む価値はあるでしょう。

3.3 API設計の見直し

アプリケーションサーバーとのAPI通信も見直すことで、データ通信量を抑えることができることもあります。

例えばユーザーの「お気に入りのコンテンツ」一覧を返すAPIがあるとします。お気に入りのコンテンツの数が多いユーザーの場合、すべてのデータを返すAPIしかないと毎回大量のデータを通信することになります。n件ずつ取得するAPIにして、クライアント側ではページネーションするように実装すれば多くの場合でデータ通信量を抑えられます。

必要なデータ以外を取得する必要があるAPIしかないと、不要なデータ使用が増えます。例えば「お気に入りのコンテンツ」の件数をクライアント側で知りたいとします。しかし件数を返すAPIがないため全件取得してその数を数えているという場合です。その他にも、ユーザーの名前だけを取得したいのに、ユーザーに関するその他の情報も一緒に取得するようなAPIしかなく、無駄とはわかっていながらもその他の情報も取得するというような場合です。

こんなことありえるのか、と思われるかもしれませんが、歴史のあるプロダクトだと、過去にAPIの開発をして、クライアント側で機能を追加したいがAPI開発にコストをかけられないというような歴史的経緯から効率的でないAPIが使われている可能性はありそうです。

GraphQLではクエリによって取得するリソースを細かく指定できるので、ユーザーの名前だけ取得するというようことができます。こういった柔軟なデータの取得ができるのはGraphQLのいいところですね。

# request
query {
   user(id: "42") {
     name
   }
}
# response
{
  "data" : {
    "user": {
      "name": "Luke Skywalker"
    }
  }
}

このようにGraphQLでは、単一のエンドポイントに対してほしいデータを記述したクエリを送ることで、データを取得します。より多種類なデータを一括で取得したい場合には、クエリに記述を追加します。これはGraphQLのメリットではありますが、複雑なクエリを毎回リクエストに含める必要があるため、通信量が増えるというトレードオフがあります。

この問題を解決する方法はないわけではありません。Persisted Query という仕組みでは、GraphQLのエンドポイントに対してクエリの代わりにハッシュ値を送ります。サーバーがそのハッシュ値がどのクエリなのかを知っている場合は、クエリを送られたときと同じようにレスポンスを返すことができます。ハッシュ値を登録する必要がありますが、同じような大きなクエリを繰り返し使用するようなプロダクトでは効果的です。

ここまではJSONを返すAPIを前提としていました。しかしデータ通信量の削減という観点からいうと、gRPCという選択肢もありえます。gRPCではJSONのようなテキストデータより圧縮率が高いバイナリデータを使用します。ただ既存のAPIをgRPCに移行するのは大きなコストが掛かりますし、エコシステムの充実度などの問題から、アプリで使用するには簡単な選択ではないでしょう。

3.4 データ節約のための機能を追加

データ節約のための機能を追加することも考えられます。例えばコンテンツをWi-Fi接続時にダウンロードするといった機能です。画質が主なコンテンツの場合、モバイル回線ではデータ節約のために低画質の画像を読み込むという機能も考えられます。どのような機能がよいかはプロダクトによって異なります。

例えばInstagramではデータ節約モードという、事前に動画読み込みを行わなくなる設定があります。Instagramは画像と動画で大量のデータ通信を行うので、データ通信量削減に効果的な機能です。

Wi-Fi接続時とモバイル回線使用時でデータ取得の処理を分けるにはそれぞれのOSのAPIを利用することで実現できます。AndroidだとConnectivityManagerで取得できます。

4 技術以外の話題

ここまでデータ通信量のUXに与える影響、計測、解決方法について書いて来ましたが、それを実行に移すために、障害となることもあります。

4.3 領域をまたぐ問題

データ通信量の最適化は領域をまたぐ問題です。これまで見てきたようにAPIサーバー、モバイルアプリ、広告、分析など多くの領域が関係しています。そのため問題を発見して把握することが難しくなっているように感じます。規模の大きなプロダクトでは複数の担当者で分担して開発している場合が多いので、その人達の間での調整も必要となります。

4.2 広告

広告にも通信が発生します。

アプリで広告を表示するには、広告プラットフォームの広告SDKを使用します。自社のアプリのなかに広告を表示するための部品のようなものです。どのような広告を取得して表示するのかというのは、広告プラットフォームとその設定によって決まります。そのためアプリ開発者にとっても、広告の通信の実態は把握しにくいものになっています。

最近は動画広告も一般的になり、通信量を増やす要因になっています。

広告の場合、通信量を減らすために表示を減らすという手段が取りにくいという問題もあります。昨今ではインターネットにおける広告売上単価は減少しており、広告を減らすという判断は難しくなっています。

4.1 非機能要件の優先順位

開発チームが存在しているといことは、開発すべき機能がある状態のはずです。その他の機能開発や修正があるなかで、データ通信量の最適化をどの優先度で取り組むのかというのは難しい問題です。

取り組みにくい理由を考えみました。

思いついたものを列挙しているだけなので、こういうこともありえるよね、くらいの温度感で見てもらえると助かります。

短期的には取り組みにくい問題かもしれませんが、長期的なUX改善のために取り組んでいきたいですし、そのためにチームとして取り組みやすい仕組みを作っていきたいですね。

まとめ

このようにデータ通信量の最適化は複雑な問題です。しかし多くのユーザーの通信環境を考えると避けては通れない問題なので、できるだけ取り組んでモバイルに最適なプロダクトを開発していきたいですね。