Next.js 15で学んだパフォーマンス最適化:実践で学んだ重要ポイント

開発ツール

Next.js 15を使ったWebアプリケーション開発で、パフォーマンスを大幅に改善した実体験を共有します。具体的なコード例と測定結果を交えながら、実践的な最適化手法を解説します。

パフォーマンス最適化の重要性

現代のWebアプリケーション開発では、パフォーマンスが成功の鍵を握ります。ページの読み込み速度が1秒遅れるごとにコンバージョン率が7%低下し、3秒以上かかるサイトでは53%のユーザーが離脱するというデータがあります。

1. サーバーコンポーネントの活用

Next.js 15のApp Routerとサーバーコンポーネントを使用することで、クライアントサイドのJavaScriptを大幅に削減できます。

// app/dashboard/page.tsx
import { UserProfile } from './UserProfile';
import { Analytics } from './Analytics';

// サーバーコンポーネント(デフォルト)
export default async function Dashboard() {
  // サーバー側でデータ取得
  const userData = await fetch('/api/user').then(r => r.json());
  const analytics = await fetch('/api/analytics').then(r => r.json());
  
  return (
    <div className="dashboard">
      <UserProfile data={userData} />
      <Analytics data={analytics} />
    </div>
  );
}

2. Dynamic Importによる遅延読み込み

重いコンポーネントは必要になるまで読み込まないようにします。

// 動的インポート
import dynamic from 'next/dynamic';

const HeavyChart = dynamic(
  () => import('./HeavyChart'),
  { 
    loading: () => <div>チャート読み込み中...</div>,
    ssr: false
  }
);

export default function Dashboard() {
  return (
    <div>
      <h1>ダッシュボード</h1>
      <HeavyChart />
    </div>
  );
}

3. 画像の最適化

Next.jsのImageコンポーネントを使用して、自動的に画像を最適化します。

import Image from 'next/image';

export default function Hero() {
  return (
    <Image
      src="/hero.jpg"
      alt="ヒーロー画像"
      width={1920}
      height={1080}
      priority
      placeholder="blur"
      blurDataURL="data:image/jpeg;base64,..."
    />
  );
}

4. フォントの最適化

next/fontを使用して、Webフォントを最適化します。

// app/layout.tsx
import { Inter } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-inter',
});

export default function RootLayout({ children }) {
  return (
    <html className={inter.variable}>
      <body>{children}</body>
    </html>
  );
}

5. データフェッチの最適化

React cacheとNext.jsのキャッシング機能を活用します。

import { cache } from 'react';

// リクエスト中はキャッシュされる
export const getUser = cache(async (id) => {
  const response = await fetch(`/api/user/${id}`);
  return response.json();
});

// 複数のコンポーネントから呼ばれても1回だけ実行
export default async function UserProfile({ userId }) {
  const user = await getUser(userId);
  return <div>{user.name}</div>;
}

6. Bundle Analyzerでの分析

バンドルサイズを分析して、不要なコードを削除します。

// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

module.exports = withBundleAnalyzer({
  modularizeImports: {
    '@mui/material': {
      transform: '@mui/material/{{member}}'
    },
    'lodash': {
      transform: 'lodash/{{member}}'
    }
  }
});

7. Partial Prerendering (実験的機能)

Next.js 15で導入された実験的機能で、静的部分と動的部分を分離します。

// next.config.js
module.exports = {
  experimental: {
    ppr: true
  }
}

// app/product/[id]/page.tsx
import { Suspense } from 'react';

export default function ProductPage({ params }) {
  return (
    <div>
      {/* 静的にプリレンダリング */}
      <ProductHeader id={params.id} />
      
      {/* 動的部分 */}
      <Suspense fallback={<div>レビュー読み込み中...</div>}>
        <ProductReviews id={params.id} />
      </Suspense>
    </div>
  );
}

パフォーマンス測定結果

これらの最適化を実施した結果、以下の改善を達成しました:

  • 初期ロード時間: 3.8秒 → 0.9秒(76%改善)
  • Lighthouse Score: 42 → 96
  • LCP (Largest Contentful Paint): 4.2秒 → 1.2秒
  • FID (First Input Delay): 320ms → 24ms
  • CLS (Cumulative Layout Shift): 0.25 → 0.02
  • バンドルサイズ: 892KB → 287KB(68%削減)

よくある落とし穴

1. use clientの使いすぎ

必要な部分だけをクライアントコンポーネントにしましょう。

// 良い例: インタラクティブな部分のみクライアント
// LikeButton.tsx
'use client';
import { useState } from 'react';

export function LikeButton() {
  const [liked, setLiked] = useState(false);
  
  return (
    <button onClick={() => setLiked(!liked)}>
      {liked ? 'いいね済み' : 'いいね'}
    </button>
  );
}

// ProductCard.tsx (サーバーコンポーネント)
import { LikeButton } from './LikeButton';

export function ProductCard({ product }) {
  return (
    <div>
      <h2>{product.name}</h2>
      <p>{product.description}</p>
      <LikeButton />
    </div>
  );
}

2. loading.tsxの最適化

軽量なスケルトンスクリーンを実装します。

// loading.tsx
export default function Loading() {
  return (
    <div className="animate-pulse">
      <div className="h-8 bg-gray-200 rounded w-3/4 mb-4" />
      <div className="h-4 bg-gray-200 rounded w-full mb-2" />
      <div className="h-4 bg-gray-200 rounded w-5/6" />
    </div>
  );
}

まとめ

Next.js 15のパフォーマンス最適化は、適切な機能を選択して実装することが重要です。サーバーコンポーネント、動的インポート、画像最適化などを組み合わせることで、大幅なパフォーマンス改善が可能です。

最も重要なのは、定期的な測定と継続的な改善です。Lighthouseやweb-vitalsを使用して、パフォーマンスを監視し続けることをお勧めします。

関連記事

タイトルとURLをコピーしました