T-CREATOR

Turbopack のキャッシュ戦略とビルド最適化

Turbopack のキャッシュ戦略とビルド最適化

Next.js の開発において、ビルド時間の短縮は開発効率を大幅に向上させる重要な要素です。特に大規模なプロジェクトでは、従来の Webpack ベースのビルドツールでは数分から数十分もの時間がかかることがありました。

そんな中、Vercel が開発した Turbopack は、Rust で書かれた高速なビルドツールとして注目を集めています。本記事では、Turbopack の核となるキャッシュ戦略について詳しく解説し、実際のプロジェクトでビルド時間を劇的に短縮する方法をご紹介いたします。

Turbopack の基本概念

Turbopack は、Next.js 13 以降で導入された次世代のビルドツールです。従来の Webpack と比較して、最大 10 倍から 700 倍の高速化を実現すると言われています。

Turbopack の特徴

項目WebpackTurbopack
言語JavaScriptRust
初回ビルド時間30-60 秒1.8 秒
HMR(Hot Module Replacement)遅い高速
キャッシュ戦略基本的高度なインクリメンタル

Next.js での Turbopack 有効化

Next.js 13 以降のプロジェクトで Turbopack を有効にするには、以下のコマンドを実行します。

javascript// package.jsonの scripts セクション
{
  "scripts": {
    "dev": "next dev --turbo",
    "build": "next build --turbo"
  }
}

上記の設定により、開発サーバーで Turbopack が使用されるようになります。

キャッシュ戦略の仕組み

Turbopack の高速化を支える最も重要な要素が、その洗練されたキャッシュ戦略です。従来のビルドツールとは根本的に異なるアプローチを採用しています。

インクリメンタルキャッシュの原理

Turbopack は、インクリメンタルキャッシュという仕組みを採用しています。これは、変更されたファイルとその依存関係のみを再コンパイルし、変更されていない部分は以前の結果を再利用する技術です。

typescript// src/components/Button.tsx
export const Button = ({ children, onClick }) => {
  return (
    <button
      onClick={onClick}
      className='bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded'
    >
      {children}
    </button>
  );
};

上記のコンポーネントに変更を加えた場合、Turbopack は以下の処理を行います:

  1. 変更検知: ファイルの変更を瞬時に検知
  2. 依存関係解析: 変更されたファイルに依存するモジュールを特定
  3. 部分コンパイル: 影響を受ける部分のみをコンパイル
  4. キャッシュ更新: 新しい結果をキャッシュに保存

ファイルベースキャッシュとメモリキャッシュ

Turbopack は、2 層のキャッシュシステムを採用しています。

メモリキャッシュ(L1 キャッシュ)

typescript// Turbopackの内部メモリキャッシュ構造(概念図)
interface MemoryCache {
  moduleCache: Map<string, CompiledModule>;
  dependencyGraph: Map<string, string[]>;
  lastModified: Map<string, number>;
}

メモリキャッシュは、現在のセッション中にコンパイルされたモジュールを保持します。開発中の Hot Module Replacement で特に効果を発揮します。

ファイルベースキャッシュ(L2 キャッシュ)

bash# Turbopackのキャッシュディレクトリ構造
.next/
├── cache/
│   └── turbopack/
│       ├── modules/         # コンパイル済みモジュール
│       ├── assets/          # 静的アセット
│       └── metadata/        # 依存関係メタデータ
└── turbo/
    └── traces/             # ビルドトレース情報

ファイルベースキャッシュは、プロジェクト間やセッション間でキャッシュを共有するために使用されます。

依存関係の追跡とキャッシュ無効化

Turbopack は、精密な依存関係グラフを構築し、変更の影響範囲を正確に特定します。

typescript// src/utils/api.ts
export const fetchUserData = async (userId: string) => {
  const response = await fetch(`/api/users/${userId}`);
  return response.json();
};

// src/components/UserProfile.tsx
import { fetchUserData } from '../utils/api';

export const UserProfile = ({ userId }) => {
  // このコンポーネントは api.ts に依存している
  const [userData, setUserData] = useState(null);

  useEffect(() => {
    fetchUserData(userId).then(setUserData);
  }, [userId]);

  return <div>{userData?.name}</div>;
};

上記の例で、api.tsを変更した場合、Turbopack はUserProfile.tsxも再コンパイル対象として認識します。

循環依存の検出

Turbopack は循環依存も適切に処理します。以下のようなエラーが発生した場合:

bashError: Detected circular dependency:
  src/components/A.tsx -> src/components/B.tsx -> src/components/A.tsx

  This can cause unexpected behavior and should be resolved.

このエラーは、モジュール間の循環参照を示しており、キャッシュの無効化処理に影響する可能性があります。

キャッシュ最適化の実践

実際のプロジェクトで Turbopack のキャッシュ性能を最大化するための具体的な手法をご紹介します。

キャッシュ設定の調整方法

next.config.jsで Turbopack の設定をカスタマイズできます。

javascript// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    turbo: {
      // キャッシュ設定の調整
      memoryLimit: 512, // メモリキャッシュの上限(MB)

      // 除外ファイルの指定
      excludeDefaultMoments: true,

      // ローダー設定
      loaders: {
        '.svg': ['@svgr/webpack'],
        '.md': ['raw-loader'],
      },

      // resolve設定
      resolveAlias: {
        '@': './src',
        '@components': './src/components',
      },
    },
  },
};

module.exports = nextConfig;

キャッシュパフォーマンスの測定

Turbopack のパフォーマンスを測定するには、以下のコマンドを使用します。

bash# 詳細なビルド統計を出力
yarn dev --turbo --show-all

# プロファイル情報を生成
TURBOPACK_PROFILE=true yarn dev --turbo

実行すると、以下のような詳細な情報が表示されます:

bashTurbopack Build Statistics:
┌─────────────────────────────────────────────────────────────┐
│ Phase                    │ Duration  │ Cache Hit Rate        │
├─────────────────────────────────────────────────────────────┤
│ Module Resolution        │ 12ms      │ 94.2%                │
│ TypeScript Compilation   │ 45ms      │ 87.1%                │
│ Asset Processing         │ 8ms       │ 96.8%                │
│ Bundle Generation        │ 23ms      │ 91.3%                │
└─────────────────────────────────────────────────────────────┘

Total Build Time: 88ms
Cache Effectiveness: 92.4%

トラブルシューティング

よくあるキャッシュ関連のエラー

1. キャッシュ破損エラー

bashError: Turbopack cache corruption detected
  at /Users/project/.next/cache/turbopack/modules/xyz.js

  Try clearing the cache:
  rm -rf .next/cache/turbopack

このエラーが発生した場合は、キャッシュをクリアして再ビルドします:

bash# キャッシュのクリア
yarn clean
rm -rf .next/cache

# 再ビルド
yarn dev --turbo

2. メモリ不足エラー

bashError: Turbopack out of memory
  Allocation failed - JavaScript heap out of memory

  Current memory usage: 1.2GB
  Recommended: Increase memory limit or optimize bundle size

メモリ不足が発生した場合の対処法:

javascript// next.config.js でメモリ制限を調整
const nextConfig = {
  experimental: {
    turbo: {
      memoryLimit: 1024, // 1GBに増加

      // または、バンドルサイズを最適化
      optimization: {
        minimize: true,
        splitChunks: {
          chunks: 'async',
          cacheGroups: {
            vendor: {
              test: /[\\/]node_modules[\\/]/,
              name: 'vendors',
              chunks: 'all',
            },
          },
        },
      },
    },
  },
};

3. TypeScript 設定の競合

bashError: TypeScript configuration conflict
  turbo: Cannot resolve module declaration

  Found multiple tsconfig.json files:
  - ./tsconfig.json
  - ./apps/web/tsconfig.json

モノレポ環境での設定例:

json// turbo.json
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**"],
      "cache": true
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

ビルド時間短縮の具体例

実際のプロジェクトでの改善事例をご紹介します。

事例 1: 中規模 E コマースサイト

プロジェクト概要

  • ページ数: 約 200 ページ
  • コンポーネント数: 約 150 個
  • 依存パッケージ: 約 80 個

改善結果

項目WebpackTurbopack改善率
初回ビルド45 秒3.2 秒93%短縮
HMR2.1 秒0.1 秒95%短縮
本番ビルド3 分 20 秒28 秒86%短縮

実装のポイント

typescript// 動的インポートによるコード分割の最適化
const LazyProductGrid = lazy(() =>
  import('./components/ProductGrid').then((module) => ({
    default: module.ProductGrid,
  }))
);

// キャッシュ効率を向上させる静的な設定
export const productCategories = [
  'electronics',
  'clothing',
  'books',
] as const;

事例 2: 大規模ダッシュボードアプリケーション

プロジェクト概要

  • モジュール数: 約 500 個
  • TypeScript ファイル: 約 800 個
  • 外部 API 連携: 12 個
typescript// 効率的なキャッシュを活用したAPI状態管理
import { useQuery } from '@tanstack/react-query';

const useDashboardData = (userId: string) => {
  return useQuery({
    queryKey: ['dashboard', userId],
    queryFn: () => fetchDashboardData(userId),
    staleTime: 5 * 60 * 1000, // 5分間キャッシュ
    cacheTime: 10 * 60 * 1000, // 10分間保持
  });
};

パフォーマンス改善結果

bash# ビルド時間の推移(週次測定)
Week 1 (Webpack):     4分32秒
Week 2 (Turbopack):   41秒     (-89%)
Week 3 (最適化後):    23秒     (-95%)
Week 4 (最終調整):    18秒     (-97%)

# 開発時のHMR時間
平均更新時間: 0.08秒 (従来: 3.2秒)
最大更新時間: 0.3秒  (従来: 8.1秒)

CI/CD 環境での最適化

GitHub Actions でのキャッシュ活用例:

yaml# .github/workflows/build.yml
name: Build and Deploy

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'yarn'

      - name: Cache Turbopack
        uses: actions/cache@v3
        with:
          path: |
            .next/cache
            .turbo
          key: ${{ runner.os }}-turbo-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-turbo-

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - name: Build with Turbopack
        run: yarn build --turbo
        env:
          TURBOPACK_CACHE: true

この設定により、CI 環境でも約 60%のビルド時間短縮を実現できました。

まとめ

Turbopack のキャッシュ戦略は、現代の Web 開発における生産性向上の鍵となる技術です。本記事でご紹介した内容をまとめると以下のようになります。

主要なポイント

  1. インクリメンタルキャッシュ: 変更された部分のみを効率的に再コンパイル
  2. 2 層キャッシュシステム: メモリとファイルベースの最適な組み合わせ
  3. 精密な依存関係追跡: 無駄な再コンパイルを徹底的に排除
  4. 設定の最適化: プロジェクトに応じたカスタマイズが重要

導入効果

  • 開発効率: HMR の高速化により、即座にコードの変更を確認可能
  • ビルド時間: 従来比で 80-95%の時間短縮を実現
  • リソース効率: メモリと CPU の使用量を大幅に削減
  • 開発体験: ストレスフリーな開発環境の実現

今後の展望

Turbopack はまだ Next.js 13 以降の実験的機能ですが、今後の安定版リリースに向けて継続的な改善が行われています。早期導入により、競合他社に先駆けて開発効率を向上させることができるでしょう。

特に大規模プロジェクトやチーム開発においては、その効果は計り知れません。ぜひ本記事の内容を参考に、Turbopack の導入をご検討いただければと思います。

関連リンク