SendGridのデモアプリをReact+Flux+Web API v3対応した話

今回は、sendgrid-demoboxというデモアプリの紹介ついでに、最近行なった更新内容についてつらつらと書いていこうと思います。
この記事はSendGrid Advent Calendar 2016の8日目の投稿です。

飲み会の帰り道は決まって納豆巻きを購入する佐藤です。二日酔い防止に効くかどうかは知りません。

## きっかけ
このWebアプリケーションは、SendGridのデモ用に[作成・公開](https://github.com/SendGridJP/sendgrid-demobox)しているものです。初期バージョンは[2年前にコミット](https://github.com/SendGridJP/sendgrid-demobox/tree/v2)したものが最後でしたが、あれから時間が経過してコードベースも古くなってしまっていました。Web API v3対応後のSendGrid公式ライブラリもまだまだ未成熟ながらだいぶ動くようになってきたのと、相変わらず変化の激しいJavaScript周りの新しい技術に触れるのにちょうどいい機会だと感じたので、全体的に実装し直そうと思い立ちました。

## 今回の目標
今回、目標とした更新内容は次の通りです。

1. クライアント周りを[React](https://facebook.github.io/react/)+[Flux](http://facebook.github.io/flux/)で構成する
2. [Web API v3のメール送信エンドポイント](https://sendgrid.kke.co.jp/docs/API_Reference/Web_API_v3/Mail/index.html)の全パラメータに対応する
3. [SendGrid公式ライブラリ](https://sendgrid.kke.co.jp/docs/Integrate/libraries.html#Official-libraries)を使ってWeb APIにアクセスする

## sendgrid-demoboxの概要
このアプリには次の機能があります。

1. アプリ起動時に[Web API](https://sendgrid.kke.co.jp/docs/API_Reference/Web_API_v3/index.html)経由でSendGridを自動設定
2. [Web API](https://sendgrid.kke.co.jp/docs/API_Reference/Web_API_v3/Mail/index.html)によるメール送信
3. [Event Webhook](https://sendgrid.kke.co.jp/docs/API_Reference/Webhooks/event.html)のPOSTを受信してWeb Socket経由で画面にリアルタイム表示
4. [Parse Webhook](https://sendgrid.kke.co.jp/docs/API_Reference/Webhooks/parse.html)のPOSTを受信してWeb Socket経由で画面にリアルタイム表示

こんな感じ。
![](https://github.com/SendGridJP/sendgrid-demobox/raw/master/demobox.png)


Reactの雛形を使うと自然とサーバサイドはNode.jsになると思いますが、SinatraとRubyが好きなので敢えてSinatraにしました。

## 画面イメージ
Webアプリ（Sinatra）起動後、サイトのトップページにアクセスすると以下のような画面が表示されます。

- **メールを送る**
画面左側でメール送信用のパラメータを指定して送信ボタンを選択すると、Web API v3経由でメールが送信され、Event Webhookで受け取ったイベントが画面右側にリアルタイムに表示されます。
![](https://github.com/SendGridJP/sendgrid-demobox/raw/master/sendmail.png)

- **メールを受ける**
画面上部に表示されている受信用アドレス（Mailto:〜部分)にメールを送信すると、Parse Webhook機能によりPOSTされた内容が画面にリアルタイム表示されます。
![](https://github.com/SendGridJP/sendgrid-demobox/raw/master/receivemail.png)

## 成果

1. **クライアント周りをReact+Fluxで構成する**
    - 以前のバージョンはｊQueryとerbによる伝統的な実装でしたが、新しいバージョンでは[React](https://facebook.github.io/react/)と[Fluxxor](http://fluxxor.com/)を組み合わせた実装に生まれ変わりました。[ほとんど](https://github.com/SendGridJP/sendgrid-demobox/tree/master/src/client)が[Web API v3のメール送信エンドポイントのパラメータ](https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/index.html)入力ための実装です。このエンドポイントはパラメータ数が非常に多いのですが、[似たような入力部分のコンポーネント化](https://github.com/SendGridJP/sendgrid-demobox/blob/master/src/client/components/email_form.jsx)にReactが威力を発揮してくれました。
    - [Fluxアーキテクチャ](https://facebook.github.io/flux/)により、画面から入力されたデータの保持と送信をAction、Dispatcher、Storeにそれぞれ分離してSinatraにPOSTする、という流れがわりとスッキリいったのではないかと思います。
    - Webhookの受信をトリガとした画面のリアルタイム更新に[Sinatra RocketIO](https://github.com/shokai/sinatra-rocketio)というRubyベースのWeb Socket/Cometライブラリを使用しました。FluxでWeb Socketのイベントをどこに流し込むか問題は[橋本商会さんの記事](http://shokai.org/blog/archives/9929)が非常に参考になりました。
    - 実装を始めた当初は意識していなかったのですが、[Reactのアニメーション](https://facebook.github.io/react/docs/animation.html)は思いのほか書きにくかったです。以前は1行のjQuery呼び出しで済んでいた実装が、[React標準の方法](https://facebook.github.io/react/docs/animation.html)だと「CSSをいじって、CSSTransitionGroupタグで括って...」とかめんどくさくてキレそうになったので、今回は[react-flip-move](https://github.com/joshwcomeau/react-flip-move)というのを使用しました。やりたいことが全部できました。素晴らしい。
    - ただ、クライアントサイドのコード量は前に比べて爆発的に増えました。

2. **Web API v3のメール送信エンドポイントの全パラメータに対応する**
コード量が爆発的に増えたのは[Web API v3のメール送信エンドポイント](https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/index.html)のパラメータ数の多さが主な原因です（Reactにもちょっとだけ原因がありますが）。単一の宛先に１通メールを送るだけだとちょっと複雑に感じますが、宛先毎の本文への文字差し込みや、メールの識別用にヘッダへ特殊なキーを入れたりと様々なユースケースに対応できる柔軟性の高いエンドポイントではないかと思います。

3. **SendGrid公式ライブラリを使ってWeb APIにアクセスする**
SendGridのWeb APIへのアクセスはサーバサイド(Sinatra)から行なっています。アプリ起動時にSendGridの[Event Webhook](https://sendgrid.kke.co.jp/docs/API_Reference/Web_API_v3/Webhooks/event.html)、[Parse Webhook](https://sendgrid.kke.co.jp/docs/API_Reference/Web_API_v3/Webhooks/parse.html)、[Transactional Template](https://sendgrid.kke.co.jp/docs/API_Reference/Web_API_v3/Transactional_Templates/index.html)の各機能をWeb APIで自動的に設定し、メール送信時にはReact側で構築したリクエストボディを一旦サーバサイドを経由してSendGridのWeb APIを叩いています。これら全てに[SendGrid公式ライブラリ](https://github.com/sendgrid/sendgrid-ruby)を使っています。クライアント（ブラウザ）からWeb APIを直接叩いていないのは[CORS](https://github.com/sendgrid/docs/issues/1417)関連の問題のためです。解決には時間がかかりそうな雰囲気。

## 今後の課題
今回のアップデートで課題に感じたことは次の通りです。

- **Reactコンポーネントのテスト**  
結果的に今回はReactコンポーネントのテストを省略しました。理由としては、Node.jsを利用していないので、ブラウザ上でテストすべきだろうと思うのですが、一番王道っぽい[Jest](https://facebook.github.io/jest/)が[ブラウザ上のテストをサポートする気がなさそう](https://github.com/facebook/jest/issues/139)で、まだまだ流動的と感じたためです。現時点ではSinatraは使わずに[Node.js](https://nodejs.org/ja/)+[Express](http://expressjs.com/ja/)あたりにしておくのが長いものに巻かれる意味では色々楽そうです。Jestの[Snapshot Testing](https://facebook.github.io/jest/docs/tutorial-react.html#snapshot-testing)には感動しました。まさに「Painless JavaScript Testing」。是非使ってみたい機能です。
- **JavaScript周り**  
JavaScriptのプロジェクトを新規作成するのは2年ぶりとかいう浦島太郎状態なので、ReactやFluxの他、Babel、ES2015、Jest、Mochaなどいろいろと勉強になりました。また、相変わらずこの辺りは混沌としていている雰囲気も感じ取れました。今回は、ビルドにGruntとWebpackを利用しましたが、調べた限りではもうちょっとトレンディーなツールはありそうです。いろんなツールの位置関係やトレンドを把握しつつゴチャゴチャと組み合わせないといけないのがいつも ~~憂鬱~~ 楽しいです。

## 最後に
はじめはReactとSendGridのWeb API v3の導入が主目的だったのですが、実際作業を始めてみると、ほとんどの時間はJavaScript周りで利用するツールの調査に費やされてしまいました。
React＋Fluxに関して言えば、以前より実装が整理されてメンテナンス性は断然向上していると思います。これは、アプリケーションの規模が大きくなったときにより効果を発揮すると思います。今回は軽いノリでやれましたが、jQuery+erbをReact+Fluxに書き直すのはクライアントサイドを全部捨てる覚悟がいるので、運用中のサービスでやるのはいろいろなものを天秤にかけて判断が必要なところじゃないかと思います。
今回イマイチだった部分は継続して改善していこうと思います。