なぜ出前館アプリでReact Nativeを採用したか?

黒澤慎治氏(以下、黒澤):私から、出前館のアプリ開発で大変だったところと解決策、現在の状況についてお話をしたいと思います。大変だったところと解決策についてお話する前に、現在の出前館アプリのメンバー構成をおさらいします。先ほどお話したとおり、アプリ開発メンバーiOSは4人、Androidは2人、フロントエンドが1人に加えてパートナーの方で開発を行っています。

このメンバー構成でReact Nativeアプリを開発していると話を聞いて、こう思った方もいるのではないでしょうか。「なぜReact Nativeなんだろう?」「なぜAndroid/iOSネイティブで書き直さなかったのだろう?」。このような疑問を持った方もいると思うので、出前館アプリでReact Nativeを採用している理由から説明したいと思います。

まずReact Nativeの特徴についておさらいします。React Nativeは、Reactの知識を活かしてAndroidとiOSのクロスプラットフォーム開発ができるツールです。JavaScriptコードで書かれていて、AndroidやiOSのネイティブエンジニアから見た時の特徴的な機能として、OTA(Over-The-Air)アップデートが可能であることなどの特徴を持ちます。この発表ではReact Nativeの具体的な内容については触れませんが、特徴をおさらいしました。

React Nativeの特徴を踏まえて、なぜ出前館アプリでReact Nativeを使用しているのか。なぜAndroid/iOSネイティブへ移行せずに、React Nativeで書き続けているのか説明しようと思います。既存コードがReact Nativeであったこと。さらに、出前館アプリがかなり複雑なロジック、およびドメインで成り立っていることから、リプレイスすることはとても難しく、コストがかかります。

そのため、事業フェーズとして改修を入れることよりも、React Nativeをメンテナンスし続けるほうがいいというのが理由の1つです。2つ目の理由として、AndroidとiOSを1つのソースコードで管理できることから、スピード感を持った開発ができることが挙げられます。出前館アプリでは、現在複数のFeatureが同時に複数チームで開発されています。

今のスピード感で開発するのはクロスプラットフォームを使用しないと難しいため、React Nativeを選択していてよかったと感じています。将来的にAndroid/iOSのネイティブアプリで書き直す選択肢はありますが、スピード感を持った開発を続けるため、現状はReact Nativeを使用することが最善と判断して使用しています。

出前館アプリ開発で大変だったところ:React Nativeの学習

以上の経緯を踏まえた上で、出前館アプリ開発を行う上で大変だったところと解決したこと、現在の状況についてお話したいと思います。

最初の課題は、React Nativeアプリを開発する上で前提知識として必要な、React Nativeの学習でした。ここでは出前館アプリの開発を行うために必要だった、React Nativeの学習コストについてお話したいと思います。先ほどReact Nativeの概要をお話しましたが、実際にAndroid/iOS開発者がReact Nativeアプリの開発をするために必要となった知識について説明したいと思います。

React Nativeアプリを開発するためには、当然ですがReactやReact Nativeについての知識が必要になります。宣言的UIやファンクションコンポーネント、HooksやReact Nativeのコンポーネント、そしてReact Nativeをネイティブ層で使う知識など、多くの知識を必要とします。

次に言語です。ReactおよびReact NativeではJavaScriptを使用します。出前館アプリではTypeScriptを使用しているため、TypeScriptの知識も必要になります。TypeScriptはSwiftやKotlinと記法が似ているところもありますが、思想も異なっているところも多いため、学習が必要です。

最後に、React Native開発を行うための環境や、ライブラリなどへの理解です。Node.jsやビルドシステム、統合開発環境、ネットワーク、アーキテクチャへの理解や、テスティングフレームワークなど把握するべき知識は多いです。

これらの知識をすべて学習するためには、学習コストがそれなりにかかる課題がありました。React Nativeを用いたアプリ開発を行うにあたり、先ほどお伝えしたような知識が必要となります。チームの中にAndroidやiOS開発者が多いため、ReactやReact Nativeの開発経験がないメンバーも多くいました。さらに、iOSやAndroid開発者が頻繁にジョインする環境のため、新しく入った人はReactやReact Nativeを学ぶ学習コストがかかります。

これらの学習コストがかかることはある程度許容していますが、学習コストを下げるための取り組みをいくつか行ったので紹介します。

React Nativeを学ぶために行った取り組みは主に3つです。まず、Reactをよく知っているエンジニアから学ぶようにしました。チーム内には、先ほど発表した植松さんを含め、何人かReactをよく知っているエンジニアがいます。

そのため、まずはReactを知っているエンジニアに相談するようにしました。React NativeはReactの技術を多く使用しているため、Reactに強いエンジニアの存在はとても大きいです。また、大き目の案件ではSlack上にスレッドを立てて作業をしました。困っている状況を早めにわかるようにすることで、初心者がよく躓くようなポイントは早めに解決できるような仕組みを作りました。

また、モブプログラミングや勉強会をチーム内で週に1度行っています。この場では軽微な不具合修正やリファクタリングをチームで取り組んだり、新しく取り入れた技術をシェアしたりしています。

最後に、学んだ知識のドキュメント化です。新しくジョインした人が効果的に学習できるように、Reactに関するTipsや、プルリクエストで受けた指摘などをドキュメントにまとめるようにしています。

このような取り組みを行った結果、現在はiOSやAndroidエンジニアもReact Nativeを使用できるようになり、新しくジョインした人も効率よく学習できる環境ができました。また、出前館にはアプリだけでなくWebサービスも存在するため、現在はフロントエンドチームともコミュニケーションを取ってチーム開発を進めています。

後ほど詳しくお話しますが、フロントエンドチームがNext.jsを採用しているため、アプリとWebで効率よく開発を進められるような取り組みを行っています。フロントエンドチームとコードベースで相談しやすいところも、React Nativeを学習してよかったことの1つとして挙げられます。

出前館アプリ開発で大変だったところ:リファクタリング

続いて大変だったこととして、リファクタリングの話をします。出前館アプリでは、1つのコンポーネントに多くのレンダリングロジックやビジネスロジックが集約されており、コードを理解するのに時間がかかる課題がありました。

また、クラスコンポーネントで書かれているコンポーネントを、より新しい技術を使っていくために、ファンクションコンポーネントで書き直したいという課題もありました。

これらのコードベース上の課題に加え、出前館のサービスでは多くのドメインが存在します。そのため、ビジネスロジックをリファクタしていくために、ドメインの把握も必要となっていました。

リファクタをできるだけ早く安全に行うために、React Nativeのエコシステムを存分に活用しています。(スライドを指して)こちらの図は出前館アプリを開発する際の、ビルドからプルリクエストまでの一連の流れとなっています。

まず、Android/iOSのビルドです。Android/iOSのネイティブモジュールのビルドは、ネイティブモジュールに更新があった時のみ必要となります。

そのため、基本的に時間のかかるネイティブモジュールのビルドは必要ありません。React NativeのJavaScriptバンドルを使用するため、ローカルサーバーを立ち上げます。続いて、エディタ上で変更を加えます。React NativeにはHot Reloadingの機能が備わっているため、エディタ上で保存することで、即時に変更の確認が可能となっています。

また、UIを簡易的に確認するための手段としてStorybookがあるため、Storybookを実装することも多いです。変更が完了したらテストの実装です。ビジネスロジックだけでなく、先ほど書いたStorybookに対してStoryshotsを使用することで、コンポーネントのスナップショットテストが可能になっています。

テストまで実装できたら、プルリクエストを出します。プルリクエストのチェックとして、変更があった場所に応じてAndroid・iOS・React Nativeモジュールのそれぞれに対し、テストを実行するようにしています。また、SonarQubeを用いることで、テストコードのカバレッジ計測や技術的負債を可視化できるようにしました。

プルリクエストがマージされたあとは、リリースを行います。リリースブランチにプルリクエストがマージされた場合は、CI上でステージングアプリのリリースを自動実行するようになっています。

このような取り組みを行った結果として、今まで1つのクラスコンポーネントに大量のロジックが記載されているところから、(スライドを指して)このようなかたちにコードのリファクタリングが進んでいます。

UIのロジックについてはコンポーネント内部のみに記載し、ビジネスロジックを分離できました。UIのコンポーネントを分割して適切にテストを書いていくことで、コンポーネントの役割を明確化しています。また、クラスコンポーネントをファンクションコンポーネントへと移行して、ビジネスロジックにはHookを使用しています。

Hookにユニットテストを書くことで、ビジネスロジックのコードの安全性も担保しています。ここまでで、アプリのビジネスロジックはUIから分離できました。

しかし、現在の出前館アプリ、Webにはまだ課題はあります。出前館アプリとWebサービスで、同じビジネスロジックを書くことが多いという課題です。

アプリとWebで同じビジネスロジックを書くのであれば、極力1つの箇所にまとめたいです。WebアプリはNext.jsを採用しているため、TypeScriptのHookをアプリ・Webで書いて2つのコードを管理するよりも、可能であれば1つのコードで管理したいです。

現在取り組んでいること

現在取り組んでいることとして、BFF(Backend For Frontend)をつうじてビジネスロジックの共通化を図っています。BFFサーバーは厳密にはビジネスロジックの共通化のためだけに作られているわけではありませんが、本発表ではリファクタリングに焦点を当てているため、リファクタリングのために作られていると考えてもらえればと思います。

クライアントとバックエンドに中間層を作り、アプリ・WebからはGraphQLをつうじてリクエストを行うことで、クライアント側で必要な情報のみを使用可能になっています。BFFサーバーはTypeScriptで書かれていて、フロントエンドチーム主導で運用されてきました。最近の取り組みとして、アプリ開発チームもBFFサーバーの開発に着手しています。

アプリ開発チームで切り出したビジネスロジックを、BFFへ移行していくようなかたちで実装を行っています。実際に私もBFFサーバーの開発に着手していますが、ふだんからReact NativeをTypeScriptで書いていることや、BFFサーバーがTypeScriptで書かれていることから、スムーズに開発に着手でき、React Nativeを開発していてよかったと感じています。このようなかたちで、アプリ・Webチームでのチーム開発を推進しています。

最後に、多くの機能を複数チームで継続的にリリースするための準備を行いました。出前館アプリ開発を始めた当初、大規模開発は想定されていない環境だったため、CIやCDはありませんでした。ここから大規模開発へ移行するため、リリース作業を自動化したい課題がありました。また、多くのFeatureを管理して、適切なプロダクトの質を保ちながら素早く機能をリリースする必要があります。

リリースを素早く継続的にリリースし続けるため、開発チーム内でルールを決める必要がありました。

(スライドを指して)多くの機能を継続的にリリースし続けるための準備として、これらの取り組みを行ってきました。CI/CDには4ヶ月ほどかけて、1人エンジニア専任で環境を開発しました。

App Store、Google Play、OTAアップデートやステージングリリースなど、ほぼすべてのリリース作業を半自動化して、プルリクエストのチェックなども含め、CI/CD環境が充実している状態になっています。

次に、リリースの管理です。継続的かつスピード感を持ってリリースを行うため、LINEのQAチームにサポートしてもらってQAを行っています。また、リリーススケジュールおよび多くの機能をドキュメント化して管理しています。

最後にOTAアップデートについてです。hotfixが必要なブランチ戦略やバージョニングの管理、OTAアップデートのプロセスなど、すべて決めました。hotfixとしてOTAアップデートを活用できることで、緊急時のhotfixまでのリードタイムが短くなるため、結果としてスピード感を持って複数機能開発を進めやすくなっています。

まとめです。出前館アプリは少人数での開発から大規模開発へと移行してきました。多くの改善を行うことで体制が整い、現在はReact Nativeの開発環境も充実してきています。また、スピード感を持った継続的な開発ができるように、現在もさまざまな取り組みを行っています。

以上、「出前館アプリのこれまでとこれから」をお話しました。ありがとうございました。