ComicFestaチームのエンジニアのyuです。先日、ComicFestaのフロントエンドのリニューアルを行いました。 今回は、このリニューアルについて考えたことや結果について、お伝えできればと思います。
問題意識
いままで、ComicFestaのフロントエンドはERB(Ruby on Railsのビューテンプレート)とjQueryで構成されていましたが、ComicFestaの規模が大きくなるにつれ、以下のような問題が発生するようになりました。
- ERBはHTMLの中にRubyのコードを埋め込めるため、ビュー内にロジックが増えてきてしまい、デザイン変更によりシステムのバグが発生しやすくなった。
- jQueryのコードの可読性が悪いため、メンテナンスしづらい。
- RailsのアセットパイプラインでCSSやJavaScriptをビルドしていたが、このビルドプロセスがブラックボックスで、問題が発生しても対応に時間がかかってしまう。
- 開発環境でアセットパイプラインのビルドの遅さが目立つのようになってきたが、原因が特定できないために問題を放置してしまい、作業効率の低下を招いていた。
改善の検討
一年前にComicFestaのシステムリニューアルでサーバーサイドが一新されましたが、フロントエンドはリニューアルしていなかったため、今回フロントエンドのリニューアルを行うことになりました。
フロントエンド環境リニューアルでは、新しく以下の導入を行いました。
Vue.js
ComicFestaでアニメを配信しているCFアニメは、ComicFesta本体とは独立しており、Reactを採用したSPAになっています。 ComicFestaには、非エンジニアのサイト編成チームがあり、そのチームでデザインの変更を担当しています。しかし、非エンジニアにとってReactのJSXを編集するのは難しいため、ちょっとした更新作業でもエンジニアチームに対応依頼を行う必要がありました。そうなると簡単な作業にエンジニアの工数が取られてしまうため、作業効率の面で問題を感じていました。
そこで、ComicFesta本体では、いままで使用していたERBと同じようにテンプレート構文を採用するVue.jsを使用することにしました。テンプレート構文であれば、サイト編成チームの学習コストも低く抑えることができると考えて採用に至りました。
ERB+jQueryからVue.jsに移行して良かった点としては、以下の点が挙げられます。
- JavaScript側でのDOMの操作がコントロールしやすくなったため、JavaScriptだけで完結できるアクション(並び替えや絞り込みなど)をJavaScriptで実装しやすくなった。
- DOM操作のコードが読みやすくなった。
- UIコンポーネントの再利用がしやすくなった。
逆に、悪くなった点については今の所思いつきません。リニューアル前の構成より改善を実感できています!
webpack
フロントエンドのビルド環境をRailsのアセットパイプラインからwebpackに変更することにしました。webpackはCFアニメでの経験があるのとウェブの情報も豊富にあるため、(個人的に)改善やトラブル時の対応をしやすいためです。
最初はRailsのWebpackerの採用を検討しましたが、Webpackerだと設定のブラックボックスが解決できないことと、webpack本体のバージョンとの乖離が激しいため、純粋なwebpackを採用することにしました。
ビルドツール移行にあたっては、まず、移行は段階的に進めるという方針を決めました。一度にすべてを置き換えるのではなく、既存のアセットパイプラインでビルドしているCSS, JavaScriptは、既存のビルド構成を維持したまま、新規ページや移行しやすい箇所から徐々にwebpackのビルドに置き換えていく方針です。
この方針を採用した理由としては、既存のアセットパイプラインの構成の解析が困難であり、同等の環境をwebpackで実現するには時間がかかることが予想されるためです。
また、移行を考慮すると、移行し易さから既存の構成に似た構成を引き継いでしまい、既存の問題(ビルドのブラックボックス)を完全に解決できない可能性があります。しかし、既存の問題を解決することが目的であるため、一から構成し直すことにしました。
Railsのアセットパイプラインからwebpackに移行して良かった点としては、
- 開発環境では、webpack-dev-serverでHot Module Replacementができるようになったため、サイト編成時のデザイン作業を効率化した。
- JavaScriptのビルド設定を改善、調整しやすくなった。
逆に気になる点は、JavaScriptの経験の浅いRailsエンジニアにとっては、より扱いにくくなったということです。この問題については、社内勉強会などを通じてwebpackの構成についてメンバーに共有していこうと考えています。
Babel
ComicFestaで推奨環境にあるブラウザ用にJavaScriptをトランスパイルするのに、Babelを使用することを選択しました。TypeScriptの検討も行いましたが、今回の採用は見送りました。
理由としては、サーバーサイドにRubyを使用しており、ComicFestaの開発メンバーは静的型付けの言語に馴染みがないため、メンバーの学習コストや親しみやすさを考慮して、動的型付けのJavaScriptを使用することにしました。ただ、Rubyにも静的型を導入する動きがあるため、Rubyで静的型に馴染めたらTypescriptを検討しても良さそうです。
Babelのトランスパイルでは、アグレッシブに提案段階の機能を使えるようにすることはせず、標準化が確定された機能のみを使うようにしていく方針です。
Babelを導入して良かった点としては、
- ES6以降の構文を使えるようになり、コードの可読性が向上した。
- JavaScriptコードが推奨環境で動作するかの心配が減ったため、作業効率が上がった。(といっても実機でのテストは必要)
逆に悪くなったこととしては、以下が挙げられます。
- JavaScriptのビルド時間の増加(現状では、コード量が少ないため、ほとんど気にならない)
- ポリフィルを使うためにJavaScriptのサイズの増大
ESLint, stylelint
いままで使用していたERBでは、フォーマッターを使用していない(ERBに使用できるフォーマッターが見つからなかった)ため、開始タグと終了タグが揃っていなかったり、インデントがめちゃくちゃだったりして可読性が悪く、コーディングの効率の悪化を招いていました。そのため、ESLint, stylelintを導入しフォーマットの統一を行えるようにしました。
Prettierの導入も検討しましたが、ESLintのルールとぶつかるところがありルールの調整に時間がかかりそうだったため、一旦無効にしています。この問題が解決できたらPrettierも導入したいと考えています。
フォーマッターを導入して良かった点として、
- コードフォーマットが統一され、可読性が保てるようになった。
- フォーマット調整を自動補正できるため、コーディング中にフォーマットを気にする必要がなくなり、コーディングに集中できるようになった。
フォーマッターを導入したことによるデメリットとしては、linterのルールに違反したコードが見つかった場合に、コード修正のための作業が発生してしまうため、作業効率ダウンしてしまうことが挙げられます。この問題については慣れの問題が大きいため、メンバーが慣れてくれば作業効率も改善できると考えています。
Storybook
コンポーネントのドキュメントの充実化のためにStorybookを導入しました。
Storybookを導入したことで、Storybook上でコンポーネントごとにデザインの確認が行えます。これが便利で、テキストエディタでファイル編集するとすぐにStorybook上に反映されるため、確認のためにプロダクトを動作させる必要がなくなりました。
以前は、バーガーメニューなど、いくつかの操作をしないと表示されない箇所でビュー変更を行う場合、
- テキストエディタで編集
- Webブラウザに切り替えて、ページをリロード
- リロードの待ち時間が発生。。。
- バーガーメニューを開いて、やっと変更内容を確認できる
といった状態でしたが、Storybookがあることで、
- テキストエディタで編集
- WebブラウザでStorybookを表示するだけで変更内容が確認できる(HMRにより編集内容が自動で反映されるため、ページリロードが不要)
になったため、劇的に作業効率が上がったと感じています。
逆にStorybook導入のデメリットとしては、以下の点があります。
- 開発環境のマシンリソースの圧迫
- ストーリーの作成が必要な分、多少、作業工数が増えてしまった。
リニューアル後の結果
がっつりとVue.js移行を行なったマイページの本棚機能を例に挙げると、ERB+jQueryをVue.jsに置き換えたことで、高速化とサーバー負荷削減を実現できました。
高速化については、改善前の本棚内の検索、並び替え、設定変更などの操作は、すべてサーバーサイドで再レンダリングを行なっており、毎回ネットワーク通信が発生して、ボタンなどの操作をする度にネットワーク待ち時間が発生していました。
しかし、これらの操作は、データ再取得が必要ない操作であるためVue.jsで操作を完結させるように変更したことで、ネットワークの待ち時間がなくなり、操作が瞬時に反映されるようになり、待ち時間なくシームレスな操作ができるようになりました。
サーバー負荷削減については、いままでは非効率なビューになっていたこともあり、サーバーの処理時間がピークタイムで平均1,700msもかかっていました。
これをVue.js化とサーバーサイドのリファクタリングを行なったことで、ピーク時でも、SSRに150ms, APIのデータ取得に250msに改善することができました。
また、改善前では、ピーク時にパフォーマンスが悪化していましたが、改善後はピーク時でもパフォーマンス悪化が見られなくなりました。
この改善の一番の要因は、ERBのビューでデータ取得してしまっており、N+1問題が大量に発生していた問題を改善できたことですが、Vue.jsは構成上、初期レンダリング時のみデータ取得をすることになるため、テンプレートのビューの繰り返し処理の中で、データ取得してしまうような問題(ERBでは、手軽にビューにRubyのコードを埋め込める分、ビューからデータ取得をしてしまうコードを書いてしまうことがある)が入ることはなくなっため、ERBよりもN+1問題が入り込みにくくなったと思います。
今後の展望
ComicFestaのVue.jsはまだ本棚と一部のコーナーでのみ使用している状態です。今後は、新機能のビューは基本的にVue.jsで作成し、既存ページについては、できるところから徐々にビューのリファクタリングも含めて、Vue.js化を進めていきたいと思っています。
そして、最終的には、ブラックボックス化しているアセットパイプラインを廃止して、フロントエンドのビルド環境を完全にwebpackに移行したいと思います。
まとめ
一年前にサーバーサイドのリニューアルを行い、今回、フロントエンドのリニューアルを行いました。ComicFestaの拡大に合わせて、環境を整えてこれたため、今後もさらに使いやすく、便利な機能を提供していければと思います。
最後に
弊社ではVue.jsに関心のある方はもちろん、新しい技術に積極的にチャレンジしたい勇猛果敢なエンジニアを募集しています。
また、不定期でもくもく会も開催しているので、ぜひご参加してみてください!