LINEヤフー Tech Blog

LINEヤフー株式会社のサービスを支える、技術・開発文化を発信しています。

LINEスキマニのトップページにおけるパフォーマンス改善(インターンレポート)

はじめまして。東京科学大学理学院数学系修士1年の岩崎丈至です。8月3日から6週間にわたって、LINEスキマニのフロントエンド開発にインターンシップとして参加しました。本稿ではその期間中に行った、メインページにおけるパフォーマンス改善についてご紹介させていただきます。

LINEスキマニとは

LINEスキマニは、LINE 上で仕事探しから応募までを完結できる、スポットワークサービスです。働きたい人と人手を必要とする事業者をつなぎ、日々多くのマッチングを支えています。

LINEスキマニのサービスイメージ。さまざまな業種で働く人々が描かれている

背景と課題

LINEスキマニの求人一覧ページは、多くのユーザーのメインページとなっています。しかし、Lighthouse や PageSpeed Insights でメトリクスを見るとパフォーマンスに関して改善を要する指標がいくつかあることが分かります。

PageSpeed Insightsのフィールドデータ。LCPは3.2秒、INPは249ミリ秒、CLSは0.14

さらに、Core Web Vitals には実際の求人情報が表示されるまでの時間が直接現れません。しかしながら、今回の計測では最初の求人情報が表示されるまで約 3,500 ms かかりました。

改修前の求人一覧ページのパフォーマンストレース。最初の求人情報が約3,500ミリ秒後に表示されている

これらの問題は求人一覧ページが CSR 前提のページであり、SSR によって解決が期待されるものの、それには以下のような技術的制約がありました。

  • 検索条件が多様で URL が複雑
  • 無限スクロール型のリストに求人情報を表示している

今回はスコープをログインしていない状態での初期表示速度の向上に絞った課題の解決を目標としました。

以前の実装

改修前の、求人一覧が表示されるまでの流れは以下の通りです。

改修前の求人一覧表示フロー。SSRで返すHTMLには求人情報が含まれず、ブラウザでの初期化後にAPIから取得していた

解決策

求人一覧ページに対して以下のような改修を行いました。

改修後の求人一覧表示フロー。SSR時に求人情報をプリフェッチし、求人一覧を含むHTMLをブラウザへ返す

LINEスキマニには求人詳細ページなどで使われている既存の SSR 基盤があります。今回はこれを活用し、TanStack Query のリクエストキャッシュに、あらかじめプリフェッチした求人情報を載せる方針としました。

検索パラメータの整理

TanStack Query のリクエストキャッシュを正しく管理するには、取得条件ごとにデータを一意に識別できる queryKey が必要です。しかし、求人一覧ページの検索パラメータは多様で、検索条件に影響するもの・しないもの・UI の状態変化に伴って書き換わるものが混在しており、これらを直接 queryKey に利用することはできません。そこでまず、検索パラメータの整理から着手しました。

具体的には、検索条件に影響しないパラメータをキャッシュから除外したうえで、検索条件に影響するパラメータに対するバリデーションおよび正規化(重複の除去・一意な順序への整列・既定値の省略)を行う処理を実装しました。

?prefectureIds=47,13,1&occupationIds=3,1,3&date=2026-09-09
↓
prefectureIds=[1, 13, 47], occupationIds=[1, 3]

この改修の副次的な効果として、パラメータの表記ゆれが排され、検索条件ごとに一意な URL を定められるようになりました。これにより求人一覧ページへ canonical タグを導入できるようになったため、並行して meta タグの整理も行っています。

求人情報一覧の SSR コンテンツの描画

求人情報一覧は無限スクロール型のリストなので、SSR でプリフェッチしたデータを hydration 後の初期値として引き継ぎ、ユーザー情報と検索条件の解決後に通常のクライアント側でのクエリへ移行します。

求人一覧の表示をSSRからCSRへ切り替える流れ。hydration後に検索条件を解決し、以降は無限スクロールで追加取得する

キャッシュの導入

上記の実装によって SSR は実現できたものの、今度は TTFB (Time to First Byte) の増大を招いてしまいました。

キャッシュ導入前のLCP内訳。TTFBに624ミリ秒、リソースの読み込みに328ミリ秒を要している

一般に 800 ms 以下が良好であるとされる(cf. https://web.dev/articles/ttfb)ので許容範囲内ではあるものの、多くのユーザーが訪れる求人一覧ページに影響するのはそれなりに問題です。ローカルサーバ上で検証した、あるリクエストの TTFB の内訳は以下の通りです(実際にはこれに通信レイテンシや DNS 解決、TCP 接続、TLS ネゴシエーションなどが計 100 ms ほど加算されます)。

指標値
ローカルサーバでの TTFB532 ms
サーバ上での SSR 合計529 ms
API フェッチ403 ms
renderToString126 ms
シリアライズ4.9 ms
dehydrate など<1 ms

API 呼び出しの待ち時間が TTFB の大半を占めていることが分かります。そこで、API のレスポンスをキャッシュすることによってこの待ち時間を削減することを考えました。しかし、TanStack Query では QueryClient をリクエストごとに生成し、異なるユーザーやリクエスト間でデータを共有しないことを推奨しており、このリポジトリもそれに則っています。QueryClient をリクエスト間で共有するには大規模な改修が必要になるうえ、そのキャッシュには個人情報が含まれ得るため、ユーザー間の情報漏洩を防ぐための考慮すべき事項が数多く生じます。加えて、フロント側のコードが基本的には isomorphic に書かれていることも、設計上の難しさになっていました。

そこでより簡潔な解決策として、サーバ側にリクエスト関数のデコレーターという形で Stale-While-Revalidate 方式の第二のキャッシュ機構を追加しました。

キャッシュ導入後のLCP内訳。TTFBは267ミリ秒、リソースの読み込み時間は43ミリ秒まで短縮している

計測したところ、キャッシュヒット時には API 待ち時間を 1 ms 程度まで削減することができました。

結果

本来であれば本番環境にデプロイし、実ユーザーに対するパフォーマンス改善を数値として見届けたかったのですが、その前にインターン期間の終了を迎えてしまいました。以下は開発環境にデプロイし計測したデータです。

改修後のLighthouse計測結果。パフォーマンススコア98、FCPは0.5秒、LCPは0.8秒

かなり早くなりました!LCP (Largest Contentful Paint) の時点ですでに求人情報まで出せているので、意味のあるユーザー体験の改善になったのではないかと思います。Chrome DevTools を利用して集計した、あるリクエストに対する LCP が以下になります。

指標値
TTFB226 ms
リソース読み込みの遅延4 ms
リソースの読み込み時間46 ms
要素のレンダリング111 ms
LCP 合計387 ms

感想

まずは6週間にわたり、毎日1on1を実施して日々の進捗報告や技術上の相談に応じてくださったメンターの門野さんに、この場を借りてお礼申し上げます。LINEスキマニには素晴らしいメンバーとカルチャーが揃っており、各々の技術力の高さもさることながら、生産性向上のために努力を惜しまない姿勢や、開発外のメンバーとの緊密な連携など、毎日が刺激となるような環境でした。これが私の初インターンということもあって至らない点も多々ありましたが、メンバーの温かいご支援のもとで多くのことを学び、成長できたと感じています。非常に濃密かつ楽しい6週間でした!