adish intelligence

アディッシュ株式会社のエンジニアブログです。

全社合宿を支えた SPA アプリ開発 〜 React + Rails でプログレッシブ Web アプリ構築〜

皆さんはじめまして。2年目新卒エンジニアの坪井(@ufo_ocha)です。普段は先日リリース致しましたhitoboの主にフロント周りを開発しています。

f:id:ufo_ocha:20161108002332j:plain

さて、Gaiaxグループでは年に2度、夏と冬に全社合宿を行っています。冬は事業部毎の発表など真面目なコンテンツがメイン、夏はみんなで集まり仕事を忘れて遊びましょうと交流がメインとなっています。だいぶ時間がたってしまっていますが、今回は9月に実施した夏合宿で運営として参加し、合宿用アプリを作成した話です。少しではありますが、当日の様子はこちらからどうぞ。

子ども・パートナーも参加!ガイアックスグループ夏合宿の軌跡

アプリ要件

今回の要件ですが、ざっくりと

  • 合宿のしおり
  • 参加メンバーのプロフィール閲覧
  • コンテンツのフォトロゲイニング

となっています。これは僕ではないのですが、昨年度の冬合宿でしおりをスマホで閲覧しやすいWebページで提供したところ、見やすくてよいと評判だったので、今年もしおりはWebページで提供しようと決まりました。であれば、コンテンツも全てスマホでできるようなものにすると楽しいのでは?ということで、このような要件に決まりました。

実装するにあたって

そんなこんなで始まったこのプロジェクトですが、技術選定や実装上で考えたことなどを紹介したいと思います。

React + Redux

全てをスマホで見る前提でしたので、なるべく体験が良くなるようにSPA(Single Page Application)にしてプログレッシブウェブっぽくしようと考えました。業務でも個人的な趣味でもReactを使っていたので、ビューライブラリとしてReact。Flux構成としては無難にReduxを使うことにしました。何度か使っていて慣れていたこともありますし、今回は状態管理が複雑になることはなさそうだったので、Reduxはシンプルに作れそうだと考えたからです。

react-toolbox

デザイン面であまり時間をかけたくなかったので、react-toolboxを利用しました。選定にはGaiaxグループ同期の@__kyrieleison__の以下の記事を参考にさせてもらい、助言をもらい、挙句実装を手伝ってもらいました。感謝!!

早く・それなりの UI を実現する React コンポーネントセット 16 選

上の記事でも多数紹介されていますが、数ある中からreact-toolboxに決めた経緯を少し。コンポーネントセットを使うにしても自分でCSSを書かなければならない部分は結構多いですよね。そのような自分で書く部分とコンポーネントセットで必要なスタイルの管理が別になるのが嫌だったので、まず以下のように絞りました。

  1. material-ui + JSXのstyleでCSS in JS
  2. react-toolbox + CSS Module
  3. react-mdlでCSSは完全別管理

この中で、1はJSXのstyleでCSSを書くのは hover など、擬似クラスが使えないため、Reactのstateで状態を管理して自分で変更せねばならず、ツラいしお手軽ではない。CSSを読み込むだけでいい3にしようかと思ったのですが、当時react-mdlは一部パッチ当てないと上手く動かない(今はどうなのかわかりません)という情報を耳に挟んだため、素直にいけそうな2のreact-toolboxを利用することに決めました。

実際やってみると、Webpackの設定などで多少すんなりいかない部分はあったのですが、その他の部分を書き慣れているCSSで書けるということと、当初の考え通り別に管理する必要がなく楽でした。

Rails

以上のSPAをRailsの上にのせることにしました。今回ログイン前提なので、サーバーサイドレンダリングは必要ないですし、なんでも良かったのですが、SPA部分に集中したかったので、レールに乗ることに。最終的には認証・API・管理画面をのせたのですが、見た目を気にしなくてよい管理画面のscaffoldはやはり便利でした。その他のライブラリも充実していますし、やはりぱぱっと作るにはRailsが便利ですね。

ただ、Rails上のJSの管理をどうするかという問題があります。今回はsprocketsをはじめから切ってnpmによせました。このあたりは以前Qiitaにまとめた

Rails + モダンJS環境(Webpack)で新規アプリ作成

と、ほぼ同じ構成ですので、気になる方はご参考ください。

認証

Gaiaxグループでは社内でGoogleアカウントを利用しています。そのため、今回は認証にGoogleログインを利用し、ログインできるアドレスを会社のドメインに制限しました。この部分はSPAにせず、RailsでOmniAuthを使って実装、ログイン部分とAPI, 管理画面以外は全てReactを読み込むビューを返すようにしました。こうやって認証情報をRailsのセッションにもたせてやることで、フロントからAPIを叩く際にもcookie情報を含めてやるだけで認証が確認できます。ログインしてなければエラーを返し、SPA側でログイン画面にリダイレクトさせるような処理にしました。

ログイン前提でのSPAでしかこのやり方は使えないと思いますが、ソーシャルログインというSPAでは煩わしい部分をRailsに任せることができるのが良かったです。また、APIを分離する場合にも、以下のようにレンダリング+認証とすることによって、認証とロジックの部分を分離でき、APIサーバーをシンプルに保つことができます。

f:id:ufo_ocha:20161108002204p:plain

若干のオーバーヘッドは気になりますが、hitoboでは実際にこのような設計で構築しました。hitoboのアーキテクチャの話もまた機会があれば、ブログに書きたいと思います。

プログレッシブ化

「おい!タイトル!?」とか言われそうですが、結論から言うとこれはダメでした。

現状Safariはプログレッシブに必要なキャッシュしてオフラインで使用、Webプッシュ通知をつかさどるService Workerが未対応です。更に合宿の出欠の際にアンケートを取ったところ、約9割がiPhone。先進国の中でも日本はiPhoneのシェアが7割程、IT企業であれば9割というのは納得の数字です。時間のない中で1割のためにServiceWorkerを使って実装するというのはコストに合わないと思ったので見送り。

ならばと思い、 apple-mobile-web-app-capable を設定し、ホームに追加してもらうことで、ナビゲーションバーを表示せずアプリのように使ってもらおうとしました。しかし、これにも罠があって、apple-mobile-web-app-capable を設定し、ホームに追加したボタンから開いた場合、

  • Safariのcookie, local storageは共有されない
  • JSの遷移以外(aタグ等)はSafariで開いてしまう

という仕様があります。今回の場合、Googleログインの部分が普通にaタグでの遷移だったので、ログインしようとするとSafariが立ち上がってしまいます。そこでログインしてもcookieが共通でないため、ホームに追加したボタンから開いた場合にログインできないという状態になりました。これについては工夫することでなんとかできそうでしたが、仮になんとかなったとしてもcookieが共通でないためにGoogleアカウントにもログインしなければなりません。弊社では2段階認証が必須なため、わざわざログインするのも手間。残念ながらこちらも見送ることにしました。

個人的にServiceWorkerはReactなど、フロントを書く機会が増える!と期待しているのですが、現状ではSafariが対応しないことにはどうにもならないですね。今後に期待!!

懺悔

さて、アプリの要件でも少し触れましたが、 「参加メンバー全員のプロフィール閲覧」という機能を実装しました。はじめにログインした際に名前や顔のわかるプロフィール画像などの情報を入力してもらい、それの一覧と詳細を表示するというただそれだけの機能です。参加者も130人ほどだったので、ページングもない簡単なものです。

 

簡単なものなのですが......

 
 
 
 
 

プロフィール画像のリサイズをしておらず、一覧を開くだけで何MBもの画像ファイルを130も読み込むという膨大な通信を発生させてしまいました。たった40x40pxを表示するために・・・

その他にもコンテンツのフォトロゲイニングでスマホで撮った写真のアップロードなどあり、 参加した社員130名のスマホのデータ通信量を2日の合宿で相当吹き飛ばす という悪行を成し遂げました。ちなみに僕自身はテストで何度も確認したこともあって、次の日に通信制限がかかりました。(あと半月残ってるのに)

言い訳をさせてもらうと、もちろんリサイズが必要なことを知らなかったわけではなく、

  • クロッピングを実装してリサイズしてからアップロードする
  • S3のPUTイベントにフックしてLambdaでリサイズを走らせる

のどちらかにしようとは考えていました。のですが、とりあえず、足りない部分作るのが先だと後回しにしているとあれよあれよと時間が過ぎ、わりと直前まで本番環境として用意した環境でWebSocketが上手く繋がらなくてあたふたし、そのままに。。。

いやー、やることはちゃんとやらないとダメですね。社員の皆様本当にすみませんでした!

感想

上記に書いた以外にも、時間なくて細部作り込めておらず悔しい思いしたりとか、合宿間近の2週間はこのプロジェクトと普段の仕事が両方共デスマっぽくなってめっちゃ辛かったとか(実際にこっちは合宿の日という期限があったので本当にデスマでしたがw)いろいろありました。ただ、合宿に参加していただいた社員の方に(データ使用量は吹き飛ばしましたが)楽しんでもらえて嬉しかったですし、足りない部分は多かったにせよやってみたかったことがアプリ制作を通して試せて良かったです。やはりこうやって、作ったものを使ってもらってはじめて学べることは多いなと再認識しました。

まとめ

  • React + Reduxはエコライブラリも充実していて、割りと枯れてきている
  • サクッと作るにはreact-toolboxなどのコンポーネントセットは便利
  • SPAではレンダリング+認証とAPIサーバと分けると楽
  • プログレッシブは現状では実用的ではない
  • Safari頑張って!!!
  • データ通信量&表示速度の観点から画像はリサイズしましょう

アディッシュ技術開発部では10%ルールという、仕事の時間の10%は自由に使っていい時間があります。今後もそういった時間を使って、最新技術を試してみたり、その技術を使って社内ツールなど、実際に使われるものを作ってみたり、ドンドンチャレンジしていこうと思います!!