React Native導入ガイド - 環境構築からクロスプラットフォーム開発のメリットまでを理解する
React Nativeは、クロスプラットフォーム開発を行うためのツールの1つです。JavaScript言語の中で、UIフレームワークのReactを用いてUIの記述と状態管理を行うことで、アプリの動作を組み立てます。本記事では、React Nativeの導入やモバイルアプリ開発の現場で起きがちな課題を解決する手段としての強みについて解説します。
- ReactとReact Native
- Reactとは/React Nativeとは/React Nativeの正体
- React Nativeの環境構築
- npm文化圏の流儀にのっとったワークフロー/スタイル定義/npmのライブラリ使用/リッチな機能を追加
- 広がるReact Nativeの世界
- Windows向けのMS公式実装/ブラウザ向けの逆輸入/React Nativeが持つUIの妥当な粒度
- React Nativeを軸にした人材戦略
- 日本のサービス提供者が抱える宿命/増員は茨の道/限られた人数での開発/React Nativeの得意分野と課題/腕力のある器用な人を活かす
プラットフォーム間でソースコードを共有したり、共通の方法論で複数のプラットフォーム向けのアプリを作成したりするための開発手法のことを、クロスプラットフォーム開発と呼びます。React Nativeは、そのクロスプラットフォーム開発を行うためのツールの1つです。JavaScript言語の中で、UIフレームワークのReactを用いてUIの記述と状態管理を行うことで、アプリの動作を組み立てます。
React Nativeの特性をよく理解し、うまく特性にマッチした事業や組織で採用すれば、モバイルアプリ開発の現場で起きがちな課題を解決する手段としてその能力を十全に発揮できます。本記事では、React Nativeの導入、モバイルアプリ開発・人材戦略のためのReact Nativeの強みについて解説します。
ReactとReact Native
まずは、ReactとReact Nativeについて解説しておきましょう。
Reactとは
Reactは、Facebookが開発している、JavaScriptのためのUI管理フレームワークです。基本的にはブラウザ向けのWebアプリケーションの開発に使用されています。
React – ユーザインターフェース構築のための JavaScript ライブラリ
その特徴は大きく分けて2つあります。特殊な記法を用いて、JavaScriptコード内にUIを記述する JSX と、JSXを組み合わせて作成したUIの部品である コンポーネント です。
それぞれ、Reactを使ってUIを作っていくためのパワフルな仕組みです。個別に解説していきましょう。
【Reactの構成要素1】JSXについて
JSXは、JavaScriptコードの中にレイアウトを記述できるようにFacebookが考案した、XMLライクな記法です。以下のページで仕様が公開されています。
ReactでUIを構築する上で、JSXの使用は必須ではありません。しかし、どのようなレイアウトになるのかを想像しやすくなるため、使用が推奨されています。
JSXの簡単な例を挙げてみましょう。Reactでアプリケーション開発を行う際には、以下のようなコードを書くことができます。
import React from 'react'; import { Text } from 'react-native'; import render from './render'; const greeting = <Text>Hello!</Text>; render(greeting);
式として書かれた <Text>
というタグ要素で文字が囲まれているので、どうやら画面に「Hello!」というテキストが表示されそうだ、ということが想像できますね。ですが、「JavaScriptにそんな文法あったっけ?」と思った方もいるはずです。
その通り、JavaScriptの中にはJSXのようなタグ要素を書くことはできません。一度、JSXが書かれたJavaScriptをコンパイルして、JavaScriptとして正しいコードに変換する必要があります。変換には、JavaScriptコンパイラのBabelを用います。
上記の例は、Babelによる変換を行うと、以下のようなコードになります。
import React from 'react'; import { Text } from 'react-native'; import render from './render'; const greeting = React.createElement(Text, null, "Hello!"); render(greeting);
これで、JavaScriptの処理系が読み込めるコードになりました。
実は React.createElement
をひたすら並べることでも、Reactを用いたUIの構築はできます。とはいえ、UIの構造を定義するならばXMLライクな記法があったほうが便利なので、JSXの使用が推奨されているのです。
【Reactの構成要素2】コンポーネントの作成
Reactを用いたアプリケーション開発では、再利用可能な画面部品である、 コンポーネント を作成し、これらを組み合わせてUIを構築します。コンポーネントは、JavaScriptのクラスまたは関数として作成できます。本記事では関数での作り方を紹介します。
では、コンポーネントの作り方を見ていきましょう。 View
や Text
といった基底のコンポーネントの役割については後述しますが、この時点ではレイアウトを区切ったり、テキストを表示したりできる機能を持ったタグだと思っておいてください。コンポーネントの例を示します。
import React from 'react'; import { View, Text } from 'react-native'; // (1) const Greeting = (props) => { return ( <View> <Text>Hello! {props.name}</Text>{/* (2) */} </View> ); }; // (3) const App = () => { return ( <View> <Greeting name={"Taro"} />{/* (4) */} <Greeting name="Jiro" />{/* (5) */} <Greeting name="Hanako" /> </View> ); }; export default App;
コンポーネントは慣例として、(1)のようにパスカルケースで名前を付けます。また、関数で作成する場合はprops(プロップス)と呼ばれる引数を渡すことができ、(2)のようにJSXの中で使用できます。文字列として式を展開する場合は {props.name}
のように波括弧で囲みます。
こうして作成した Greeting
コンポーネントを、(3)の App
コンポーネントで使います。コンポーネントはJSXのタグ要素として使用することができます。また、何度でも再利用が可能です。
関数の引数になっていたpropsに値を渡したい場合は、(4)のような形で、属性として値を与えます。この属性は {}
で値を囲むのが本来の書き方ですが、文字列を渡すだけの場合は(5)のように {}
を省略することもできます。
このようにコンポーネントを組み合わせることで、ReactによるUI構築が行われます。
React Nativeとは
React Nativeは、Facebookが開発している、モバイルアプリ開発のための各種ツール群の総称です。
2015年にReact Nativeが発表されるより少し前に、WebViewを活用した「ハイブリッドアプリ」が市民権を得ていく過程で、各プラットフォームの本来のUIを「ネイティブUI」と呼ぶ文化が一部に生まれました。そういった時代背景から、Reactフレームワークを通じてネイティブUIを扱うためのツールは「React Native」と名付けられたのでした。機械語をネイティブコードと呼ぶ文化を連想してしまいますが、まったくの別物です。
React Native · A framework for building native apps using React
react-native
パッケージから提供されるコンポーネントを組み合わせてUIを作成すると、AndroidとiOSのどちらでも動かすことができます。
試しに、前述のコードを動かしてみましょう。少しだけスタイル情報も加えてみます。
import React from 'react'; import { View, Text, StyleSheet } from 'react-native'; const Greeting = (props) => /*省略*/; const App = () => { return ( <View style={styles.container}>{/* (2) */} {/*省略*/} </View> ); }; export default App; // (1) const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', }, });
(1)では、 StyleSheet
モジュールを使って、 container
という名前のスタイルを定義しました。ここではCSSに準拠した記述ができるので、今回はCSS 3で導入されたレイアウト指定の仕様であるFlexboxの各種プロパティを使って、「自身は画面いっぱいに広がりつつ、中身は上下中央寄せになる」という指定を行いました。これを(2)で View
コンポーネントのスタイルとして適用しています。
これを実行した結果が以下です。
(左)iOSで実行した様子 (右)Androidで実行した様子
iOSとAndroidそれぞれで、指定した通りに、画面中央にテキストが表示されています。
このようにReact Nativeでは、1つのソースコードを複数のプラットフォームで動かすことができるのです。
代表的なコンポーネントとモジュール
React Nativeでは、モバイルアプリを開発する上で最低限必要になるUIコンポーネントが、ちょうど良い粒度で提供されています。代表的なコンポーネントとして、以下のようなものがあります(表1)。
コンポーネント名 | 役割 |
---|---|
View | レイアウトを区切るための基本単位 |
Text | テキストを表示・装飾する |
ActivityIndicator | 処理待ち |
FlatList | 配列データを省メモリで並べる |
ScrollView | 内側のコンテンツが画面をはみ出たらスクロールする |
RefreshControl | 「引っ張って更新」を付与する |
▲表1:代表的なコンポーネント
また、UIとは関係なく、プラットフォームの機能を活用するために、モジュール群も提供されています(表2)。
モジュール名 | 役割 |
---|---|
Alert | 簡易なダイアログ表示をする |
Dimensions | 画面サイズやアプリの表示領域のサイズを取得する |
Keyboard | キーボードに関する情報を提供する |
Animated | アニメーションを扱う |
StyleSheet | コンポーネントに適用するスタイルを管理する |
Geolocation | 現在地の地理空間情報を取得する(window.navigator.geolocationとして提供) |
Fetch | 通信を行う(window.fetchとして提供) |
Console | デバッグログを表示する(window.consoleとして提供) |
Require | NPMモジュールや別のファイルとして定義されたモジュールを取り込む(window.requireとして提供) |
▲表2:代表的なモジュール
これらの機能を活用することによって、実用的なアプリを作れます。
標準ライブラリはブラウザに準拠
React Nativeの標準ライブラリは、基本的にブラウザに準拠したものになっています。 setTimeout
や fetch
などブラウザでよく使われているAPIで、アプリ開発ができるのです。このおかげで、ブラウザやNode.js向けに作られたライブラリのうち何割かはReact Nativeでもそのまま利用できます。
ただし、後述するようにDOMでビューの管理をしているわけではないので、 document
をはじめとしたDOMを扱うためのグローバル変数は提供されていません。あくまでもReactが持つ標準的な仕組みの範囲で、UIを更新していくことになります。こういった点でいうと、jQueryなどでDOMを操作することに慣れている方は、慣れるまでに少し時間がかかるかもしれません。
React Nativeの正体=Reactとスマホアプリのつなぎ役
JavaScriptで記述したHTMLライクなレイアウトが、実際に画面に表示されるのを見て、まず生じる疑問は「これは、ブラウザをアプリ内に組み込む仕組みであるWebViewではないのか?」だと思います。
しかし、React NativeはWebViewではありません。React Nativeは、JavaScript内に定義されたレイアウトを、各プラットフォームのUIコンポーネントに翻訳して描画します。
UIコンポーネントが翻訳される
JavaScript側で <ScrollView>
というReactコンポーネントを記述した場合に、Androidでは ScrollView
、iOSでは UIScrollView
に翻訳されます。React NativeがJavaScriptライブラリとして提供しているすべてのReactコンポーネントは、各プラットフォームの何らかのビューに翻訳されるのです。
2つのレイヤー「UIライブラリ層」「ミドルウェア層」
React Nativeは、次の2つのレイヤーが連携することで成り立っています。
- UIライブラリ層
- ミドルウェア層
React Native製のアプリを以下のように図示すると、その立場がわかりやすいかもしれません。
AndroidアプリやiOSアプリである以上、画面をつかさどるクラスである Activity
や UIViewController
は必ず存在しています。そういった「普通のAndroid/iOSアプリ」と呼ぶべき部分と、これまで解説してきたReactコンポーネントの組み合わせでできている「React製アプリ」の部分。この2つの間を埋めるのが、React Nativeです。
UIライブラリ層 は、JavaScriptライブラリとして提供されています。JavaScriptコード内で import { ... } from 'react-native';
の形で読み込まれるものがこれに当たります。主に <View>
や <Text>
といったReactコンポーネントを提供します。
一方で、 ミドルウェア層 は、私たちが書いたReactコンポーネントや、前述のUIライブラリ層を動作させるための、あらゆる努力を行います。主に次の部分から成り立っています。
- JavaScriptの動作環境(JavaScriptCore)
- JavaScript側でUIライブラリが利用された際に、各プラットフォームのUIに翻訳する、C++と(Java | Objective-C)で作られたミドルウェア
Android開発出身の筆者がReact Nativeに惚れ込んだのは、後者のミドルウェア部分(のAndroid向けJava実装)のコードを読んだ際に、筆者にも読めるくらいに複雑過ぎないコードで実現されていて感動したのがきっかけでした。また、このミドルウェア層はネイティブモジュールという仕組みを用いて自分で拡張したり、自作のUIコンポーネントを組み込んだりできます。Android SDKやiOS SDKでの開発に慣れ親しんだ人であれば作成が難しくないのも、魅力的な点です。
React Nativeの魅力は「ブラウザらしさ」と「Android/iOSアプリらしさ」の二面性
React Nativeは、Reactによるブラウザアプリ開発とAndroid/iOSアプリ開発の、いわば“いいとこ取り”をしたような特色を持っています。
JavaScriptのエンジンはSafariのものを借りてきていますし、標準ライブラリもブラウザに実装されているものの多くを再現しています。Reactで使えるコンポーネントも、ブラウザとは少しラインアップが違うものの、慣れればブラウザアプリを作る時と近い感覚で、自作のコンポーネントを素早く組み立てていくことができるでしょう。React Nativeは、JavaScriptエンジニアにとって「クセの強いブラウザ」と見なすことができると、筆者は考えています。
その一方で、視点を変えてみると、React NativeはAndroidの Activity
やiOSの UIViewController
の上で、ネイティブUIを描画しているという側面があります。JavaScriptやReactという特殊なUI記述言語を読み込んで、更新指示を受け取ることができる……そんな特殊能力を持った FrameLayout
や UIView
で作られたUIライブラリとしてReact Nativeを捉えることもできます。AndroidやiOSでのアプリ開発に慣れた方には、こちらの方がしっくりくるかもしれません。
基本的には「クセの強いブラウザ」として見なして開発を行い、Android/iOSのプラットフォーム機能を使いたくなった場合は、Android SDKやiOS SDKの助けを借りて「ブラウザ」の機能を拡張できる。そんな開発スタイルが、React Nativeには適しています。
それでは、実際の開発の流れを追いながら、ブラウザアプリ開発と近い感覚でできる部分や、ネイティブUIで拡張できることの強みについて、見ていきましょう。
React Nativeの環境構築
React Nativeでの開発を行う場合、事前に以下の環境をセットアップする必要があります。
- AndroidアプリのHello Worldができる環境(JDKやAndroid Studioのインストールなど)
- iOSアプリのHello Worldができる環境(XcodeやCocoaPodsのインストールなど)
- Node.jsの実行環境
ここの環境を全てそろえようとすると難易度が高めになります。もし実際にセットアップから試してみたい方は、公式ドキュメントの"React Native CLI Quick Start"の項に、より詳しい解説がありますので、そちらをお読みください。
npm文化圏の流儀にのっとったワークフロー
それではまず、ブラウザアプリの経験者が親しめそうなところをお見せしていきましょう。React Nativeのツール群の多くは、Node.jsのパッケージマネージャであるnpmで公開されています。公式CLIのReact Native CLIもその1つで、以下のようにインストールします。
$ npm install -g react-native-cli
次に、React Nativeのプロジェクトをセットアップしましょう。CLIで初期化コマンドを実行するだけです。
$ react-native init MyFirstReactNative
これで、必要なツール群がnpmによりインストールされます。出来上がったプロジェクトフォルダは、ブラウザアプリ開発に近い構成になっています。
エントリーポイントがindex.jsで、最上位のコンポーネントがApp.jsとして用意されています。
さて、それでは早速、React NativeのHello Worldを起動してみましょう。React Nativeでは、ターミナルで開発サーバーを起動して、開発中のJavaScriptをアプリに向けて配信する仕組みを採用しています。既にpackage.jsonには開発サーバーを起動するためのスクリプトが設定されていますので、以下のようにnpm start
を実行するだけでOKです。
$ cd /path/to/MyFirstReactNative $ npm start
これで開発サーバーが立ち上がります。React Nativeでは、JavaScriptをビルドして取りまとめる(バンドルする)ための専用ツールとして、Metroが用いられます。
続いて、別のターミナルで初期化コマンドを実行してみましょう。iOSアプリがビルドされ、iOSシミュレータの中で実行されます。
$ npm run ios
すると、実行されたiOSアプリからの呼び掛けに応じる形で、開発サーバーがJavaScriptのビルドを行います。
iOSアプリが開発サーバーからJavaScriptファイルを読み込む
ビルドされたJavaScriptは、iOSアプリに配信され、表示に使用されます。
実行される環境がブラウザなのかiOSアプリなのかという違いはあるものの、手順や考え方としては、Node.jsやnpmを利用してブラウザアプリ開発を行う場合とかなり近い考え方になっています。
CSS-in-JSでスタイル定義
さて、次はApp.jsに目を向けてみましょう。ファイルの最下部、スタイル定義の部分に着目してみます。
const styles = StyleSheet.create({ // 省略 sectionContainer: { marginTop: 32, paddingHorizontal: 24, }, sectionTitle: { fontSize: 24, fontWeight: '600', color: Colors.black, }, // 省略 });
コンポーネントについて解説した際に少し触れましたが、ブラウザアプリ開発とは感覚が異なる部分の1つが、このスタイル定義です。React Nativeによるコンポーネント定義では、CSSファイルではなく、この StyleSheet
モジュールを使用する方式が標準です。
ブラウザで策定されているCSSのすべてをサポートしているわけではありませんが、基本的にはFlexboxの alignItems
などでコンポーネントを配置、margin
やpadding
などでコンポーネント間を調整、 color
や borderWidth
等でコンポーネントを装飾していきます。
CSSとは違ってコンポーネントの種類ごとに使えるスタイルが分かれている点で注意が必要です。公式ドキュメントでは、次のように別々のドキュメントとして解説されています。
特にブラウザと挙動が違うのがテキストです。ブラウザではほぼすべての要素にテキストを記述し、装飾できます。しかし、React Nativeではテキストを記述できるのは Text
コンポーネントの中だけです。同様に、テキストを装飾するためのスタイルである color
などは、 Text
コンポーネントのみに適用できます。
テキストが表示できることをスタート地点にしているブラウザとは違って、AndroidやiOSでは限られた方法でしかテキストを表示できないことが、こういった違いとして現れています。
npmのライブラリがそのまま使えることが多い
npmを中心としたJavaScriptのエコシステムは巨大なものとなっており、npmリポジトリにあるライブラリを探せばよくある課題を簡単に解決できてしまうということも珍しくありません。
喜ばしいことに、React Nativeでのアプリ開発を行う際にも、そのエコシステムの恩恵を多分に受けることができます。React Nativeが台頭するより前から、コミュニティでは「ブラウザとNode.jsの両方で動くライブラリ(Universal JS)」という課題が扱われてきました。そのおかげで、Node.jsやDOMのAPIを必要以上に使わずに、共通で用意されている機能のみでライブラリを組み立てる文化が根付いています。そういったライブラリは、React Nativeでも十全に動くことが多いのです。
例えば、日付・時刻のライブラリとして有名なMoment.jsを使いたい場合、ブラウザアプリの開発と同じようにインストールします。
$ npm install moment --save
すると、React NativeのJavaScript内でインポートして使えるようになります。
import moment from 'moment'; // コンポーネント内 <Text>{moment().format('YYYY-MM-DD HH:mm:ss')}</Text>
上記が含まれたコンポーネントを実行すると、特に問題なく表示されました。
Moment.jsを利用した日時表示
JavaScriptのエコシステムという巨人の肩に乗ることは、生産性を向上する上で追い風になることでしょう。
ネイティブUIのライブラリでリッチな機能を追加する
せっかくネイティブUIを扱えるのですから、ネイティブUIを最大限に活かした機能も入れてみたいところです。npmリポジトリにはReact Nativeに特化したライブラリも多く存在しています。その中でも筆者がReact Nativeと相性がいいと思っているネイティブUIのライブラリがあるので、ご紹介しましょう。
react-native-mapsという名前のライブラリです。その名の通り、マップを扱うためのライブラリですが、AppleマップやGoogleマップをネイティブUIで扱える優れものです。
今回はiOS向けにAppleマップを表示してみましょう。公式ドキュメントの導入手順の通りに、npmでインストールします。
$ npm install react-native-maps --save-exact
インストールされたライブラリには、JavaScriptのコードだけではなく、JavaやObjective-Cのコードも含まれています。iOSで動かす場合には、ライブラリに含まれるObjective-Cコードとアプリ側のiOSプロジェクトをリンクさせる必要があるので、次のようにCocoaPodsのインストールコマンドを実行します。
$ cd /path/to/MyFirstReactNative/ios $ pod install Detected React Native module pod for react-native-maps Analyzing dependencies Downloading dependencies Installing react-native-maps (0.26.1) Generating Pods project Integrating client project Pod installation complete! There are 29 dependencies from the Podfile and 27 total pods installed.
これで準備は完了です。それでは早速、App.jsを書き換えてみましょう。
import React from 'react'; import MapView, { Marker } from 'react-native-maps'; const App = () => { const markers = [ // (2) { title: "中洲の島", coordinate: { latitude: 37.892216, longitude: 139.056579, }, }, { title: "ビッグスワン", coordinate: { latitude: 37.882522, longitude: 139.059196, }, }, { title: "鳥屋野潟運動公園野球場", coordinate: { latitude: 37.889901, longitude: 139.046972, } }, ]; return ( <MapView style={{ height: '100%' }} initialRegion={{ latitude: 37.8891012532126, longitude: 139.05403617011592, latitudeDelta: 0.035584745442925225, longitudeDelta: 0.022820403593073024, }}> {markers.map(marker => ( // (1) <Marker title={marker.title} coordinate={marker.coordinate} /> ))} </MapView> ); }; export default App;
このコードで着目すべき点は(1)です。(2)で用意した位置情報の配列を、map関数でJSXへと変換しています。既に解説した通り、実際にはJSXは React.createElement
の呼び出しコードに変換されるので、JavaScriptの式としてデータからの変換を行うことができるわけです。
では、このコードを実行してみましょう。先ほどと同様に npm run ios
でアプリを起動します。
マップにピンが表示されました。
JSXとしての記述はほんの数行でしたが、こうして簡単にマップを扱うことができます。従来、JavaやObjective-CでマップのSDKを扱う場合は、どのピンにどのデータが紐づいているのかを管理するのに手間がかかっていましたが、Reactを使うことでその手間が大きく削減されます。もしマップに複雑な状態管理を要するアプリを作りたい場合は、React Nativeの採用を検討してみる価値は十分にあります。
Reactが持つ「データをUIに変換する」という特性は、ネイティブUIの管理においても有用なのです。
広がるReact Nativeの世界
React Nativeは、ブラウザ向けのReactの方法論で書いたアプリケーションを、AndroidとiOSで動かすということに主眼を置いたツールです。しかし、近年ではその枠を超えてReact Nativeを動かそうとする流れが出てきています。それらのうち、大きなものを2つ、紹介しましょう。
Windows向けのMS公式実装
UIライブラリ層とミドルウェア層のインターフェースは疎結合になっています。そのため、理論上はミドルウェア層をサードパーティーのものに差し替えて、任意のプラットフォームのUI管理を抽象化するために、React Nativeを利用できます。ただ、実験的な参考実装はいくつかありますが、本番への導入を実現した事例はほとんどありません。
その中で数少ない事例の1つが、マイクロソフト社がメンテナンスしているオープンソースプロジェクト 「React Native for Windows」です。SkypeとOffice365の開発を効率化するためにReact Nativeが導入されているそうです。当初はVisual C#で書かれていたミドルウェア層を、2019年になってからVisual C++に前面リライトするなど、最近も精力的に開発が行われています。
GitHub - microsoft/react-native-windows: A framework for building native Windows apps with React.
ブラウザ向けの逆輸入
ミドルウェアの差し替えとは、また別のアプローチからReact Nativeを活用している事例もあります。ブラウザのためのReact Native、React Native for Web です。
GitHub - necolas/react-native-web: React Native for Web
「本末転倒」という言葉が思い浮かんだかと思いますが、少しお待ちください。なかなかどうして、これは妥当なライブラリなのです。React Native for Webは、Material-UI等と同じく、ブラウザのUIをきれいに実装するためのUIライブラリです。以下のようにインポートして使います。
import { View, Text, ScrollView } from 'react-native-web';
モジュール名は異なりますが、そこから出てくるコンポーネントの名前は、React Nativeのものと合わせてあります。
React Nativeが持つUIの妥当な粒度
「代表的なコンポーネントとモジュール」の項で解説した通り、React Nativeにはモバイルアプリを開発する上で、必要最低限かつ妥当な粒度のコンポーネントが提供されています。もし、この粒度のコンポーネントが、ブラウザでも使えたらどうでしょうか。 <ScrollView>
で挟むだけで、どんなモバイルブラウザの中でも慣性スクロールが実装できるとしたら、嬉しい人もいるのではないでしょうか。
そんなメリットを享受するために「React Nativeと同じ名前、同じインターフェースを持ち、概ね同じ見た目と同じ挙動を再現する、ブラウザ向けのUIコンポーネント集」であるReact Native for Webを作って、本番に導入したサービスが存在します。それが、皆さんご存知のTwitter for Webです。
Twitter. It's what's happening.
TwitterはモバイルアプリをReact Nativeを使って作っているわけではないでしょう。おそらくは本当にブラウザ向けの開発の体験を良くするためだけに、React Native for Webを導入していると筆者は考えます。React Nativeが持つUIの粒度は、それだけ魅力的であるということです。
React Nativeを軸にした人材戦略
ここまでの解説で、React Nativeの特性については、ざっくりとご理解いただけたかと思います。では、React Nativeを活用することで、サービス開発においてはどのような問題が解決されるでしょうか。
日本のサービス提供者が抱える宿命
現代の日本において、インターネットサービスを提供する事業者の多くが抱えている宿命があります。それは、サービスをAndroidとiOSの両方から利用できるようにしなければならない、ということです。
NTTドコモのモバイル社会研究所が発表している2019年版のケータイ社会白書では、2大モバイルOSを比較した場合のそれぞれのシェアは、Androidが52.6%、iOSが47.4%となっています(資料1-1b「最もよく利用するスマホ・ケータイ(1台目)」より抜粋)。
Androidがやや優勢ですが、大雑把に見れば、日本ではAndroidとiOSのデバイスがだいたい同じ規模といっていいシェアで存在しているといえます。海外では国によってAndroidのほうが圧倒的なシェアを持っているケースもあり、そういった国ではアプリ提供の際にAndroidアプリだけを作ればいいのですが、日本においてはそうではないということです。
例外的に、B2Bのビジネスで「Androidだけでいい」「iOSだけでいい」という契約ができていれば、片方だけに絞ることも可能です。しかし、基本的には両プラットフォームへの対応が求められると思っておいたほうがよいでしょう。
モバイルアプリ開発のエンジニアを増やすのは茨の道
日本のモバイルアプリ市場の特性を考慮すると、できるだけ早く、複数のプラットフォームにアプリを提供するのが、アプリ開発の現場に求められるビジネス上の要求となりそうです。筆者が考える、モバイルアプリ開発の現場の「王道」となり得る理想の方針は、以下の2つです。
- 採用によってAndroidエンジニアとiOSエンジニアを増やす
- 同じメンバーで1プラットフォームずつ開発する
開発のスピードを上げたい場合に行うべきチームビルディングの「王道」といえば、やはりエンジニアの人数を増やすことです。しかし、そんなにうまくいかないことは、皆さん薄々勘付いていることでしょう。筆者の肌感覚では、モバイルアプリのエンジニアは人材市場に潤沢にはいません。人材採用に多くの投資ができ、かつ魅力的な人材を次々登用できている組織ならばいいかもしれませんが、世の中の全ての会社がそうできるとは限りません。採用活動を頼みの綱とするには少し頼りないといえるでしょう。また、残念ながら、組織や事業の状況によっては人を増やすだけではうまく回らないとも考えています。
限られた人数でのモバイルアプリ開発の並列性は頭打ちする
B2BやB2B2B/B2B2Cの事業では、往々にしてステークホルダー同士の関係性が複雑になり、プロジェクト進行の前提条件を開発サイドの都合だけで整えづらくなるケースがあります。そんな状況で発生しうるのが、Android版とiOS版の(ほぼ)同時リリースです。理想的なプロジェクト進行を語るならば、それぞれ十分な人数のAndroidエンジニアとiOSエンジニアでチームを組み、事前にある程度固まった仕様書を用意し、開発中に発生した仕様の齟齬は綿密なコミュニケーションによって解決する、という流れになるでしょうか。
しかし、人材が潤沢ではないとなれば、取れる手段は限られてきます。開発プロジェクトの進行を穏当にするならば、まず選択肢に上るのが「iOS版を先に作ってAndroid版は後から」か「Android版を先に作ってiOS版は後から」のどちらかのパターンでしょう。
まずはどちらかのプラットフォーム向けに開発を行いながら、仕様書へのフィードバックを行い、それがひと段落ついた頃に、もう片方のプラットフォーム向けの開発に着手すれば、開発全体としての完成度を上げやすい傾向にあると筆者は考えます。開発メンバーも同じであればなお良いでしょう。これも王道の1つです。
この王道は、Android SDKとiOS SDKのどちらも扱える、腕力のある器用な人で構成された少数精鋭のチームで実施するとうまく回るはずです。しかし、残念ながらそういった人は、そんなに多くはありません。また、もし運良く両プラットフォームに親しんだ、腕力のある器用な人をそろえることができたとしても、1人の人間はKotlinを書きながらSwiftは書けないのです。「Androidアプリ開発もiOSアプリ開発もできる能力の高い人」がいるからといって、「AndroidアプリとiOSアプリが同時に作れる」とはならないのが、現実世界の世知辛いところです。
開発におけるReact Nativeの得意分野と課題
「Android/iOSのアプリを同時に開発し得る」のは、React Nativeの得意分野です。Android/iOSエンジニアメンバーにJavaScriptやTypeScriptを覚えてもらえれば、同時進行でのアプリ開発も実現可能でしょう。プラットフォーム間で挙動を変えたい部分も出てくるかもしれませんが、それでも共通の部分が多いことは、効率的な開発の助けになるはずです。人材不足についても、React Nativeがブラウザアプリ開発の方法論で開発ができるようにしてくれているため、ブラウザアプリ開発の経験者を採用すれば、選択肢は広がるのではないでしょうか。
React Nativeは、ある状況の事業や組織に一定のメリットをもたらしてくれます。しかしその一方で、注意すべき点が2つあります。
- エラーメッセージから、プラットフォームとJavaScriptのどちらのエラーなのかを判断しなければいけない
- 運用に関する知識は全プラットフォームのものが必要
React Nativeでアプリ開発をしていると、エラーメッセージにAndroid/iOS由来のものと、JavaScript由来のものが混在して出てきます。ほとんどの場合はJavaScriptのエラーを扱うことになるので、ブラウザアプリ開発と同じつもりで進めても基本的に問題ありません。とはいえ、まれにAndroid/iOS由来のエラーメッセージが出た場合に、対処できる人がいなければどうにもならず、ブラウザアプリ開発の経験者しかいない組織でReact Nativeを採用するのは危険です。
また、React Nativeで作ったアプリをリリースするときや、クラッシュレポートの体制を整えていくような、運用に関する作業を行う際には、普通のAndroid/iOSアプリと同じ手間がかかります。もしかすると、Node.jsやnpmについての知識も必要になる分、もう少しだけ大変かもしれません。
腕力のある器用な人を活かす
これらのモバイルアプリ開発事業が抱えがちな課題は、前述した「Android SDKとiOS SDKのどちらも扱える、腕力のある器用な人」に力を奮ってもらうと、器用さがそのまま事業や組織への貢献につながるようになるはずです。React Nativeを扱うチームでは、プラットフォーム横断でトラブルシュートや運用タスクをこなす人が、1人ないし複数人必要です。「ここはJava側に処理を逃して解決しよう」「これはJavaScript側で解決できる範囲だ」といった、それぞれのプラットフォームについて分かっている立場からのアドバイスは、一緒に開発を担当するメンバーの強い助けになるはずです。
チーム作りのイメージとしてはこのようになります。
まとめ
ここまで解説してきたReact Nativeは、クセが強いツールですが、うまくモバイルアプリ開発事業と組織の課題にハマった場合は、強い力を発揮します。Android /iOS両方の開発を行う際の課題は、「王道」の開発手法では対応しづらいものでした。React Nativeは、これらの課題の解決に向けた示唆を与えてくれます。
Android/iOSエンジニアは、JavaScriptやTypeScriptというスキルセットを手に入れるための足掛かりとして、JavaScriptエンジニアはモバイルアプリ開発のスキルセットを手に入れるための入り口として、チャレンジしてみるといいと思います。
著者プロフィール
中川 幸哉 (なかがわ・ゆきや)
監修:WINGSプロジェクト 山田 祥寛
WINGS @yyamada WINGSプロジェクト