Upgrade to Pro — share decks privately, control downloads, hide ads and more …

本当の敵は思い込み? ZOZOTOWNのパフォーマンス チューニング事例

本当の敵は思い込み? ZOZOTOWNのパフォーマンス チューニング事例

2026/03/18 に行われた「ZOZO フロントエンドMeetup」で発表した登壇資料です。
https://zozotech-inc.connpass.com/event/385379/

株式会社ZOZO
ZOZOTOWN開発本部1部 WEBフロントエンドブロック
佐藤 仁

Avatar for ZOZO Developers

ZOZO Developers PRO

March 18, 2026
Tweet

More Decks by ZOZO Developers

Other Decks in Technology

Transcript

  1. © ZOZO, Inc. https://zozo.jp/ 3 • ファッションEC • 1,700以上のショップ、11,000以上のブランドの取り扱い •

    常時107万点以上の商品アイテム数と毎日平均2,700点以上の新着 商品を掲載(2025年12月末時点) • ブランド古着のファッションゾーン「ZOZOUSED」や コスメ専門モール「ZOZOCOSME」、シューズ専門ゾーン 「ZOZOSHOES」、ラグジュアリー&デザイナーズゾーン 「ZOZOVILLA」を展開 • 即日配送サービス • ギフトラッピングサービス • ツケ払い など
  2. © ZOZO, Inc. 5 アジェンダ 1. 背景 2. 結果 3.

    問題の切り分け 4. 制約 5. PCのチューニング 6. SPのチューニング 7. まとめ
  3. © ZOZO, Inc. 7 CWVの指標について • LCP (Largest Contentful Paint)

    ◦ 知覚される読み込み速度を測定するための重要な、安定した Core Web Vitals 指標です。ページの読み込みタイム ライン上で、ページのメイン コンテンツが読み込まれた可能性が高いポイントをマークします。 • TTFB (Time To First Byte) ◦ リソースのリクエストからレスポンスの最初のバイトが到着するまでの時間 • FCP (First Contentful Paint) ◦ ユーザーがページに初めて移動してから、ページのコンテンツのいずれかの部分が画面上にレンダリングされるまで の時間 • TBT (Total Blocking Time) ◦ FCPの後にメインスレッドが入力の応答性を妨げるほど長くブロックされていた合計時間 https://web.dev/?hl=ja
  4. © ZOZO, Inc. 15 制約 • GTM、KARTE、Datadogといった計測scriptの読み込みは遅延できない ◦ page_viewといったログにリアルタイム性が求められる ◦

    partytownを使いたいが、メジャーバージョンではないので実運用は難しい • 新しいWeb API、プロパティはブラウザ互換性のために使用できない ◦ requestIdleCallback ◦ content-visibility
  5. © ZOZO, Inc. 17 PCのチューニング 画像コンポーネントではloading=”eager”が ないとInterSectionObserverで遅延読み込 みをするようになっていた Web APIはSSR上でレンダリングできないた

    め、Hydration完了を待つ必要があった const enableRender = useMemo(() => { if (loading === 'eager') { return true } return isIntersecting }, [loading, isIntersecting]) {enableRender && ( <Img onLoad={handleLoaded} {...imageProps} width={width} height={height} />
  6. © ZOZO, Inc. 19 PCのチューニング これだけでは改善に至らず LCPの画像がSSRをした画像ではなく、遅延読み込みをした画像になってしま う... 商品画像は同じサイズなはずなのにどうして? >

    同じサイズの 2 つの画像がレンダリングされた場合、最初の画像が LCP 要素と見なされます。LCP 要素 は、LCP 候補が現在の LCP 要素よりも大きい場合にのみ更新されます。 https://web.dev/articles/carousel-best-practices?hl=ja
  7. © ZOZO, Inc. 20 PCのチューニング WebPerf SnippetsのLCP Trailを改修したものでLCPを調べると rectの高さと幅は同じなのにsizeがわずかに異なることが判明 •

    SSRした画像 ◦ rect.area:23527.219 ◦ LargestContentfulPaint.size:23514 • 遅延読み込みした画像 ◦ rect.area:23527.219 ◦ LargestContentfulPaint.size:23517(+3) https://webperf-snippets.nucliweb.net/CoreWebVitals/LCP-Trail https://developer.mozilla.org/en-US/docs/Web/API/LargestContentfulPaint/size?utm_source=chatgpt.com
  8. © ZOZO, Inc. 25 SPのチューニング • appチャンクのバンドルサイズを 計測 ◦ Gzipped

    sizeで238.83KB • CarouselとapiClient、これらは必 ずしも全てのページで読み込むも のではない
  9. © ZOZO, Inc. 28 SPのチューニング • apiClientはopenapi-typescript-code-generatorから生成される • ClassApiClient.generatorを使っているが、クラスが全てのメソッドを一 つのオブジェクトにまとめてしまうのでtree-shakingが効きにくい

    ◦ 加えてリプレイスが進むにつれてエンドポイントが増え続け、appチャンクも膨らみ続け る • FunctionalApiClient.generatorを使い、tree-shakingが効くようにする https://github.com/Himenon/openapi-typescript-code-generator
  10. © ZOZO, Inc. 31 私がしていた思い込み • getServerSidePropsにおけるSSRの境界が曖昧だった ◦ 商品画像は既にSSRされているものかと思っていた ◦

    SSRされていればHTMLのプレビューにimg要素が存在している ◦ HTML解析時に画像のリクエストも飛ぶ • LCPの算出においてrectの面積が同じであれば同じサイズとして扱われると 思っていた ◦ 見かけに騙されず、snippetで実測値から判断をする
  11. © ZOZO, Inc. 32 思い込みと戦うには • とりあえずベストプラクティスに則る ◦ First Viewに含まれる画像は早期にリクエストを送る

    ◦ 画像にはwidth/height属性を付与する • AIと壁打ちする ◦ Chrome DevTools MCPやdevtools内のAI アシスト機能を使う ◦ コードを読み込ませるだけで色々提案してくれるので便利 ▪ loading=”eager”くらいなら提案してくれる
  12. © ZOZO, Inc. 33 思い込みと戦うには • 実測値ベースで考察する ◦ WebPerf Snippets便利でおすすめ

    • 他のページと比較をしてみる • 正しい知識を身につける ◦ 仮説の幅を広げ、確度を高める