てくすた

ピクスタ株式会社のエンジニア・デザイナーがつづるよもやまテクニカルブログです

SnapmartアプリをReactNative + Expoでリリースしました

こんにちは。ピクスタ開発部長の星直史です。
最近自炊にハマっているのですが、料理が下手でも調味料に投資をすると、一定レベルの質が担保されることを発見しました。現在はヘルシオとホットクックの導入を検討しています。

さて、ピクスタグループにはスナップマート株式会社も属しています。
これまでのSnapmartのスマホアプリはiOSしか存在していなかったのですが、5月7日(火)にAndroid版Snapmartアプリをリリースしました。
Android版SnapmartアプリはReactNative + Expoで実装を行なったのですが、このリリースでWebエンジニアでもスマホアプリ開発が行えると確信しました。
この記事では、SnapmartアプリにReactNativeを導入した背景や、導入のポイント、リリースまでの道について紹介します。

ReactNativeとExpoとは

ReactNativeは、Facebook社が開発しているJavaScriptのフレームワークです。
ReactNativeで実装を行うと、1つのソースでiOSとAndroidアプリを生成することが可能です。
また、画面の描画はWebViewではなく、モバイルUIコンポーネントで描画を行います。

ExpoとはReactNativeによる開発を支援するツールです。
iOSとAndroidでの実行環境やビルドを簡単に行えることや、Expoが提供するAPIによりデバイスの機能(カメラ、位置情報、通知、センサー)へアクセスすることができます。
また、Expoでビルドとデプロイをすることで、面倒な証明書の処理をExpoに任せたり、修正を即時に反映することもできます。

ReactNativeに踏み切った背景

現状

私は2018年12月からSnapmartの開発に携わることになったのですが、当時は下記のような状況でした。

  • Swiftで書かれたiOSアプリは存在するが、実装したエンジニアは退職済
    • アプリの仕様を知る者は現職の非エンジニアのみ
  • エンジニアは2名(自分と学生アルバイト)
  • エンジニアにネイティブアプリ開発の知識がない

このような状況であったため、とてもネイティブアプリの開発を継続的に行える状況ではありませんでした。

あるべき姿

このような状況下でも、継続的に開発を行えるように整備していかなければなりません。*1
そこで、現状の状況はさておき、長期的にどのように開発をしていくべきなのか、あるべき姿を考えました。

Snapmartは、まだまだサービスの規模が小さく、プロダクトが市場に受け入れられているという確証はありません。
そのため、プロダクト・マーケット・フィット(以下PMFと表記)*2を目指して高速に開発を行える状態を、あるべき姿だと定義しました。

また、JOINして早々にスナップマートの代表から「Android版スマホアプリもやっていきたい」と相談を受けていたので、開発チームとしては、Web, iOS, Androidをカバーする必要がありました。
そのため、iOSとAndroidを別々に開発するのではなく、開発スピードと効率を重視したクロスプラットフォーム開発が行える技術が望ましいと考えました。

技術選定

あるべき姿が決まったので、現状とのギャップを具体的に考えていきます。
考慮検討すべき点としては大きく4つあります。

  • ユーザーに良いUXを提供できるか
  • クロスプラットフォーム開発が行えるか
  • 学習コストは低いか
  • 採用難易度は低いか

このポイントを元に、候補となる技術を挙げたのが下記です。

  1. ネイティブ: Swift / Kotlin
  2. クロスプラットフォーム開発: ReactNative / Flutter / Xamarin
  3. Web開発: PWA

そして、考慮検討すべき点と候補となった技術をマッピングし、評価を行なった結果が下記の表です。

ネイティブ(Swift, Kotlin) ReactNative PWA
UX ×
クロスプラットフォーム開発 ×
学習コスト ×
採用難易度 ×

この表だけを見ると、PWAにメリットがあるように見えます。
それぞれ、もう少し詳しく見ていきましょう。

1. ネイティブ: Swift / Kotlin

  • ユーザーに良いUXを提供できるか
    • 他と比べるとこの点は優位
  • クロスプラットフォーム開発
    • Swift / Kotlinで別々に開発しなければならないため、開発スピードが遅くなってしまう
  • 学習コストは低いか
    • 異なる言語をそれぞれ学ぶ必要があるため、学習コストが高い
  • 採用難易度は低いか
    • それぞれ別のエンジニアを採用しなければならない

2. クロスプラットフォーム開発: ReactNative / Flutter / Xamarin

FlutterやXamarinなども検討しましたが、言語がそれぞれDartとC#を使うことになってしまうため、学習コストの点でReactNativeが有力候補になりました。

  • ユーザーに良いUXを提供できるか
    • 要求によってはある程度制約を受けるものの、ネイティブのコンポーネントを使用して開発を行える。
  • クロスプラットフォーム開発
    • 1つのコードでiOSとAndroidの両方を開発できる。
  • 学習コストは低いか
    • WebのフロントエンドでReactを導入しているため、言語に関する学習コストは無い。
  • 採用難易度は低いか
    • Webエンジニアが使用している言語を使うが故に、採用ターゲットもこれまでと同様。

3. Web開発: PWA

  • ユーザーに良いUXを提供できるか
    • Snapmartのユーザー層の多くは20~30代女性。そのユーザー層が「Snapmartアプリ」を知った場合の次の行動はアプリストアでの検索に流れると仮説を立てた。その仮説が真の場合、多くの新規流入を失ってしまう懸念がある。
  • クロスプラットフォーム開発
    • Web上で動作するためプラットフォームを問わない
  • 学習コストは低いか
    • WebのフロントエンドでReactを導入しているため、言語に関する学習コストは無い。
  • 採用難易度は低いか
    • マークアップを行わなければならず、エンジニアがマークアップができない場合にデザイナーと協業する必要がある。そのため、マークアップも行えるデザイナー採用をする必要が出てくる。

上記のような比較検討を行なった結果、ReactNativeで実装することに決断しました。
技術選定は現状と理想のギャップを埋めるための道具選びといった具合です。

ReactNative導入のコツ

制約を受け入れる

ReactNativeは基本的にはReactで実装していくのですが、ネイティブのコードを生成する代わりに二度とReactに戻れないというオプションが存在します。
Expoを使用しているのであればexpo detachコマンドです。*3
これを行うとネイティブでコードを実装しなければならないことや、Expoによる恩恵を受けにくくなります。 そのため、今回の意思決定ではexpo detachを行わず、JavaScriptの世界に閉じて実装をしていく必要があります。

また、開発を行なっていくと、OSSモジュールを使用したいタイミングが出てきます。
しかし、モジュールによっては、expo detachを行うことを前提としたreact-native linkを行わなければ使用できないものも存在します。
そのため、JSのみで作成されたモジュールしか使えないという制約もあります。

上記の制約があるため、細かい挙動が再現できない場合や仕様や要件を満たすことができないことを覚悟しなければなりません。
これについては、開発者だけで意思決定できるものではないので、ビジネスサイドとの期待値の調整が非常に重要です。

チーム全体が「ReactNativeでは実装できません」を許容し、柔軟に仕様や要件を変更しながら落とし所を決めていくことが求められます。

この制約をチーム/組織が受け入れらるかどうかが、ReactNative導入の成功可否を分けるといっても過言ではありません。

Snapmartにおけるリリースまでの戦い

最後に、昨年末の状況(ネイティブアプリ開発がなく、ReactNative開発も初めてで、仕様も知らず、メインエンジニアが1名)から数ヶ月でAndroidアプリをリリースするまでの戦いを紹介します。

仕様がわからず、Swiftのコードも読めない中での実装は、下記の手順に沿って進めていました。

  • APIサーバーのRoutingを知る
  • 元々存在するSwift製iOSアプリのエミュレーターからlocalに立てたAPIサーバーにリクエストできるようにする
  • リクエストの内容を確認する
  • APIの処理を読む
  • レスポンスの内容を確認する
  • エミュレータ上のアプリの動作を確認する
  • ネイティブの処理を読む

基本的にネイティブのコードは自分の力量不足により読むことができないため、APIサーバーへのリクエスト/レスポンスと実際のアプリの動きから仕様を読み解いていきます。
そして、その読み解いた仕様をReactNativeでの実装に落とし込んでいくというやり方です。

幸い、Railsのコードは読むことができるので、バックエンドの処理、DBの値、生成されたJSONをデバッグすることで、ほとんどの仕様を把握することができました。

また、事前にAPIサーバーのRoutingを把握しておくことで、「アプリではこういう画面が用意されているだろう」と推測し、バックエンドの処理やDBの値からViewでの分岐を総当たりで試し、ある程度条件(とそこからくる描画)を網羅することも可能でした。
ネイティブアプリはバックエンドから渡されたJSONを用いて描画するだけであるため、バックエンドのコードを根気強く読み解いていきました。

上記を行なってもわからない時は、諦めてネイティブの処理を読みます。これは本当に気合と根性です。
「同じプログラムなんだから読めないわけはない」と自分に言い聞かせて自らを奮い立たせることがポイントです。
非常に時間がかかる行為であるため、オススメはもちろんできないのですが、手持ちのスキルをうまく使うことで、どうしようもない状況を打破することができました。

ここまで修羅の道を行くような開発はなかなかないと思いますが、参考になれば幸いです。

まとめ

Snapmartにおける、ネイティブアプリの技術選定の背景や、導入のポイントと実装のコツを紹介しました。

  • 何を解決するための技術なのかを考える
  • 選定した技術を使った場合に受ける制約が許容できるかを考える
  • 手持ちのスキルを活かして、実装可能な環境を整える

リリースまで辿りついて振り返ると、ReactNative + Expoが良かったのではなく、制約を受け入れることができるチームのメンバーに恵まれたことが重要なポイントだと感じました。 ReactNativeで実装できないことに対して、要望や仕様変更が許されない環境なのであれば、導入は諦めた方が賢明です。

スナップマートでは、Webもネイティブアプリも両方開発が行いたい欲張りなエンジニアを絶賛募集中です!

*1:燃えますね

*2:顧客の課題を満足させる製品(プロダクト、サービス)を提供し、それが適切な市場に受け入れられている状態のこと。

*3: ReactNativeの場合はyarn run ejectコマンド