React Nativeの利点と運用について (2020年版)
By kkoudev
現在React Nativeで mocri というiOS/Androidアプリケーションを開発しています。
このアプリケーションは2019年3月にiOS版をリリースし、その3ヶ月後の2019年6月にAndroid版をリリースしています。
(mocri で使用している技術については過去何度かWEBエンジニア勉強会の方でも軽く紹介させていただきました)
今回運用し始めて約1年ほど経ったため、運用する上での利点、意識するべきポイントがいくつか見えてきましたので、紹介させていただきます。
1. React Nativeはスタートアップのサービスおよびチームの構築に最適である
これはReact Nativeの特性上からわかることだと思われますが、
iOS/Androidアプリケーションを一度に開発できるのが最大の利点です。
(この利点は、同様にクロスプラットフォーム開発ができるFlutterにも言えることだとは思います)
今回React Nativeを採用した mocri というアプリケーションは音声通話アプリということもあり、
1つのプラットフォームだけでは特に成り立ちにくいアプリケーションでした。
そのため、iOS版を先行リリースするにしても、あまり間を空けずにAndroid版をリリースしたいという思いがありました。
上記にも書いた通り、iOS/Androidアプリケーションのリリースの間が3ヶ月空いてますが、最初の1ヶ月半はiOS版の機能開発の期間であり、
Android版を開発し始めたのは4月の下旬で、5月の下旬には完成していました。(その後、QAとバグ修正をして6月上旬にリリース)
5月はGWもあったので、実質Android版の開発は1ヶ月もかかっていません。
また、この1ヶ月で作ったところはネイティブブリッジで実装しているTwitter認証や音声通話まわりの部分のみです。
UI部分はAndroid用に若干の調整を行っただけで、ほぼiOS版開発時のコードそのままで作成することができました。
このクロスプラットフォーム開発できる点はチーム構築についても大きな利点があり、
iOS/Androidを同時に開発できるということは、それだけエンジニアも少ない人数で開発することが可能ということです。
サービスのスタートアップにおいては少人数で開発することが多く、
仮に人数が多くいたとしてもコミュニケーションコストが増大してチームとして纏まりづらい事態が多々発生しがちです。
そのため多くの人数を採用するとそれだけ意思決定スピード(≒開発スピード)を遅くしてしまいがちになります。
またUXデザインの手法でよく語られるサービス開発で重要なポイントは、いきなり完璧なものを作り上げるわけではなく、
コアユーザーにヒットする機能を練り上げ、その上で改善・スケールアウトしていくことがしばしば重要視されています。
いきなり大人数で開発するほどの大きな機能を作ろうとすることはこの原則からもずれているため、
敢えて小規模体制からスタートすることで強制的に風呂敷を広げすぎない動きをしていくことも可能になっていきます。
もちろんサービスの規模にもよりますが、そのちょうど良い塩梅を実現できるのがReact Nativeであると私は思っています。
色々良いことだけ言いましたが、
これはメンバー同士との相性も大いに関係してくるので、一概にこれだけで意識すれば実現できるポイントではありません。
ただ、重要なポイントの1つであることには違いないと思います。
そのため、React Nativeは少人数でまとまりやすいチームを作る上でもスタートアップに最適なアーキテクチャーであると私は考えています。
2. UIの作成が非常にしやすい
1の利点に合わせて、React Nativeの特製の1つはHot Reloadが可能でCSSでスタイリングできるというポイントです。
CSSといってもReact Native用なのでブラウザで使えるスタイルがそのまま使えるわけではありませんが、ほぼWebのスタイリングの知識があれば転用可能です。
ただ実際にはメディアクエリを使用可能にする react-native-extended-stylesheet のライブラリを使わなければ解像度の異なる複数デバイス対応の運用には堪えられないケースが殆どなので、このライブラリを入れることが前提となります。
ネイティブでUIを作成する場合、iOSの方はUIKitが非常にクセのある仕様で、例えば単なるトルツメをしようにもUIStackViewを使う必要が出てきたり、それがAutoLayoutと合わさったり、UITableViewのセルの高さ計算と組み合わさると利用箇所によって意図しない動きをすることが多々あります。
AndroidについてはLayout EditorがXcodeのInterface Builderに比べると使いやすいので比較的まだマシな方ですが、
Androidは古いOSのバージョンもそこそこシェアがあるため、古いOSのサポートを考えながらUI作成をしだすとButtonビュー1つを取っても結構違いがあったりします。
(例えばiOSのUIButtonのようなアイコン付きボタンをButtonビューで実現しようとすると古いOSだとサポートされていない機能も多々あって少々大変)
そのため、これらの影響を考慮しながら作っていくのはなかなか大変です。
その点、React Nativeではflexboxレイアウトなので頭で思い描いたスタイルを比較的そのまま実現しやすく、JSC上で動くためOSのバージョンによって使える機能が大きく異なるということはほぼありません。
SwiftUIやJetpack Composeの誕生によってある程度ネイティブでも作成しやすくなったものの、
やはり大きな利点である 1 でも説明したクロスプラットフォーム開発が1つのコードでできる点というのはReact Nativeが強いポイントとして揺るがないと思います。
3. ネイティブブリッジがあるので、ネイティブコードで出来ることをほぼそのまま実現可能
React Nativeはネイティブコードで実装することも可能です。
これは公式のドキュメントをみるとiOSはObjective-C、AndroidはJavaのコードだったりするのですが、Swift/Kotlinでもネイティブブリッジすることが可能です。
また、これは単なるロジックだけでなく、UIコンポーネントもネイティブブリッジできます。
なので、React Nativeにおいてライブラリでも用意されていない機能を実装する必要性が出てきた場合は
ネイティブブリッジを行うことで実装することが可能です。
もっとも、多用すればするほどクロスプラットフォームコードにはならない箇所が増えてしまうので、あくまで最終手段として使います。
今まで mocri を作ってきた中ではネイティブブリッジさえできれば解決できる問題が殆どで、
それでも解決できない問題というのは基本的にはありませんでした。
大抵React Nativeの採用を躊躇う理由の1つに、ネイティブでなければ実現できない機能があると困るという理由があると思うのですが、
この問題については2020年現在においては、基本的に React Native本体のコードが致命的にバグっていなければそのようなことは無い と言えます。
過去のReact Native(0.56系以前)には日本語入力時の不具合など致命的な問題もありましたが、
0.57以降のReact Nativeについては大きな問題もなく、(仮にあっても回避可能なレベル) アプリを実装できています。
mocriを実装し始めたころはReact Nativeが 0.57系 の頃だったのでタイミング的にもちょうどよかったというのもありそうです。
4. バージョンは積極的に上げていくのがオススメだが、上げ方とタイミングの見極めが重要
mocriを作り始めたころは React Native 0.57 系だったのですが、現状は 0.61 系になっています。
バージョンは上げて問題なければ上げる というスタンスでやっているのですが、これは結構大事だと思っています。
なぜなら、過去のReact Native採用失敗事例をみていると、バージョン互換の問題にハマってそれを放置し続けた故にバージョンを上げられなくなっているケースも多々あるためです。
バージョンが最新版から離れれば離れるほど、互換性を維持することが難しくなってきますし、いつまでも修正できないReact Native本体の不具合も残ったままになりがちです。
常に最新版に追従する必要はないですが、React Nativeのバージョンを上げる際は 0.57 → 0.58 → 0.59 …というように、
マイナーバージョンを1つずつ上げてテストして問題なければ再度上げる、としていくことをおすすめします。
(つまり、0.57 → 0.60 のように一気に上げるのは互換性のない変更が多数入っているケースがあるため、あまりおすすめできないです)
また、React Nativeのバージョンを上げる際は、バージョン番号のリビジョンが 2 以上になったタイミングがおすすめです。 つまり、 0.57.2 や 0.61.2 といったように、最後のリビジョン部分が 2 以上になったときです。
React Nativeはマイナーバージョンが上がったばかりの状態(つまり、0.61.0 のようにリビジョンが 0 のとき)は
大抵のケースでデグレや何かしらの新機能追加に伴う問題が発生していることがあります。
そのため、ある程度そのようなバグが取れた段階で上げる方が安全という理由です。
5. TypeScriptは必ず導入するべきである
長期運用においてはTypeScriptの導入が必須と言っても過言ではありません。
型がない言語はIDEやエディタで入力補完もしづらいのと、エラーの検知もしづらいため、TypeScriptは絶対導入しましょう。
React Nativeは 0.57 から正式にTypeScript対応をしているため、TypeScriptを採用することも比較的簡単です。
6. ライブラリにバグがあればPRを送る気概を持っておく
React Nativeにはコミュニティベースでライブラリの開発や運用がされています。
ただ、コミュニティベースである以上、利用者が少ないライブラリは特に修正がされないケースがあり、
利用者がそこそこ多い場合でもコミッターの人が何もしていないケースもあったりします。
これはReact Nativeに限った話ではありませんが、そのような理由からもしバグがあれば自分でPRを作って送ることもときには必要になります。
ただ、PRを送らなければいけなかったケースはそこまで多くはなかったので、
あくまで問題があった場合に自ら直してPRを送るという気概は持っておいた方が良い、という心構えの意味合いの方が強いです。
それによってある意味問題があれば自分で直せばいいという安心感も生まれます。
7. 最低でもネイティブで作る際の開発環境設定の知識は必要になる
これはハッキリ言っておきたいのですが、React Nativeを採用する上でもiOS/Androidネイティブの環境設定についての理解は必須になります。
単なるコードを書くだけであれば確かにReactの知識だけあれば作れる箇所も多いのですが、
環境別のConfigurationの設定および切替、fastlaneによるビルドとアップロードの設定、iOSであればCocoaPodsとCarthageとSPM、AndroidであればGradleやAndroidManifestの設定、
については最低限必要になってきます。
これについては各所でも言われていることなので、WebのフロントエンドエンジニアがiOS/AndroidアプリケーションをReact以外の前提知識なしに作れる、とは思わない方がいいです。
どちらかといえば、ネイティブでiOS/Androidアプリケーションを作ったことがあり、かつWebのフロントエンドもある程度わかるエンジニアが開発効率を上げるために使うアーキテクチャー、だと私は考えています。
・・・と、これだけいうと敷居が高いように聞こえてしまうかもしれませんが、
ネイティブの設定といっても環境設定がメインなので、ネイティブでUIやロジックをめちゃくちゃ書けるようになっていなければならない、というわけではありません。
なので、環境設定まわりだけ理解すればあとはReactを書くのがメインになるので、経験がなかったとしても敷居はそこまで高くはないと思います。
8. Hermesは使わない方が良い
React Nativeは 0.60.2 からHermesという高速なJavaScriptエンジンを使えるようになりました。
が、こいつはPHPでいうHHVMと同様に、互換性の面でまだまだ問題があります。
特に問題なのはMobX(v5以上)を採用しているケースで、MobXは内部的にES2015以上で使えるProxyを使っているのですが、
HermesはこのProxy機能が実装されていません。
他にもいくつか実装されていない機能があるので、その辺の互換性を考慮しながら使用するライブラリを選ぶというのはかなり運用難易度が上がってしまいます。
よっぽど簡単なアプリで殆どライブラリを使う必要がなければまた話は別かもしれませんが、
長期運用が発生する可能性があったり、ある程度の規模になっていくことが想定されるアプリの場合はHermesは基本的には採用しない方向をおすすめします。
というよりHermesを使わなくても、React Nativeは基本的には問題のないパフォーマンスを出すことは可能です。
まとめ
以上、React Nativeを採用する上での利点と運用ポイントについて代表的なものを説明させていただきました。
細かいポイントを挙げだすとまだまだあるのですが、React Nativeは使いこなすことができれば非常に強力なツールとなりますので、特にスタートアップでiOS/Androidアプリケーションを開発しようとしている方は是非とも触ってみてください。