はじめに
こんにちは。WEAR部フロントエンドブロックの藤井です。WEARでは現在、Webサイトのリプレイスを進めています。本記事では、リプレイスに至った背景や課題と、課題解決のために行ったリプレイスのアーキテクチャ選定についてご紹介します。
なぜリプレイスするのか
WEARはサービスローンチしてから約10年が経ちます。これまでローンチ当時の技術スタックのまま開発を続け、サービスを成長させてきました。今後もより継続的にスピード感を持ってユーザーへ価値を届けていくにあたってさまざまな課題があったため、新たな技術スタックでリプレイスを開始することにしました。
リプレイス前の環境
リプレイス前の環境はオンプレミスの環境にロードバランサー、Windowsサーバー(IIS)があり、そこでVBScriptが動いています。VBScriptでテンプレートHTMLにデータを流し込み、ブラウザに表示する仕組みで動いています。フロントエンド部分はjQueryを使用し、インタラクションの実装、Ajaxで取得したデータを元に後読みでコンテンツの描画などを行っていました。
課題
上記の環境で長らく開発を進めていましたが、サービスの成長や機能の増加に伴って以下のように生産性、保守性に関する課題がでてきました。
- レガシー技術を使っており、使用できるライブラリが少ない
- CI/CDの環境が整備されていないため、非効率な部分がある
- 約10年間の技術的負債による実装や仕様の把握コストが高い
- jQueryやVBScriptのエンジニアの採用が難しい
今後、WEARのサービス改善をスピード感を持って実行していくためにはこれらの課題を改善する必要がありました。このような背景からリプレイスを始めることにしました。
どのようにリプレイスを進めたか
リプレイスを進めるにあたって課題を解決するだけではなく、リプレイスで実現すべきミッションについて改めて整理しました。そして、そのミッションを元にアーキテクチャを選定しました。
リプレイスのミッション
リプレイスのミッションを考えるときに軸となったのは、WEARのプロダクトとしてのミッションでした。WEARはミッションとして、”ファッションデータを集めて、人々のファッションの悩みを解決する”を掲げています。より多くの人にコーディネート投稿をしていだき、より多くの人にファッションデータを届けることで、このミッションを実行しようとしています。
このミッションの元、WEARのWebサイトで何をすべきかをチームで話し合い、以下の方針を決めました。
【MUST】
- 開発生産性の向上
- 最低限必要な機能の選択
- 現在の利用状況を考慮した取捨選択
【WANT】
- SEO改善
- パフォーマンス
- UX
MUSTの要件としてはまず、課題であった開発生産性を向上させることを挙げました。 次に、できるだけ早くユーザーに快適なリプレイス環境を利用してもらうために必要な機能を取捨選択することを決めました。機能を取捨選択する際にはユーザーの利用状況を確認し、数値を元に判断しています。
WANTの要件にはSEO改善を目的として、パフォーマンス、UXの向上を挙げました。 WEARのWebサイトはSEO流入で閲覧してくださるユーザーがとても多いです。そのためWEARのMissionのうち、”より多くの人にファッションデータを届ける”ということを重視してSEOへの注力をミッションに決めました。
アーキテクチャ選定の軸
リプレイスのミッションに沿って、開発生産性の向上と、SEO改善目的でパフォーマンスを重視した技術選定をしました。
リプレイス後のアーキテクチャ
フレームワーク
フレームワークは当初、Preact+Fastifyで検証を進めていたのですが、最近になってNext.jsを選択することにしました。それぞれの選定理由や選定までの経緯を説明します。
Preact+Fastify
まず初めに選定したアーキテクチャは以下のような構成で、Preact+Fastifyを使用していました。
SEO改善目的でパフォーマンスを重視した選定になっています。FastifyはNode.jsフレームワークとしてよく使われるExpressよりも高いパフォーマンスであることから採用しました。Preactはファイルサイズが大きいReactDOMを使用せず軽量化されているため採用しました。
処理の流れとしてはまず、Build時のSSG(Static Site Generator)やサーバー上でのSSR(Server Side Rendering)で生成したHTMLをブラウザに返します。基本的にSSGやSSRではSEOに重要な情報について扱います。そして、SEOに必要のない部分やログインユーザーの情報に関わる部分はCSR(Client Side Rendering)用のJavaScriptを生成し、ブラウザ側でレンダリングするようにしています。まずこちらのアーキテクチャで簡単なページから検証とリプレイスを進めることにしました。
パフォーマンス目的でこのようにCSR部分を分離した構成にしていたのですが、リプレイスを進めたところ以下のような考慮点がでてきました。
- SSRで取得したデータをCSRで使用したいときの受け渡しにPropsが使えないため、自前で実装する必要がある
- SSRで描画した要素に対してインタラクションをつけたい場合、イベントリスナで実装しなければならずDOM操作をベタ書きする必要がある
- CSRで描画する部分の高さ確保の考慮が必要である
- SSR、SSGする際にCSR部分の高さを考慮する必要があり、高さを二重で管理するなど煩雑になってしまう部分があった
上記を踏まえ、WEARのWebサイトにおいてこのアーキテクチャで進めるべきか考えたところ、以下の課題がありました。
- WEARのWebサイトではブラウザ上のインタラクションが多いため、SSRした箇所にイベントリスナで実装しなければならない箇所も多く、開発効率の悪い部分がある
- SEO重視のためにシンプルな作りにはしたいが、それでも既存のブラウザ上のインタラクション部分など削除できない機能・実装が多い
Next.js
先述したような課題がわかったため、アーキテクチャを見直して以下のようにNext.jsを使用することにしました。
Next.jsを選定した理由は以下のメリットがあったためです。
- 先述のPreact+Fastifyの構成において自前で実装しようとしていた部分をフレームワークに頼れる
- Next.jsがWebフロントエンドのスタンダードになりつつあり、ドキュメントや採用事例が多く、コミュニティも活発である
- ZOZOTOWNもNext.jsでリプレイスを進めているので互いにナレッジシェアできる
もともと、Preactのファイルサイズが軽量であることの恩恵としてCore Web Vitalsの指標にあるFirst Input Delayの短縮を見込んでいました。しかし、上記に挙げたようなメリットや開発効率を優先させてNext.jsへの移行を決めました。
エッジサーバー
リプレイスするにあたってエッジサーバーとしてFastlyを導入しました。一番の目的はパスベースルーティングをさせるためです。
パスベースルーティング
WEARは日々新しい機能追加や機能改善をしているサービスなので、1回で全機能を切り替えるのは難しいという背景ありました。そのため、以下のようにFastlyを用いて、パスベースでリプレイス前の環境とリプレイス環境に割り振ってルーティングし、段階的にリプレイスを進めています。
Fastlyを選定した理由や詳しい活用方法については以下の記事で紹介されているので、併せてご覧ください。
CDNキャッシュ
Fastlyのもう1つの活用方法として、CDNキャッシュを利用して素早くページを表示することによるユーザー体験の向上とSEO改善を検討しています。安全にキャッシュを利用できるようにログインしているユーザー情報に関わらない部分のみをビルド時やサーバー側で生成します。そして、ユーザー依存情報はuseEffectなどのフックを利用してCSRで描画されるようなコンポーネント設計にしています。
その他ライブラリ・SaaS
Mock Service Worker
WEARではWebのリプレイスと並行してバックエンドチームにWebリプレイスで必要なAPIをリプレイスを進めてもらっています。リプレイス前の環境ではAPIの開発を待つか、モックAPIを作成してもらってAPIデータ取得部分の実装をしていました。そのため、フロントエンドとバックエンドを同時並行で開発を進めることがなかなか難しい環境でした。しかし、Mock Service Workerを使うことによって、Swagger定義があればAPIの開発を待たずにAPIデータ取得部分の実装を進められるようになりました。
Tailwind CSS
スタイリングについては以下のような理由でTailwind CSSを選定しました。
- CSS設計やクラス名を考える時間の削減
- クラス名の名付けを考える時間を削減できる
- JSX内で記述できるので、別でCSSを管理する必要がなく書きやすい
- レスポンシブ対応のコスト削減
- 将来的にレスポンシブ化するにあたって、レスポンシブユーティリティが用意されているフレームワークを利用したかった
- CSSのバンドルサイズ縮小
- purgeオプションを使用することで未使用のスタイルが削除され、最終的なビルドサイズを最適化される
- UIライブラリより自由度が高い
- Chakra UI等のUIライブラリも検討しましたが、既存のWEARのデザインやUIを実現するために比較的、自由度の高いTailwind CSSを選択
StoryBook
UIのカタログ化のために導入しました。リプレイス前は共通UIがあることに気づかず個別に実装してしまうということなどもあったのですが、カタログ化することでこの問題が解消されました。また、初めは個別に実装していた箇所についてもカタログ化していることで共通性に気づいて後から共通コンポーネントに移すということもあり、コンポーネント整理に役立っています。
Chromatic
Chromatic、Mock Service Worker、Storybookを連携させてビジュアルリグレッションテストを行なっています。スタイルの差分を検知してくれるようになったので、今まで目視で細かくチェックしていた時間を削減でき、開発やレビュー時間の短縮につながりました。
Chromatic、Mock Service Worker、Storybookを使った取り組みについてはFAANS部の田中が以下の記事で紹介しているので、併せてご覧ください。
まとめ
WEARではプロダクトのミッションやサービス特性を元にアーキテクチャ選定をしました。アーキテクチャ選定の際にはいくつかの観点でメリット・デメリットを挙げて検討しましたが、特に重きをおいたのは以下の3点です。どれか1つに偏ることなく、サービスとして欠かせない点や許容できる範囲を見極めることが大切だと思います。
- 開発効率
- 必要な機能を実装しやすい技術選定になっているか
- SEO観点でのパフォーマンス
- パフォーマンスがでる技術選定になっているか
- 既存の仕様の実現可能性
- 既存の仕様、要件を満たせる技術選定となっているか
さいごに
ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。