比較サイトの検索改善 -SPAからSSRに変更-

aumoでは、比較サイトと呼ばれるユーザの口コミを活用したおでかけCGMを展開しています。当該サイトの速度改善、SEOの向上を目的として、SSRを導入したときの話をします。

こんにちは、サーバーサイドエンジニアの天野です。aumoでは主に比較サイト、aRMの開発を担当しています。

コロナ禍の影響(たぶん関係ない)もあり、20年ぶりに遊戯王にハマりました。やはり旧レリーフは美しいですね。年末にはデュエルディスクが届くので、装着して街に繰り出したいなと思っています。

比較サイトとは

おでかけ関連の施設情報を展開しているサービスで、アプリから投稿した写真(口コミ)やWeb記事から入稿した記事を元にコンテンツをリッチにし、SEOの向上を狙っています。

こちらで「Web施設」と紹介されているサービスになります。

フロントエンドにNuxt.jsを採用、バックエンドはRuby on Railsを用いてGraphQLをAPIとして参照する構成になっています。

当時の課題

比較サイトは当初、SEOを考慮した設計になっておらず、UXを優先したSPAという方法を採用していました。

SPA(Single Page Application)

SPAとは、単一のページでコンテンツの切り替えを行うアプリケーションのことで、ブラウザによるページ遷移を行わないため、UXを大きく向上させることができます。切り替えは高速になりますが、JSのコード量が増えるため初回の読み込みに時間がかかるといった特徴があります。

  • 2回目以降は差分のデータのみ要求するため早い
  • 初回の読み込みが遅い

比較サイトの機能追加、コンテンツの増加に伴い、SPAの弱点である初回読み込みの遅延が顕著に現れるようになりました。特に「ジャンル(ホテルグルメレジャー・観光チラシ・ショッピング)× エリア × 施設」で施設を検索するページでは、読み込みが完了した後に、非同期でコンテンツを生成していたため、

  • 表示までの速度
  • SEO観点

で課題がありました。

そこで、上記の課題を解消するため、施設の検索ページをSSRで同期生成するように変更しました。

SSR(Server Side Rendering)

SSRは、Webページのレンダリングをブラウザの代わりにサーバー上で行う、画面の表示において有用なアプリケーションの機能です。サーバーサイドは完全にレンダリングされたページをクライアントに送信します。

  • 画面の表示が早い
  • サーバー側の負荷が高い
  • Node.jsサーバー環境が必要

仕様変更

変更前の比較サイトにおける施設の検索ページの挙動

  1. ユーザリクエスト
  2. クエリを解析、ストアに検索パラメータを設定
  3. Vueインスタンス生成、DOMレンダリング
  4. URLパラメータを解析、ストアに検索パラメータを設定、検索リクエスト:Nuxtライフサイクルにおけるmounted
  5. レスポンスを解析、検索結果を再度レンダリング

つまり、DOMがレンダリングされた後に非同期で検索処理を実行していました。。。

mounted () {
  return this.search()
},

search () {
  const page = this.$route.query.page || 1
  :
  検索パラメータ設定
  :
  this.searchGourmetAction({ searchQuery, page }) // store経由でAPIリクエスト
}
Nuxtライフサイクル

変更点

① 検索処理の移行

そこで、検索処理を上記Nuxtライフサイクルにおけるfetch(context)のタイミングで実行するように移行しました。

async fetch ({ app, route }) {
  return await Promise.all([
    app.$categorySearch({ route })
  ])
},

これにより、ページがレンダリングされる前に検索処理を実行できるようになります。また、検索結果はストアに格納されサーバーサイドでレンダリングされます。

② 検索処理の共通化

検索処理はジャンル毎に実装されており、同様の処理が散見されていたため、併せてリファクタリングを実施しました。

具体的にはpage componentsのfetchで呼び出しがあるため、pluginを利用して検索処理を共通化しました。

export default ({ store, req }, inject) => {
  inject('categorySearch', async ({ route }) => {
    const page = route.query.page || 1
    switch (process.host) {
      case process.env.GOURMET_DOMAIN:
        :
        検索パラメータ設置
        :
        await store.dispatch('gourmetSearch/searchGourmetAction', { searchQuery, page })
        break
      :
    }
  })
}

SSRはNode.jsの環境を利用するため、ブラウザに帰属するオブジェクト(window、document)にアクセス出来なくなるので注意が必要です。

③ 検索ページ導線を変更

SSRの導入に伴い、比較サイトにおける検索ページへの導線は常にサーバーを経由する必要があります。

<NuxtLink>によるページ遷移はクライアント側で実行されるため、ブラウザをハード再読み込みしてサーバー側へリクエストされるように修正しました。

おわりに

上記の通り、SSRを導入したことにより、

  • 表示までの速度:55~62%削減
  • SEO観点:初回読み込み時に全てのコンテンツを生成

当時の課題を解消することに成功しました。

当ブログの詳細に関しては、GREE Tech Conferenceで登壇予定なので宜しければご参加下さい。

比較サイトは、aumoで最初に作られたサービスであるWeb記事と比べて、まだまだ改善の余地があります。これからも引き続き機能改善に努め、サービスの発展に貢献していきたいと思います。