Vercelエンタープライズ:大規模アプリケーションとチーム開発
概要
Vercelは個人開発者からエンタープライズまで幅広く対応していますが、大規模なチーム開発やエンタープライズ要件では特別な機能とベストプラクティスが必要です。この記事では、エンタープライズレベルの機能と運用について詳しく解説します。
エンタープライズ機能
1. チーム管理と権限
bash
# エンタープライズチームの作成
vercel teams create enterprise-team --type enterprise
# ロールベースのアクセス制御
vercel teams invite user@company.com --role admin
vercel teams invite user@company.com --role developer
vercel teams invite user@company.com --role viewer
2. SSO (Single Sign-On)
json
// vercel.json
{
"sso": {
"provider": "saml",
"domain": "company.com",
"attributes": {
"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
"name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
}
}
}
3. カスタムドメイン管理
bash
# ドメインの追加
vercel domains add app.company.com
# DNS設定の確認
vercel domains ls
# SSL証明書の管理
vercel certs ls
大規模アプリケーションのアーキテクチャ
1. マイクロフロントエンド
javascript
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: '/auth/:path*',
destination: 'https://auth.company.com/:path*'
},
{
source: '/admin/:path*',
destination: 'https://admin.company.com/:path*'
}
]
}
}
2. モノレポ管理
json
// package.json
{
"workspaces": [
"apps/*",
"packages/*"
],
"scripts": {
"build:all": "turbo run build",
"dev:all": "turbo run dev",
"test:all": "turbo run test"
}
}
3. 環境分離
bash
# 環境別のプロジェクト
vercel --env production
vercel --env staging
vercel --env development
# 環境変数の管理
vercel env add DATABASE_URL production
vercel env add DATABASE_URL preview
vercel env add DATABASE_URL development
パフォーマンス最適化
1. エッジキャッシュ戦略
javascript
// middleware.js
import { NextResponse } from 'next/server'
export function middleware(request) {
const response = NextResponse.next()
// 動的コンテンツのキャッシュ
if (request.nextUrl.pathname.startsWith('/api/data')) {
response.headers.set('Cache-Control', 's-maxage=300, stale-while-revalidate')
}
// 静的コンテンツのキャッシュ
if (request.nextUrl.pathname.startsWith('/static')) {
response.headers.set('Cache-Control', 'public, max-age=31536000, immutable')
}
return response
}
2. 画像最適化
javascript
// next.config.js
module.exports = {
images: {
loader: 'custom',
loaderFile: './image-loader.js',
formats: ['image/webp', 'image/avif'],
minimumCacheTTL: 31536000,
dangerouslyAllowSVG: true,
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;"
}
}
3. バンドル最適化
javascript
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true'
})
module.exports = withBundleAnalyzer({
experimental: {
optimizeCss: true,
optimizePackageImports: ['@mui/material', 'lodash']
},
webpack: (config, { dev, isServer }) => {
if (!dev && !isServer) {
config.optimization.splitChunks.cacheGroups = {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
return config
}
})
セキュリティ強化
1. 認証と認可
javascript
// lib/auth.js
import { getServerSession } from 'next-auth/next'
import { authOptions } from './auth-options'
export async function requireAuth(req, res, next) {
const session = await getServerSession(req, res, authOptions)
if (!session) {
return res.status(401).json({ error: 'Unauthorized' })
}
req.user = session.user
return next()
}
2. API保護
javascript
// middleware.js
import { NextResponse } from 'next/server'
import { verifyToken } from './lib/jwt'
export async function middleware(request) {
if (request.nextUrl.pathname.startsWith('/api/protected')) {
const token = request.headers.get('authorization')?.replace('Bearer ', '')
if (!token) {
return NextResponse.json({ error: 'No token provided' }, { status: 401 })
}
try {
const decoded = await verifyToken(token)
request.user = decoded
} catch (error) {
return NextResponse.json({ error: 'Invalid token' }, { status: 401 })
}
}
return NextResponse.next()
}
3. セキュリティヘッダー
javascript
// next.config.js
const securityHeaders = [
{
key: 'X-DNS-Prefetch-Control',
value: 'on'
},
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload'
},
{
key: 'X-XSS-Protection',
value: '1; mode=block'
},
{
key: 'X-Frame-Options',
value: 'SAMEORIGIN'
},
{
key: 'X-Content-Type-Options',
value: 'nosniff'
},
{
key: 'Referrer-Policy',
value: 'origin-when-cross-origin'
}
]
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: securityHeaders
}
]
}
}
監視とログ
1. 統合監視
javascript
// lib/monitoring.js
import * as Sentry from '@sentry/nextjs'
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
integrations: [
new Sentry.BrowserTracing({
tracePropagationTargets: ['localhost', 'your-domain.com']
})
]
})
export function captureException(error, context = {}) {
Sentry.captureException(error, {
extra: context
})
}
2. パフォーマンス監視
javascript
// lib/performance.js
export function trackPerformance(metric) {
if (typeof window !== 'undefined') {
window.gtag('event', 'performance', {
event_category: 'Web Vitals',
event_label: metric.name,
value: Math.round(metric.value)
})
}
}
export function trackCoreWebVitals() {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(trackPerformance)
getFID(trackPerformance)
getFCP(trackPerformance)
getLCP(trackPerformance)
getTTFB(trackPerformance)
})
}
3. ログ集約
javascript
// lib/logger.js
import pino from 'pino'
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
formatters: {
level: (label) => {
return { level: label }
}
}
})
export function logError(error, context = {}) {
logger.error({
error: error.message,
stack: error.stack,
...context
})
}
export function logInfo(message, data = {}) {
logger.info({ message, ...data })
}
CI/CDとデプロイメント
1. マルチステージデプロイメント
yaml
# .github/workflows/deploy.yml
name: Deploy to Vercel
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm run test
- run: npm run build
deploy-staging:
needs: test
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID }}
vercel-project-id: ${{ secrets.PROJECT_ID_STAGING }}
deploy-production:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID }}
vercel-project-id: ${{ secrets.PROJECT_ID_PROD }}
vercel-args: '--prod'
2. カナリアデプロイメント
javascript
// middleware.js
import { NextResponse } from 'next/server'
export function middleware(request) {
const canaryPercentage = 10 // 10%のトラフィックを新バージョンに
const random = Math.random() * 100
if (random < canaryPercentage) {
// 新バージョンへのルーティング
return NextResponse.rewrite(new URL('/canary' + request.nextUrl.pathname, request.url))
}
return NextResponse.next()
}
3. ロールバック戦略
bash
# 特定のデプロイメントにロールバック
vercel rollback https://my-app-abc123.vercel.app
# 最新のデプロイメントにロールバック
vercel rollback
# 特定のコミットにロールバック
vercel rollback --to=abc123
コスト最適化
1. リソース使用量の監視
bash
# プロジェクトの使用量確認
vercel usage
# チーム全体の使用量
vercel teams usage
2. キャッシュ戦略
javascript
// lib/cache.js
import { kv } from '@vercel/kv'
export class CacheManager {
static async get(key) {
return await kv.get(key)
}
static async set(key, value, ttl = 3600) {
await kv.set(key, value, { ex: ttl })
}
static async invalidate(pattern) {
const keys = await kv.keys(pattern)
if (keys.length > 0) {
await kv.del(...keys)
}
}
}
3. バンドルサイズ最適化
javascript
// next.config.js
module.exports = {
experimental: {
optimizeCss: true,
optimizePackageImports: ['@mui/material', 'lodash', 'date-fns']
},
webpack: (config, { dev, isServer }) => {
if (!dev && !isServer) {
config.optimization.splitChunks = {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
},
common: {
name: 'common',
minChunks: 2,
chunks: 'all',
enforce: true
}
}
}
}
return config
}
}
まとめ
Vercelのエンタープライズ機能を活用することで、大規模なチーム開発と本格的なプロダクション環境を構築できます。特に、SSO、高度な権限管理、統合監視、CI/CDパイプラインの組み合わせにより、エンタープライズレベルの開発・運用が可能です。
これらの機能を適切に設定・運用することで、開発チームの生産性向上とアプリケーションの安定性確保を両立できます。