デプロイとCI/CD
トムのブログはローカル環境では問題なく動作していたが、本番環境にデプロイした後、さまざまな問題に直面した。画像が読み込まれない、APIのURLが
localhostにハードコーディングされていた、SSH経由での手動によるコードアップロードが遅く、エラーも発生しやすかった。彼は、コードのプッシュから本番環境へのデプロイまでのプロセスを完全に自動化されたワークフローに変えるために、自動化されたデプロイパイプラインを必要としていた。
このレッスンでは、「ローカル開発」から「本番環境へのデプロイ」に至るまでの全プロセスを解説します。Vercel を使ってワンクリックでデプロイする方法、GitHub Actions による自動パイプラインの設定、マルチ環境変数の管理、そしてページの読み込み時間を短縮するためのビルドアーティファクトの最適化について学びます。これらのデプロイスキルは、フロントエンドエンジニアが「コードを書く」ことから「製品を届ける」ことへと移行する上で、欠かせないステップとなります。
1. 学習内容
- Vercelの自動デプロイ(Git連携、ドメイン設定、チームでの共同作業)
- さまざまなデプロイ方法の比較:Netlify、Docker、および従来のサーバー
- GitHub Actions による CI/CD パイプラインの設定
- 環境変数の管理(開発環境、プレビュー環境、本番環境の分離)
- 最適化戦略の策定(パケットサイズの分析、CDN、圧縮、キャッシュ)
@next/bundle-analyzerパッケージの体積を可視化するための具体的な方法next/imageコンポーネントが画像の読み込みを自動的に最適化する方法:原理と設定
2. 概念図
トムは、包括的なCI/CDパイプラインを設計しました。開発者がGitHubにコードをプッシュすると、自動的にビルドとテストのプロセスが開始されます。これらが正常に完了すると、コードはレビューのためにステージング環境に自動的にデプロイされます。レビューが承認され、コードがメインブランチにマージされると、自動的に本番環境にデプロイされます。
このパイプラインの中核は、自動化と標準化です。すべての検査ステップ(lint、型チェック、テスト)は、手動による介入を必要とせずにCI環境内で自動的に実行されます。これにより、コード品質上の問題がマージ前に確実に検出され、検証済みのコードのみが本番環境にデプロイされることが保証されます。Vercelのプレビューデプロイ機能は、各ブランチごとに個別のプレビューURLを自動的に生成するため、プロダクトマネージャーやテスターはワンクリックで結果を確認することができます。
flowchart LR
A[Developer Push Code] --> B[GitHub Receive]
B --> C[GitHub Actions Trigger]
C --> D{Run CI Process}
D --> E[Install Dependencies<br/>npm ci]
E --> F[Code Review<br/>lint / type-check]
F --> G[Run Test<br/>jest / playwright]
G --> H[Build<br/>next build]
H --> I{Deployment Objectives}
I -->|Feature Branch| J[Vercel Preview<br/>Preview Environment]
I -->|Main Branch| K[Vercel Production<br/>Production Environment]
J --> L[Preview URL Automatically Generated]
K --> M[CDN Distribution<br/>Global Acceleration]
3. 実際の事例
| 導入オプション | 導入方法 | ISRサポート | 運用コスト | 活用事例 |
|---|---|---|---|---|
| Vercel | 自動(Gitプッシュ) | ✅ ネイティブ | 非常に低い | Next.jsプロジェクト、個人・小規模チーム |
| Netlify | 自動(Gitプッシュ) | ⚠️ 設定が必要 | 低 | 静的サイト、Gatsby |
| Docker + Nginx | 手動/CI | ❌ 手動での設定が必要 | 中 | プライベート環境での導入、完全な制御が必要 |
| AWS Amplify | 自動化 | ⚠️ 限定 | 中 | AWSエコシステムプロジェクト |
| 従来型サーバー PM2 | 手動 | ❌ | 高 | 複雑なカスタマイズ要件 |
トムのブログ公開に関する課題は増え続けるばかりだ。投稿を更新するたびに、SSH経由でサーバーにログインし、git pull、npm run build、pm2 restartを手動で実行しなければならない。この作業には少なくとも10分かかる。もしデータベースのバックアップを忘れたら、たった一つのミスですべてが消えてしまう可能性がある。さらに苛立たしいのは、他のチームメンバーによる変更によって、彼のコードが上書きされてしまうことがよくあることだ。
彼は、VercelとGitHub Actionsを活用した最新のデプロイソリューションを採用することに決めました。最初のステップは、コードをFTPからGitHubリポジトリへ移行することでした。2番目のステップは、Vercelに接続して自動デプロイを有効にすることでした。3番目のステップは、コードチェックとテストのワークフローを追加するためにGitHub Actionsを設定することでした。移行が完了すると、mainブランチにコードがプッシュされるたびに、Vercelが自動的にビルドとデプロイを行い、その全プロセスは2分未満で完了するようになりました。
また、トムはいくつかのデプロイ方法の長所と短所を比較しました。VercelはNext.jsプロジェクトに最も適しており、設定も最も簡単です。NetlifyもNext.jsに対応していますが、一部の高度な機能(ISR、ミドルウェア)を利用するには追加の設定が必要です。Dockerによるデプロイは、サーバー環境を完全に制御する必要があるシナリオに適しています。そして、従来のサーバーデプロイ(Nginx + PM2)は最も柔軟性が高い反面、運用コストも最も高くなります。個人のブログや小規模なプロジェクトにとっては、Vercelが間違いなく最良の選択肢です。
(1) Vercelの自動デプロイ
Vercelは、Next.jsを開発しているVercel社が提供するサーバーレスデプロイメントプラットフォームです。Next.jsと緊密に連携しており、フレームワークの自動検出、Serverless Functions、Edge Functions、ISR、ミドルウェアなど、Next.jsのすべての機能をサポートしています。Vercelの自動デプロイメント機能はGitとの連携に基づいており、GitHub、GitLab、またはBitbucketのリポジトリを連携させると、プッシュが行われるたびに自動的にビルドとデプロイが実行されます。
Vercelでは、3つの環境が提供されています。Production(本番環境、カスタムドメインに紐付けられています)、Preview(プレビュー環境、各ブランチごとに個別のURLが自動的に生成されます)、そしてDevelopment(ローカル開発環境)です。特にPreview環境はチームでの共同作業に最適です。プルリクエストごとにプレビューURLが自動的に生成されるため、レビュー担当者は変更内容が本番環境でどのように表示されるかを簡単に確認できます。
Vercelへのデプロイ手順(GUI方式)
- Vercelダッシュボードで
Add New -> Projectをクリックします - GitHubリポジトリを選択し、Vercelがそのリポジトリにアクセスすることを許可します
- VercelはNext.jsフレームワークを自動的に検出し、デフォルトの設定を使用します
- 「環境変数」に、必要な環境変数を追加します。
- 「デプロイ」をクリックし、デプロイが完了するまで1~2分ほど待ちます。
- デプロイが完了すると、Vercel は自動的に
.vercel.appというドメイン名を生成します - 「設定」→「ドメイン」でカスタムドメインを追加します
Vercelへのデプロイ手順(CLI方式)
# 1. Global Installation Vercel CLI
npm install -g vercel
# 2. Log In Vercel Account
vercel login
# 3. Run the deployment from the project's root directory
# The first time you run it, you'll be guided through the project setup.
vercel
# 4. Deploy to the production environment
vercel --prod
(1) ▶ サンプル:Vercel のデプロイ設定と CLI
# 1. Installation Vercel CLI
npm install -g vercel
# 2. Log in to the project root directory
vercel login
# 3. Deploy the project's root directory to the preview environment
vercel
# 4. Deploy to the production environment
vercel --prod
# 5. View the current deployment status
vercel ls
# 6. View the deployment log
vercel logs --all
// next.config.ts - Vercel Automatically read this configuration
import type { NextConfig } from 'next'
const config: NextConfig = {
// Image Optimization Settings - Allow remote image domains
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'images.example.com' },
{ protocol: 'https', hostname: '**.cloudfront.net' },
],
},
// HTTP Compression
compress: true,
// Remove X-Powered-By header (security)
poweredByHeader: false,
// Custom Build Directory(Optional)
distDir: '.next',
// Enable Strict Mode
reactStrictMode: true,
}
export default config
// vercel.json - Vercel Project Configuration(Optional,Most projects do not require)
{
"framework": "nextjs",
"buildCommand": "npm run build",
"outputDirectory": ".next",
"regions": ["hnd1", "iad1"],
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" }
]
}
]
}
(2) GitHub Actions CI/CD
GitHub Actions は、GitHub が提供する CI/CD サービスであり、YAML 設定ファイルを使用してワークフローを定義します。プッシュやプルリクエストが行われるたびに、指定された一連のジョブが自動的にトリガーされます。Tomは3つのジョブを設定しています。1つ目は、どのブランチへプッシュが行われた際にもリンティングと型チェックを実行するものです。2つ目は、mainブランチへプッシュが行われた際に、完全なテストとビルドを実行するものです。3つ目は、新しいバージョンがリリースされた際に、プレビュー環境へ自動的にデプロイするものです。
ワークフローファイルは .github/workflows/ ディレクトリに保存され、ファイル名は自由に指定できます。各ワークフローには複数のジョブを含めることができ、ジョブ間の依存関係を設定することも可能です。GitHub では、マーケットプレイスで多種多様なアクションが提供されており、コミュニティによって作成されたステップを直接再利用することができます。
GitHub Actions の主な概念には、on トリガーイベント(push、pull_request、schedule など)の定義、jobs 実行するタスクの定義、steps 各タスクの実行手順の定義、および actions/ コミュニティが提供した再利用可能なモジュールが含まれます。トムのワークフローでは、3つのコミュニティアクションが使用されています:actions/checkout@v4(コードのチェックアウト)、actions/setup-node@v4(Node.js環境の設定)、およびactions/upload-artifact@v4(ビルド成果物のアップロード)です。
(2) ▶ サンプル:GitHub Actions の完全なワークフロー
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
# Job 1:Code Quality and Type Checking
quality:
name: Code Quality Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: TypeScript type check
run: npx tsc --noEmit
- name: Lint check
run: npm run lint
# Job 2:Run Test + Build
test-and-build:
name: Test & Build
needs: quality # Dependency quality Mission Successful
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm test
env:
CI: true
- name: Build project
run: npm run build
env:
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: next-build
path: .next/
# Job 3:Automatically deploy to Vercel
deploy:
name: Deploy to Vercel
needs: test-and-build
runs-on: ubuntu-latest
# Only at main Branch Deployment
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
(3) 最適化戦略の策定
デプロイとは、単にコードをサーバーにアップロードすることだけではありません。適切に最適化されたビルド構成は、ページの読み込み時間を大幅に短縮し、帯域幅のコストを削減することができます。トムはデプロイの最適化に多大な労力を注いでおり、主に「パッケージサイズの分析」「画像の最適化」「キャッシュ戦略」という3つの分野に焦点を当てています。
パッケージサイズの分析には、@next/bundle-analyzerプラグインを使用しています。これにより、各モジュールのサイズを視覚的に確認し、サイズに異常がある依存関係を特定することができます。Tom氏は、自身のプロジェクトにおいてmoment.jsが85KBを占めていることを発見しました。dayjs(6KB)に切り替えたところ、最初の画面でのJavaScriptの読み込み量が28%削減されました。また、一度も使用されていないにもかかわらずグローバルにインポートされていたコンポーネントを発見したため、Tree Shakingを使用してそれを削除しました。
画像の最適化は、Next.jsに組み込まれているnext/imageコンポーネントを使用して実装されており、これによりWebP/AVIF形式の画像、レスポンシブサイズ、レイジーローディングが自動的に生成されます。トムのブログ記事には多数の画像が含まれていますが、next/imageを使用した後、画像の平均読み込み時間は1.2秒から0.3秒に短縮されました。
キャッシュ戦略は、CDNのCache-Controlヘッダーを設定することで実装されます。静的リソース(JS、CSS、画像)は1年間のキャッシュ期間(max-age=31536000)に設定され、HTMLページはISRの自動更新と組み合わせてより短いキャッシュ期間(max-age=60)に設定されます。これにより、ユーザーが初めてアクセスした後、それ以降の静的リソースは新たなリクエストを行うことなく、ブラウザのキャッシュから直接読み込まれます。
その他の導入オプションのクイックリファレンス
| ソリューション | 設定の複雑さ | Next.js との互換性 | 活用事例 |
|---|---|---|---|
| Vercel | 非常に低コスト | 完璧 | Next.js が推奨するデプロイプラットフォーム |
| Netlify | 低 | 良好(一部の高度な機能に制限あり) | 小規模な静的サイト |
| Docker | 中級 | 良好 | 一貫性のある環境が求められるチームプロジェクト |
| 従来の Nginx | 高 | 手動での SSR 設定が必要 | エンタープライズ向けホスティングサーバー |
| AWS Amplify | 中国語 | 良い | AWSエコシステムプロジェクト |
(3) ▶ サンプル:最適化された構成の構築
// next.config.ts - Complete Optimization Configuration
import type { NextConfig } from 'next'
// Package Volume Analysis(Enable on Demand)
const withBundleAnalyzer = process.env.ANALYZE === 'true'
? require('@next/bundle-analyzer')({ enabled: true })
: (config: any) => config
const config: NextConfig = {
// === Image Optimization ===
images: {
// Allowed Remote Image Domains
remotePatterns: [
{ protocol: 'https', hostname: 'images.example.com' },
{ protocol: 'https', hostname: 'cdn.example.com' },
],
// Image Format(Supported by default WebP)
formats: ['image/avif', 'image/webp'],
// Equipment Breakpoint(Configure according to the design draft)
deviceSizes: [640, 768, 1024, 1280, 1536],
},
// === Safety and Performance ===
compress: true,
poweredByHeader: false,
reactStrictMode: true,
// === CDN Layout ===
// If you use a custom CDN,Settings assetPrefix
// assetPrefix: 'https://cdn.example.com',
// === Experimental Features ===
experimental: {
// Optimization CSS Volume
optimizePackageImports: ['antd', '@ant-design/icons', 'lodash-es'],
},
}
export default withBundleAnalyzer(config)
// vercel.json - Caching and Security Header Configuration
// {
// "headers": [
// {
// "source": "/static/(.*)",
// "headers": [
// { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
// ]
// },
// {
// "source": "/_next/image(.*)",
// "headers": [
// { "key": "Cache-Control", "value": "public, max-age=86400, stale-while-revalidate=2592000" }
// ]
// }
// ]
// }
# Commands for Package Volume Analysis
ANALYZE=true npm run build
# This command generates two HTML Report:
# - .next/analyze/client.html (Client-Side Code Analysis)
# - .next/analyze/server.html (Server-Side Code Analysis)
# After opening your browser to view the report,,You can identify and optimize dependencies that are too large.
# Common Optimization Techniques:
# 1. Dynamic Import: const HeavyComponent = dynamic(() => import('./HeavyComponent'))
# 2. Replacing a Large Database: moment → dayjs(Minimize 95%)
# 3. Tree Shaking: import { Button } from 'antd' in place of import { Button } from 'antd/es/button'
最適化前後の比較例
| 指標 | 最適化前 | 最適化後 | 改善度 |
|---|---|---|---|
| スクロールせずに見える範囲のJSサイズ | 285 KB | 168 KB | 41%の削減 |
| Lighthouse パフォーマンススコア | 62 | 94 | 32ポイントの改善 |
| TTFB(最初のバイトまでの時間) | 420 ms | 180 ms | 57%の短縮 |
| ビルド時間 | 3分12秒 | 1分45秒 | 45%短縮 |
| CDNヒット率 | 52% | 95% | 43%の増加 |
(4) ▶ サンプル:GitHub Actions の CI パイプラインの設定
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm test -- --coverage
- name: Upload coverage
if: matrix.node-version == 20
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
build:
needs: lint-and-test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run build
- name: Check bundle size
run: |
SIZE=$(du -sk .next/static | cut -f1)
echo "Bundle size: ${SIZE}KB"
if [ "$SIZE" -gt 500 ]; then
echo "⚠️ Bundle exceeds 500KB threshold"
fi
(5) ▶ サンプル:包括的 — Next.js の本番環境へのデプロイに向けた完全な設定
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Type check
run: npx tsc --noEmit
- name: Run tests
run: npm test
- name: Build application
run: npm run build
env:
NEXT_PUBLIC_API_URL: ${{ vars.NEXT_PUBLIC_API_URL }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
- name: Run Lighthouse audit
uses: treosh/lighthouse-ci-action@v12
with:
urls: |
http://localhost:3000
uploadArtifacts: true
budgetPath: ./lighthouse-budget.json
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
working-directory: ./
- name: Notify deployment
if: always()
run: |
STATUS="${{ job.status }}"
curl -X POST "${{ secrets.SLACK_WEBHOOK }}" \
-H 'Content-type: application/json' \
-d "{\"text\":\"Deploy $STATUS on $(date -u +%Y-%m-%dT%H:%MZ)\"}"
// lighthouse-budget.json
[
{
"path": "/*",
"options": { "first-contentful-paint": { "maxNumericValue": 2000 } },
"budgets": [
{ "resourceSizes": [{ "resourceType": "script", "budget": 200 }, { "resourceType": "stylesheet", "budget": 50 }, { "resourceType": "image", "budget": 300 }, { "resourceType": "total", "budget": 600 }] }
]
}
]
❓ よくある質問
NEXT_PUBLIC_ で始まる変数はブラウザ側の JavaScript にバンドルされます。接頭辞のない変数はサーバー側でのみ利用可能です。機密性の高い変数は GitHub Actions で secrets を使用して渡し、ワークフロー内で ${{ secrets.XXX }} を使用して参照してください。npm test をローカルで実行して正常に動作することを確認することをお勧めします。また、continue-on-error: true を設定して、特定のジョブが失敗してもジョブを継続できるようにすることもできます。@next/bundle-analyzer を使用して各依存関係のバンドルサイズを分析し、置き換えたり動的にインポートしたりできる大きなライブラリを特定してください。画像の最適化は通常、最も容易に改善が図れる領域です。next/image を使用して、WebP 形式およびレスポンシブ画像サイズを自動的に生成してください。📖 まとめ
- VercelはNext.jsに最適なデプロイプラットフォームであり、Gitとの連携による自動デプロイ、プレビュー環境、サーバーレス関数、グローバルCDNをサポートしています
- GitHub Actions のワークフローは
.github/workflows/*.ymlで定義されており、複数のジョブのオーケストレーションと依存関係をサポートしています(needsが実行順序を制御します)。 - 標準的なCI/CDプロセス:コードレビュー → 型チェック → テスト実行 → ビルド → デプロイ(各段階は並行して、または順次実行可能)
- Vercelの環境変数は、環境(本番、プレビュー、開発)ごとに分離されています。機密性の高い変数は、ダッシュボードまたはCLIから設定できます。
- 3つの最適化スイート:パッケージサイズの分析(
@next/bundle-analyzer)、画像の最適化(next/image)、CDNキャッシュ(Cache-Control) dynamicダイナミックインポートとoptimizePackageImportsにより、最初の画面表示時の JavaScript のサイズを効果的に削減できる- プレビューデプロイでは、プルリクエストごとに固有のURLが割り当てられるため、チームがレビュー作業で連携しやすくなります
next.config.ts、compress、poweredByHeader、images、およびその他の構成は、セキュリティとパフォーマンスに影響を与えます- デプロイの選択肢:VercelはNext.jsに最適、Netlifyは静的サイトに最適、Dockerはチームプロジェクトに最適、そして従来のデプロイ方法はエンタープライズ環境に最適です
- CI/CDの核心的な価値:コード品質の自動チェックを行い、検証済みのコードのみが本番環境にデプロイされるようにすること
- 環境変数のスコープは接頭辞によって決まります:接頭辞なし(サーバー側)、
NEXT_PUBLIC_(ブラウザ側)、NEXT_PRIVATE_(サーバー側で明示的に宣言されたもの) next/imageコンポーネントが画像の読み込みを自動的に最適化:WebP/AVIF形式への変換、レスポンシブなサイズ調整、遅延読み込み、CDNキャッシュ- TTFB、Lighthouseスコア、および最初の画面で読み込まれるJavaScriptコードのサイズは、デプロイの品質を評価するための重要な指標です
📝 練習問題
- Next.js プロジェクトを GitHub リポジトリにプッシュし、Vercel にインポートします(「Git リポジトリのインポート」)。自動デプロイのプロセスを確認し、デプロイ後にプレビュー URL と本番 URL が正しいことを確認してください。Vercel ダッシュボードのデプロイログを確認し、ビルドプロセスの各ステップを把握してください。カスタムドメインを設定し(任意)、HTTPS を有効にしてください。
- プロジェクト内に
.github/workflows/ci.ymlを作成し、CI ワークフローを設定します。任意のブランチにコミットがプッシュされた際、自動的にnpm ci→npm run lint→npm test→npm run buildが実行されるようにします。意図的にリンターエラーやテストの失敗を引き起こし、変更をプッシュして、CI が失敗し、エラーメッセージが表示されるかどうかを確認します。その後、エラーを修正し、CIが正常に実行されることを確認します。 - プロジェクトのビルド最適化を設定する:
@next/bundle-analyzerを使用して現在のプロジェクトのパッケージサイズを分析し、サイズが最も大きい依存関係を上位 3 つ特定する。これらの大きなコンポーネントのうち 1 つに対して動的インポートを実装し(dynamic(() => import(...)))、最適化前後の JavaScript サイズの変動を比較する。next.config.tsでは、画像の最適化(remotePatternsの設定)と圧縮設定を有効にします。最後に、Chrome DevToolsのLighthouseを使用して、最適化前後のパフォーマンススコアをテストし、ファーストスクリーンのJavaScriptサイズおよびパフォーマンススコアの改善度を記録します。



