minne事業部でフロントエンドエンジニアをやっているやまちゃん(@yukishinonome_)です。今回は、Webブラウザ版minneのフロントエンドアーキテクチャと検索ページの刷新を行って無事リリースできたので、実際にチームで刷新してみてわかったことを書きます。
やったこと
フロントエンドアーキテクチャの刷新
この記事では「アーキテクチャの刷新」という言葉を、「アプリケーションの構成や構成要素を新しくすること」という意味で使います。
従来のWebブラウザ版minneはフロントエンドもサーバーサイドもRuby on Railsでモノリシックになっていました。刷新では、フロントエンドをRuby on Railsから分離してNext.jsアプリケーションとして再構築しています。刷新は1ページずつ進めていて最初に刷新したのが作品検索ページです。UIデザインも大きく刷新しました。
こちらのスライドにフロントエンドアーキテクチャの刷新概要をまとめてます。
刷新による効果
Webサイト評価の計測スコア改善
以下の画像を見ていただくとスコアが大幅に改善しているのがわかります。実は各スコアを向上させるために特別高度な実装をしたつもりがないので、Next.jsで実装して基本的なこと(画像にalt属性とサイズを必ず指定するなど)をしっかりやればこれだけの改善につながる可能性があるということです。
PC表示におけるLighthouseの結果 - 刷新前 -
PC表示におけるLighthouseの結果 - 刷新後 -
スマホ表示におけるLighthouseの結果 - 刷新前 -
スマホ表示におけるLighthouseの結果 - 刷新後 -
良好URL数の増加
Google Search Consoleのエクスペリエンスの項目において良好判定のURL数が急激に増えました。これはコアウェブバイタルの数値が良好の基準を満たしたURLが増加したということです。検索ページは検索条件によって動的にURLが生成させるのでURLの数も多いわけです。
開発効率の向上
TypeScript
アプリケーションの構成要素で影響の大きいものの1つがプログラミング言語です。動的型付け言語であるRubyから静的型付け言語であるTypeScriptに変わったことで型チェックの恩恵が得られるようになりました。コンパイル時に型チェックを行うことによってデータの型が正しくない場合にエラーを出してくれるのでアプリケーションの不具合発生を減らすことができます。
DRYな実装
元のRailsアプリケーションでは、MVCにおけるViewのディレクトリにページ単位でディレクトリが存在し、その中でさらに部分テンプレートのファイルがあるディレクトリ構成になっており、コードを再利用するために異なるページの部分テンプレートをレンダーしている実装箇所があるなど見通しが悪い状況が発生している箇所がありました。新しいフロントエンドアーキテクチャはページ単位ではなく、Atomic Designの手法を用いてコンポーネント単位でファイル分割しているのでDRYな実装が実現しやすいです。
コンポーネント単位の表示をStorybookで確認できる
今まで、あるページのある部分の表示をしている箇所を探そうとしたら、ファイル名から推測したり、コードを直接読んで該当の実装箇所を探す必要がありましたが、Storybookを導入したことでコンポーネント単位で実装内容を視覚的に確認することが可能です。ログイン状態・未ログイン状態など異なる状態の表示も表現できるので、表示を網羅的に確認しやすいです。また、コンポーネントがWebアクセシビリティに準拠しているか確認できるアドオンを追加するなど、コンポーネント単位で作業や確認を行う際にも便利です。さらに、作業ブランチがmasterブランチにマージされたタイミングで自動的にGitHub Pagesを用いてデプロイするようにしており、開発環境を構築しなくてもデプロイ先のリンクにアクセスするだけで最新のStorybookを確認することが可能になっています。これによって、開発環境のアプリケーションを起動せずともコンポーネント単位で見た目の確認をしながら開発関連のコミュニケーションを取ることが可能です。
Next.jsアプリケーションとモックサーバーのみ起動して開発できる
データベースや認証サーバーなどを必要としないため、フロントエンド開発のための開発環境構築は簡単であり、動作確認のために起動すべきアプリケーションも最小限で済みます。
得意な技術領域に集中して開発ができる
サーバーサイドを意識せずに開発可能なので、フロントエンドを得意とするエンジニアがフロントエンドに集中して開発することが可能です。逆に、サーバーサイドを得意とするエンジニアもサーバーサイドに集中して開発することが可能です。
苦労したこと
アーキテクチャの刷新を施策として進める中で苦労したことについて書きます。アーキテクチャの刷新は開発コストが大きく、調査が必要なものが多いです。事業部の積極的な協力がないとなかなか進みません。調査が不足していればデグレードやスケジュールの遅れにつながります。
想定されるコストや得られるメリットの整理
- どんなことをやって、どんな効果が期待できるのか
- 大きな開発コストをかけるだけのメリットがあるのか
技術選定
- ベストプラクティスはないので、自分たちにとって最適なものを見極める
- React or Vue.js, Next.js or Gatsby …
アーキテクチャの設計
- コンポーネントの分割どうするか
- テストファイルやストーリーファイルはどこに置くか
- state管理どうするか
- Reduxを用いてstateを一括管理するか
職種間の連携
- プロダクトオーナー、デザイナー、フロントエンドエンジニア、サーバーサイドエンジニア
- 開発環境(アーキテクチャや使用する技術)が変わるので、開発を進める流れや連携も変わる
Ruby on Railsから分離するため、データを渡すためのAPI開発
- フロントエンド刷新と並行してAPIの刷新(GraphQL化)も進めているので、検索ページで必要なデータを取得するためのAPIはほぼ全て新規実装
初回リリースの要件決め
- リリースしてユーザーに届かないと価値が0なので、質と速さを意識してチームで合意を取っておく必要がある
- 従来の仕様をどこまで再現するか
- UIはどこまで刷新するか
実装上の課題
実装を進める中で気づいた、こうしておけば良かったなと思うことや現時点ではベストな判断だったかわからないものについて紹介します。これからフロントエンドアーキテクチャの刷新を行う人は実装を始める前に一度以下の内容について検討してみてください。そして、より良い方法を知っている人はぜひ教えていただければ嬉しいです。
コンポーネントの分割
有名な手法であるAtomic Designを採用しつつ、5つの粒度で分割すると粒度が細かくてどちらに分類するか悩むことが多そうだったので、atoms, organisms, pagesの3つの粒度で分けることにしました。しかし、それでもどちらに分類するか悩むということが発生しています。なぜなら、コンポーネントの規模感や汎用性などは分類する人の感覚によってぶれてくるからです。しかし、他の分類方法を考えても絶対により良くなるという手法はないですし、ディレクトリの構成を直す手間もあります。現状としては、分類に悩むことはあるものの致命的に使いにくいということはないので分類はそのままにしています。
TypeScriptのstrictオプション
これは最初からtrueにしておけば良かったなと後悔してます。この設定が頭から抜けていてfalseになっていました。余計な不具合を防ぐためにも型チェックは厳格な方が良いです。後からtrueに変更したのですが、大量のエラーが発生したので@ts-ignore
で一時的に対処しつつ、新規に実装を行う時に都度自分が触るコードの周辺にある@ts-ignore
を減らしてます。
GraphQL Code Generatorで自動生成された型の扱い
コンポーネントにPropsでGraphQL APIから取得したデータを渡す場合、コンポーネントのPropsの型定義としてGraphQL Code Generatorで生成した型をimportして使うべきか、コンポーネントのPropsの型は全て自分で定義すべきかというところで悩みました。GraphQL Code Generatorで生成した型で特定のデータのみPropsで渡したい場合はPropsの型定義でPickを重ねるなど定義が複雑になってしまうことがあります。そこで、1つのクエリで一括取得したデータをそのままPropsとして渡す場合はなるべく自動生成した型をimportして、特定のデータだけ使いたい場合は自分で定義するというように状況に応じて使い分けるようにしています。また、GraphQL Code Generatorにgraphql-letを組み合わせて使うことでHot Module Replacementを使えてより便利に開発できるようにしています。
Jestのスナップショットテスト
このテストはとにかく実装が楽です。コンポーネントが出力するものを考えずに実装できるので短時間での実装が可能であり、今回の刷新施策のように大規模な施策では少しでも早くユーザーに価値を届けたいため速さ優先の開発になっていました。そのため、全てのコンポーネントのテストをスナップショットテストで済ませるという実装をしていました。スナップショットテストを実装することで、コンポーネントを編集するとテストが落ちるようになるので、編集による意図していない影響に気づきやすくなります。
しかし、今ではスナップショットテストを多用しない方が良かったなと思っています。スナップショットテストは簡単に壊れます。コードを編集するたびに差分が発生するため、毎回スナップショットを更新するという作業が発生します。これを頻繁に行っていると差分を確認せずにルーチンとしてスナップショットを更新するようになるためテストの意味がなくなってきます。また、コンポーネントに時間を取得するようなロジックがあると、時間経過によってテストが壊れます。したがって、新規実装する際には意図したテキストが出力されているか、意図した数だけ特定の要素が出力されているか、といったテストを書くようにしています。(スナップショットテストを使うこと自体が良くないというわけではなく、乱用は避けましょうという学びです。)
まとめ
実際にやってみて初めて気づくことも多いなと実感しています。刷新施策は規模が大きくなるので苦労や課題も多いですが、フロントエンド刷新はやって良かったなと思えることの方が圧倒的に多かったです。それに、フロントエンドエンジニアとしてはTypeScript on Next.jsというモダンな環境で開発できること自体が楽しいです!今後も引き続き他のページを刷新していきます!