Turborepo完全ガイド — モノレポ開発を10倍高速化する


Turborepoとは

Turborepoは、Vercelが開発するモノレポ向けビルドシステムです。複数のパッケージやアプリケーションを含むリポジトリにおいて、タスクの実行を劇的に高速化します。

従来のモノレポツール(Lerna、Nx)と比較して、Turborepoは設定がシンプルで、インクリメンタルビルドとリモートキャッシュによって圧倒的なパフォーマンスを実現します。

なぜTurborepoなのか

1. インクリメンタルビルド

変更されたパッケージのみをビルドし、未変更のパッケージはキャッシュから復元します。これにより、CIでのビルド時間を数分から数秒に短縮できます。

2. リモートキャッシュ

チーム全体でビルド結果を共有。他の開発者やCIが既にビルドしたパッケージは、再ビルドせずにキャッシュから取得します。

3. タスクパイプライン

依存関係を自動解決し、並列実行可能なタスクを最大限に活用します。

4. ゼロコンフィグ

最小限の設定で始められ、段階的に最適化できます。

基本的なセットアップ

プロジェクト構造

my-monorepo/
├── package.json
├── turbo.json
├── pnpm-workspace.yaml
├── apps/
│   ├── web/          # Next.jsアプリ
│   ├── admin/        # 管理画面
│   └── api/          # APIサーバー
├── packages/
│   ├── ui/           # 共通UIコンポーネント
│   ├── utils/        # ユーティリティ
│   ├── config/       # 共通設定
│   └── tsconfig/     # TypeScript設定
└── tooling/
    ├── eslint-config/
    └── typescript-config/

pnpm-workspace.yaml

packages:
  - 'apps/*'
  - 'packages/*'
  - 'tooling/*'

ルートのpackage.json

{
  "name": "monorepo",
  "private": true,
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "test": "turbo run test",
    "lint": "turbo run lint",
    "clean": "turbo run clean"
  },
  "devDependencies": {
    "turbo": "^2.0.0",
    "typescript": "^5.3.0"
  },
  "packageManager": "pnpm@9.0.0"
}

turbo.jsonの設計

turbo.jsonはTurborepoの心臓部です。タスクの依存関係、キャッシュ戦略、入出力を定義します。

{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": [".env", "tsconfig.json"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "build/**"],
      "cache": true
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"],
      "cache": true
    },
    "lint": {
      "cache": true
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "clean": {
      "cache": false
    }
  }
}

重要な設定項目

dependsOn

タスクの依存関係を定義します。

  • ^build: 依存パッケージのbuildタスクを先に実行
  • build: 同一パッケージ内のbuildタスクを先に実行
{
  "test": {
    "dependsOn": ["^build", "build"]
  }
}

outputs

キャッシュ対象となる出力ファイルを指定します。

{
  "build": {
    "outputs": [
      "dist/**",
      ".next/**",
      "!.next/cache/**"
    ]
  }
}

inputs

入力ファイルを明示的に指定することで、不要なキャッシュ無効化を防ぎます。

{
  "build": {
    "inputs": ["src/**", "package.json"]
  }
}

パッケージ間の依存関係

内部パッケージの利用

// apps/web/package.json
{
  "name": "web",
  "dependencies": {
    "@repo/ui": "workspace:*",
    "@repo/utils": "workspace:*"
  }
}
// apps/web/src/app/page.tsx
import { Button } from '@repo/ui'
import { formatDate } from '@repo/utils'

export default function Home() {
  return <Button>{formatDate(new Date())}</Button>
}

TypeScriptの設定

// packages/ui/tsconfig.json
{
  "extends": "@repo/typescript-config/react-library.json",
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}
// apps/web/tsconfig.json
{
  "extends": "@repo/typescript-config/nextjs.json",
  "compilerOptions": {
    "paths": {
      "@repo/ui": ["../../packages/ui/src/index.ts"]
    }
  }
}

キャッシング戦略

ローカルキャッシュ

Turborepoは自動的に.turbo/cacheにビルド結果をキャッシュします。

# 初回ビルド
pnpm build  # 20秒

# キャッシュからの復元
pnpm build  # 0.5秒

リモートキャッシュ(Vercel)

# Vercelにログイン
npx turbo login

# リモートキャッシュを有効化
npx turbo link

これにより、チームメンバーやCIがビルド結果を共有できます。

カスタムリモートキャッシュ

自前のキャッシュサーバーを使うことも可能です。

{
  "remoteCache": {
    "signature": true,
    "preflight": true
  }
}

実行戦略

フィルタリング

特定のパッケージのみを対象にする。

# webアプリのみビルド
turbo run build --filter=web

# uiパッケージとそれに依存するすべてをビルド
turbo run build --filter=...@repo/ui

# webとその依存をビルド
turbo run build --filter=web...

# 変更のあったパッケージのみテスト
turbo run test --filter=[HEAD^1]

並列実行

# 最大4プロセスで並列実行
turbo run build --concurrency=4

# 最大限の並列実行
turbo run build --concurrency=100%

Force実行

# キャッシュを無視して実行
turbo run build --force

CI/CDでの活用

GitHub Actions

name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v3
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - run: pnpm install --frozen-lockfile

      - name: Build
        run: pnpm turbo build
        env:
          TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
          TURBO_TEAM: ${{ secrets.TURBO_TEAM }}

      - name: Test
        run: pnpm turbo test

変更検知デプロイ

- name: Check for changes
  id: changes
  run: |
    if turbo run build --filter=web...[HEAD^1] --dry-run | grep -q "web:build"; then
      echo "web_changed=true" >> $GITHUB_OUTPUT
    fi

- name: Deploy Web
  if: steps.changes.outputs.web_changed == 'true'
  run: vercel deploy

パフォーマンス最適化

1. 適切なoutputs指定

{
  "build": {
    "outputs": [
      "dist/**",
      "!dist/**/*.map"
    ]
  }
}

2. globalDependenciesの最小化

{
  "globalDependencies": [
    ".env.production",
    "tsconfig.base.json"
  ]
}

3. 環境変数の管理

{
  "pipeline": {
    "build": {
      "env": [
        "NODE_ENV",
        "NEXT_PUBLIC_API_URL"
      ]
    }
  }
}

環境変数が変わった時だけキャッシュを無効化します。

よくあるパターン

共通UIライブラリ

// packages/ui/src/button.tsx
export function Button({ children }: { children: React.ReactNode }) {
  return <button className="btn">{children}</button>
}

// packages/ui/package.json
{
  "name": "@repo/ui",
  "exports": {
    "./button": "./src/button.tsx",
    "./input": "./src/input.tsx"
  }
}

共通設定の共有

// packages/config/eslint-preset.js
module.exports = {
  extends: ['next', 'turbo', 'prettier'],
  rules: {
    '@next/next/no-html-link-for-pages': 'off',
  },
}

// apps/web/.eslintrc.js
module.exports = {
  extends: ['@repo/config/eslint-preset'],
}

まとめ

Turborepoは、モノレポ開発を次のレベルに引き上げるツールです。

  • インクリメンタルビルドで圧倒的な高速化
  • リモートキャッシュでチーム全体の効率向上
  • シンプルな設定で段階的な導入が可能
  • pnpm workspacesと完璧に統合

特に、複数のアプリケーションと共通ライブラリを持つプロジェクトにおいて、開発体験とCI時間の大幅な改善が期待できます。まずは既存のモノレポにTurborepoを導入し、ビルド時間の変化を体感してみてください。