EXERCISE 60分

佐藤CTOからの指令:「ECサイトのLighthouseスコアが45点まで落ちている。Core Web Vitals を改善し、90点以上を達成してほしい。」

#ミッション難易度目安時間
1Core Web Vitals の診断★★☆15分
2バンドル最適化★★★15分
3画像・フォント最適化★★☆15分
4レンダリング戦略の設計★★★15分

Mission 1: Core Web Vitals の診断

以下のLighthouse結果を分析し、改善の優先順位を付けよ。

Performance Score: 45
LCP: 6.2s (Poor) - Hero画像(2.4MB JPEG)
INP: 450ms (Poor) - 商品フィルタの再レンダリング
CLS: 0.35 (Poor) - Web Font読み込み時のシフト、画像サイズ未指定
FCP: 3.1s - レンダリングブロックCSS(800KB)
TBT: 1200ms - メインスレッドで重いJS処理

バンドル構成:
  main.js: 1.2MB (gzip 380KB) - lodash全体, moment.js含む
  vendor.js: 2.1MB (gzip 650KB) - UI library全体
  styles.css: 450KB - Tailwind未使用クラス大量
解答例
■ 優先順位(インパクト × 実装容易性)

1. [LCP] Hero画像の最適化 (インパクト: 大, 容易性: 高)
   - JPEG → WebP/AVIF変換で70%圧縮(2.4MB → 200KB)
   - srcset でレスポンシブ対応
   - preload + fetchpriority="high"
   - 期待改善: LCP 6.2s → 2.0s

2. [TBT/INP] バンドルサイズ削減 (インパクト: 大, 容易性: 中)
   - lodash → lodash-es(Tree Shaking)
   - moment.js → dayjs(280KB → 6KB)
   - UI libraryをTree Shaking対応版に変更
   - 期待改善: main.js 380KB → 120KB

3. [CLS] レイアウトシフト修正 (インパクト: 中, 容易性: 高)
   - 全img/video にwidth/height属性追加
   - font-display: swap + size-adjust設定
   - 期待改善: CLS 0.35 → 0.05

4. [FCP] CSS最適化 (インパクト: 中, 容易性: 中)
   - Tailwind CSS Purge有効化(450KB → 15KB)
   - Critical CSS インライン化
   - 期待改善: FCP 3.1s → 1.2s

5. [INP] フィルタ再レンダリング最適化 (インパクト: 中, 容易性: 中)
   - React.memo + useMemoで再レンダリング抑制
   - 商品リスト仮想化(react-window)
   - フィルタ処理をWeb Workerに移動
   - 期待改善: INP 450ms → 150ms

Mission 2: バンドル最適化

以下のバンドル構成を分析し、最適化計画を策定せよ。

// 現在のインポート構成
import _ from 'lodash';
import moment from 'moment';
import 'moment/locale/ja';
import { Chart } from 'chart.js';
import * as Icons from '@heroicons/react/24/outline';
import { DataGrid } from '@mui/x-data-grid';
解答例
// 最適化後
import { debounce, get, groupBy } from 'lodash-es';  // 個別インポート
import dayjs from 'dayjs';                             // moment → dayjs
import 'dayjs/locale/ja';
dayjs.locale('ja');

// Chart.js: 必要なコンポーネントだけ登録
import { Chart, LineController, LineElement, PointElement,
         LinearScale, CategoryScale, Tooltip } from 'chart.js';
Chart.register(LineController, LineElement, PointElement,
               LinearScale, CategoryScale, Tooltip);

// Icons: 個別インポート
import { ShoppingCartIcon, HeartIcon } from '@heroicons/react/24/outline';

// DataGrid: 動的インポート(管理画面でのみ使用)
const DataGrid = lazy(() => import('@mui/x-data-grid').then(m => ({ default: m.DataGrid })));

// 結果:
// main.js: 380KB → 95KB (gzip) - 75%削減
// vendor.js: 650KB → 180KB (gzip) - 72%削減

Mission 3: 画像・フォント最適化

以下のECサイトの画像構成を最適化せよ。

現在の構成:
- Hero画像: 2400x1200px JPEG, 2.4MB
- 商品サムネイル: 800x800px PNG, 平均500KB × 20枚
- カテゴリアイコン: 64x64px PNG × 12個
- カスタムフォント: NotoSansJP-Regular.ttf (16MB)
解答例
<!-- Hero画像 -->
<picture>
  <source srcset="/hero-1200.avif 1200w, /hero-800.avif 800w" type="image/avif" />
  <source srcset="/hero-1200.webp 1200w, /hero-800.webp 800w" type="image/webp" />
  <img src="/hero-800.jpg" alt="Hero"
       sizes="100vw" width="2400" height="1200"
       fetchpriority="high" decoding="async" />
</picture>
<!-- 2.4MB → 約150KB (AVIF) -->

<!-- 商品サムネイル: 画像CDN + lazy loading -->
<img src="https://cdn.example.com/products/123?w=400&f=webp&q=80"
     srcset="...?w=400 400w, ...?w=800 800w"
     sizes="(max-width: 640px) 50vw, 25vw"
     loading="lazy" decoding="async" width="400" height="400" alt="Product" />
<!-- 500KB → 約30KB/枚 -->

<!-- アイコン: SVGスプライトに統合 -->
<!-- 12個のPNG (合計100KB) → 1つのSVGスプライト (8KB) -->

<!-- フォント: woff2 + サブセット化 -->
<!-- ttf 16MB → woff2サブセット 約300KB -->
<link rel="preload" href="/fonts/NotoSansJP-subset.woff2"
      as="font" type="font/woff2" crossorigin />

<!-- 総削減: 12.4MB → 約0.9MB (93%削減) -->

Mission 4: レンダリング戦略の設計

以下のECサイトの各ページに最適なレンダリング戦略を選択し、根拠を説明せよ。

ページ一覧:
1. トップページ(プロモバナー、人気商品、カテゴリ一覧)
2. 商品一覧ページ(フィルタ、ソート、ページネーション)
3. 商品詳細ページ(商品情報、レビュー、関連商品)
4. カート・チェックアウト(ユーザー固有、決済)
5. マイページ(注文履歴、お気に入り)
6. 特集ページ(セール、季節コンテンツ)
解答例
ページ戦略根拠
トップISR (60秒)プロモは頻繁に変わるが秒単位の鮮度は不要。高トラフィックなのでキャッシュ効果大
商品一覧SSR + クライアントフィルタフィルタ/ソートはクライアントで動的処理。初期一覧はSSRでSEO対応
商品詳細ISR (300秒)SEO重要。在庫・価格は部分的にクライアントで最新取得
カートCSRユーザー固有データ。SEO不要。リアルタイム更新が必要
マイページCSRユーザー認証必須。SEO不要。動的データ
特集ページSSG事前に用意可能な静的コンテンツ。ビルド時に生成
Hydration戦略:
- トップ/商品詳細: Progressive Hydration
  → Hero/商品情報は即Hydrate、レビュー/関連商品は遅延
- 商品一覧: フィルタUIのみ即Hydrate、商品カードは静的
- カート: Full Hydration(全体がインタラクティブ)