🐤

数十万ダウンロードのアプリをExpo + Next.js + Tamaguiでコード共通化しつつリニューアルした話

2024/06/10に公開

昨年12月に、「Cloveフリマ」という鑑定品トレカに特化したフリマサービスをリリースしました。

https://clove.jp/

もともとトラストハブ社では、Cloveというトレカ委託販売のサービスを提供していました。そして昨年、より良いUXを提供するためにフリマアプリとしてサービスリニューアルするというプロジェクトが始動しました。

このリニューアルのタイミングで、技術スタックを一新してプロジェクトを作り直すことになりました。

結果的に大規模なリニューアルになりましたが、本格的な開発開始から実質4ヶ月でリリースすることができました (Android版はまだ未リリースですが、近日中に公開予定です → 追記: 6月25日に公開しました!)。この記事では、短い期間・少ない人数でWeb, iOS, Androidアプリを同時に開発した手法を紹介します。

プロジェクトの背景と技術選定の方針

Cloveは2年前にiOSアプリをリリースしていたものの、社内リソースはWebアプリ開発に注力しており、ネイティブアプリ開発を外部の事業者に頼っている状況でした。今後のサービス開発において、プロダクト開発速度を向上させるためにエンジニア組織を拡大するという方針があり、ネイティブアプリも内製化したいという話になりました。

しかし現時点では社内の開発チームの規模が小さく、各プラットフォーム専任のエンジニアを育成・採用するのは難しい選択だと考えていました。私はiOSアプリをSwiftで開発する業務の経験があったものの、他のメンバーはWeb開発の経験しかなく、また今から採用するにしてもすぐに良い人を必ず採用できるわけではありません。そこで、まずはそんな状況でも内製でネイティブアプリ開発を継続的に行うためにはどのような選択肢があるかを検討し始めました。

トラストハブ社はいくつかのプロダクトをリリースしていますが、そのすべてのフロントエンドとバックエンドがTypeScriptで書かれています。この理由として、

  • 複数のプロダクトをどんどん出す中で、各エンジニアが1つのプロダクトしか触れないという状況にならないようにする
  • フロントエンド・バックエンドのような領域で担当者を切り分けせずに、プロダクトに関わることはなんでもやるというマインド

というのが挙げられます。もちろん複数言語を扱っていても上記は達成可能ですが、言語を統一することでコンテキストスイッチを減らして実装に集中できるというメリットがあり、TypeScriptに統一しています。

今回の新規サービスにおいても上記の考え方をベースにして、TypeScriptベースでネイティブアプリを書けるReact Nativeを中心とした技術スタックを検討し始めました。

サービス全体の技術スタック

今回のサービスはWebだけでなく、ネイティブアプリでもリリースするという要件がありました。

Typescriptに慣れているというチームの特性を踏まえた上で、以下のライブラリを使って開発をしています。

  • フロントエンド・アプリ
    • Expo - iOS, Androidアプリを作るためのReact Nativeベースのフレームワーク
    • Next.js - Webアプリを作るためのReactベースのフレームワーク
    • Tamagui - マルチプラットフォーム対応のスタイル・コンポーネントライブラリ
    • Solito - マルチプラットフォーム対応のルーティングライブラリ
    • Tanstack Query - GraphQLのデータフェッチ・キャッシュライブラリ
  • バックエンドはNestJS, GraphQL, Prismaという構成です。弊社の他のプロダクトもバックエンドは全てこの構成になっています。

ExpoでWebアプリを作る際にNext.jsを使うのは、公式に推奨されている開発フローではありません。しかし、弊社の他のプロダクトでNext.jsを採用しており、チームに知見が溜まっているためWebアプリではNext.jsを採用しました。

Tamaguiの紹介

ExpoとNext.jsで全く別々にアプリを開発することになると、せっかくどちらもReactで開発しているという点が活かせません。そこで、コンポーネントを共通化できるように、Tamaguiというスタイルライブラリを採用しました。

TamaguiはReactを使ってWeb、Android、iOSのUIを作ることができるスタイルフレームワークです。従来のプラットフォーム共通化ライブラリに比べて、プラットフォームネイティブな出力に重点をおいてあり、オプションの最適化コンパイラにより、アプリやサイトのパフォーマンスが向上するという特徴があります。

Webとネイティブアプリを同時に開発する技術スタックは何年も前からありましたが、パフォーマンスが悪かったり、プラットフォーム専用の開発環境で作られたアプリに比べて挙動に違和感があったりするという問題がありました。しかし、Tamaguiでの開発はネイティブに比べても遜色ないパフォーマンスだと感じました。このようなスタックについて、英語圏コミュニティでは「Universal Apps」という名前で再び注目され始めております。

Tamaguiはまだ新しいライブラリですが、実際にTamaguiで作られているアプリとして有名なものに、暗号通貨のウォレット&分散型取引所アプリでトップシェアを誇る「Uniswap」があります。Uniswapはソースコードが公開されており、実装をみることができるのでぜひ見てみてください。

https://github.com/Uniswap/interface

実装してみた感想

ディレクトリ構成

Universal Appsでは、バックエンドを除いたWeb, ネイティブアプリを1つのリポジトリで管理することになるため、以下のようなディレクトリ構成となっています。

`apps`
│ ├ `expo` (Native)
│ ├ `next` (Web)
│ ├ `storybook` (Web Storybook)
│ └ `storybook-rn` (Native Storybook)`packages` (expo, nextから呼び出される共通パッケージ)`ui` (Tamagui をベースしたカスタム UI キット)`app``features` (機能ごとにコンポーネントをまとめたディレクトリ)`locales` (i18n 用の言語対応)`provider` (アプリをwrapするすべてのプロバイダーと、ネイティブまたはWebの一部の処理)`utils` (その他のもの全て)

appsディレクトリの中のexpo, nextの中身は、各プラットフォームごとの設定と、ルーティングのためのファイルが配置されており、共通化されているページコンポーネントを呼び出すだけの実装になっています。

また、この記事では深くは解説しませんが、GraphQLのFragment Colocationという考え方に基づいて、Query, Mutationファイルを各ページやコンポーネントに近い場所に配置しており、UIとAPIリクエストの依存関係がわかりやすくなるように工夫しています。

学習の簡単さ

トラストハブ社は全てのプロダクトでReactを利用しているため、基本的には参加したメンバーがすぐに実装に取り掛かることができました。

また、スタイルフレームワークとしてのTamaguiは、Chakra UIを使ったことがある人なら違和感なく触れていました。

とはいえ、Webアプリとしては問題なく動作したのですが、ネイティブアプリに関してはプラットフォームごとに処理を分岐するのが必要な箇所もあります。プラットフォームの知識がゼロでもネイティブアプリ開発ができるわけではなく、React NativeとExpo固有の情報を度々調べる必要がありました。

Webとネイティブで多くのコードを共通化できた

TamaguiのComponentを利用すればネイティブで動くようになっているため、最終的には90%程度のコードを共通化させることができました。

プラットフォーム固有の対応が必要で、共通化できなかった箇所としては以下のようなものがあります。

  • クッキーなどのストレージ周りの処理
  • 認証周りの処理
  • モーダルのような一部のコンポーネントでは、スマホで使いやすくするために挙動をネイティブ独自のものに分岐
  • ネイティブアプリではScrollViewで引っ張ってスクロールをさせるなど、固有の機能を追加

これらの処理を分岐させたいコンポーネントは、同名のファイルを作成して拡張子を「.native.tsx」のような名前にすることで、特に工夫しなくても処理を分岐させることができました。

また、特にコードを分岐させずにDeeplinkを実装できるという点も魅力的でした。

Expoはビルドや配布が簡単

Expoの提供しているサービスを活用すれば、ネイティブアプリのビルドをクラウド上で実行することができます。難しい設定もなく、簡単にビルド環境を構築できました。

また、EAS Internal Distributionという機能を使えば、社内への配布がとても簡単にできる点も便利でした。TestFlightやGoogle Play Consoleの設定をせずに、すぐにアプリを社内でテストしてもらうことができました。

まとめ

  • Expo, Next.js, Tamaguiを使ったUniversal Apps構成での開発で、Webとネイティブアプリの両方を同時に開発することができ、少ない人数・短い開発時間でアプリをリリースすることができました
  • マルチプラットフォーム開発は過去にはパフォーマンスの問題があり評価が低かったものの、最近のスタックでは改善されてきており、現実的な選択肢になっています。ぜひお試しください!

ここまでお読みいただきましてありがとうございました。最後に、株式会社トラストハブではカード事業だけでなく、toC向けの様々なプロダクトを提供しているのですが、やりたいことに対してエンジニアが足りておりません。toC向けプロダクトを開発したいという方はぜひこちらからお話しさせてください!

TrustHub テックブログ

Discussion