リクルートテクノロジーズのフロントエンド開発
古川 陽介
こんにちは、リクルートテクノロジーズの古川です。
リクルートグループのエンジニアでアドベントカレンダーをやることになりました!
ということで、このエントリーは Recruit Engineers Advent Calendar の 1日目の記事です。
Recruit Engineers Advent Calendar はこちら
リクルートテクノロジーズのフロントエンド開発
リクルートテクノロジーズではいくつもの並行するタスクが走っていて、プラットフォーム基盤と呼ばれる基盤技術開発とインフラソリューションと呼ばれるインフラ開発、後はアプリケーション開発支援などのタスクが存在します。 アプリケーション開発支援の中でもウェブフロントエンド開発は目下のところ重要タスクとされており、色んなやり方をトライアルしています。 基本的には、 React Redux Node.js という組み合わせでフロントエンド開発をしています。
主には以前 ubb.jp というイベントで発表したこの資料にこれから記載する内容でやっていますが、諸々補足します。
リッチなウェブアプリケーションを作るための 7つの原則
以前に書いた下記のような原則をなるべく守るようにしています。
特に気をつけているポイントは以下の点です。
- サーバーサイドでもクライアントサイドでもレンダリングをする ( Server Side Rendering)
- ユーザーの入力に迅速に反応する ( Optimistic UI )
- history を壊さない、特にブラウザバック・フォワードをした時のスクロール位置に注意する (History API / Scroll Middleware)
それぞれ少しずつ補足します。
サーバーサイドでもクライアントサイドでもレンダリングする
所謂サーバーサイドレンダリングと呼ばれるレンダリングを行っています。 構成としては下記のような形です。
Backend に Micro Services 化された API 群があり、 ブラウザのレンダリングをサポートするための層として Backend For Frontend を設置し、そこでサーバーサイドレンダリングなり、 API のアグリゲーションなり、認証なり、ファイルアップロードなり、 WebSocket なりのマルチタレント役を引き受けてやっています。
サーバーサイドレンダリングをするのは主に初期ロードの高速化のためです。実アプリケーションの中では react-router の機能と react の renderToString を使ってレンダリングをしています。ただし、そのままだとアプリケーションによっては性能の問題を引き起こす可能性があるので、チューニングもしています。
主なチューニングの内容はこちらの資料が詳しいです。
ユーザーの入力に迅速に反応する
Optimistic UI とも言われますが、入力した内容に対して迅速にリアクションを返して何かが起きてる事をちゃんと伝えるようにしています。
Optimistic UI 出典元:Stop Getting In My Way! — Non-blocking UX—Sophie Paxton—Medium
これを実現するにはボタンを押した、フォームから検索した等の状態が変わったタイミングでインジケータなり、プログレスバーなりの情報を出して上げる必要があります。今のところ、このリクエストの元となるイベントが発火してから完了するまでを簡単に実現するためには redux-effects-steps というミドルウェアライブラリを活用しています。
これ自身はそこまで多機能ではなく、 redux-effects と呼ばれるミドルウェアライブラリを使ってアクションをまとめ上げる機能を提供するものです、これを使ってリクエストの開始、完了、失敗を管理しつつ、開始が始まったらインジケータを表示し、完了したらインジケータを無くすといった処理をシンプルに書けるようにしています。
history を壊さない、特にブラウザバック・フォワードをした時のスクロール位置に注意する (History API / Scroll Middleware)
Single Page Application だと、 ブラウザの戻る・進むをした時にうまく戻れなかったり状態が引き継げないことがあったりします。これ自身はアプリの作りが悪かったりもするのですが、気をつけないと簡単に起こせてしまうので、注意が必要です。
戻るボタンを押した時に正しく初期のHTMLがロードされていない例(Githubでコメントを追加て、別なページに行った後、戻ってくるとコメントが消えている例)。
出典元: http://yosuke-furukawa.hatenablog.com/entry/2014/11/14/141415
リクルートテクノロジーズでは、 react-router の history 機能を使いつつ、自分たちでロードのタイミングを調整するために redux-async-loader というライブラリを使っています。
redux-async-loader
これ自身は、割と多機能なライブラリで、 react-router から Component が mount された時にアクションを実行させてデータをロードする機能やサーバサイドレンダリングでもアクションを実行する機能などが存在しますが、 react-router-scroll
と組み合わせると、戻る・進むを行った時のスクロール制御の機能も提供しています。
1 2 3 4 5 6 |
import useScroll from 'react-router-scroll'; const RenderWithMiddleware = applyRouterMiddleware( useAsyncLoader(), useScroll() ); |
API仕様 ( Consumer Driven Contract )
API 仕様を定義するのに Consumer Driven Contract を実践しています。 Consumer Driven Contract とは、従来バックエンド (Provider)が決めていた API の仕様をフロントエンド(Consumer)が主導して要求を書く(Contract)ことで API 仕様を決めていくというスタイルの仕様策定方法です。 pact とかのツールが有名ですが、リクルートテクノロジーズの僕の開発では、 agreed というツールで行っています。
agreed は Mock Server 兼、テストクライアントになっており、 agreed を使って Mock となる振る舞いを決めてサービスを作りつつ、バックエンドはその Mock の内容を確認しながら実装していきます。 最初にクライアント側での要求を書きます。 agreed ではこれを contract と呼んでます。
このファイルは JSON5 や js などで書くことが可能で、 request の形と response の形を決めて、その内容を記述するスタイルです。
次に agreed server を起動させてクライアント側を先行して実装していきます。実装する過程で API の詳細が決まってくると思うので、それを基に agreed の contract ファイルを拡充させていきます。
最後にagreedをclientとして起動させてバックエンドがその要求(Contract)を満たしているのかを確認することができます。 これを使うとバックエンドの統合テストっぽくも振る舞えるので、少なくともクライアントの要求を満たしているかどうかは確認できます。
ただし、これを使ったからといって、 『API 仕様定義のための交渉事が完全になくなる』というわけではありません。あくまで API 仕様を定義するのはバックエンドとフロントエンドの両方です。このフロントエンド側とバックエンド側の議論の叩き台にはなるし、交渉をスムーズにする手助けにはなります。
他に諸々
上記のような技術を使って実際のアプリケーションを構築している真っ最中です。 1つは先日リリースされた Booking Table ですが、 Node 学園祭でも発表があったとおり、 かなりチューニングに気を使って設計されています。 またこれからもリリースされるものはあります。 まだまだフロントエンド開発のためのツールや方法論は足りていないので、絶賛作りながら足りないツールやハックをノウハウとして貯めている所です。アップデートがあったら2017年版をいつか公開します。
※こちらの記事は、はてなブログから転載しています