mcpField
P
performance

performance

github.com/motormetrics開発2270 回インストール2 日前に更新
リポジトリ

performance について

Web アプリ開発者向けのパフォーマンス改善スキルです。フロントエンド、バックエンド、データベースの測定方法と、一般的な最適化手法を例とともに示します。

主な機能

  1. 01Web Vitals とバックエンドの目標値
  2. 02Lighthouse と k6 による測定
  3. 03コード分割とツリーシェイキング
  4. 04React の再レンダリングとリストの最適化
  5. 05データベースクエリとインデックスの調整
  6. 06Redis と Next.js のキャッシュ

活用シーン

  1. 01Lighthouse で Web アプリを監査
  2. 02k6 で API の負荷テストを実施
  3. 03React の再レンダリングとバンドルサイズを削減
  4. 04遅いクエリを特定してインデックスを追加

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/motormetrics/motormetrics --skill performance

インストール後、Claude Code で/performanceを入力して読み込まれたか確認します。

詳細

パフォーマンススキル

パフォーマンス目標

フロントエンド(Web Vitals): LCP < 2.5s、FID < 100ms、CLS < 0.1、FCP < 1.8s、TTFB < 600ms バックエンド: 応答時間 < 500ms(p95)、コールドスタート < 2s、エラー率 < 1% データベース: クエリ時間 < 100ms、キャッシュヒット率 > 80%

パフォーマンスの測定

# Lighthouse audit
npx lighthouse https://motormetrics.app --view

# Bundle analysis
cd apps/web && ANALYZE=true pnpm build

# Load test with k6
k6 run --vus 100 --duration 5m load-test.js

バンドルサイズの最適化

動的インポート
// ❌ Static import (loads immediately)
import { HeavyComponent } from "./heavy-component";

// ✅ Dynamic import (lazy load)
import dynamic from "next/dynamic";
const HeavyComponent = dynamic(() => import("./heavy-component"), {
  loading: () => <div>Loading...</div>,
  ssr: false,
});
ツリーシェイキング
// ❌ Imports entire library
import _ from "lodash";

// ✅ Import only what you need
import uniq from "lodash/uniq";

React のパフォーマンス

再レンダリングの防止
// useMemo for expensive calculations
const processed = useMemo(() => expensiveOperation(data), [data]);

// useCallback for stable function references
const handleClick = useCallback(() => doSomething(), []);

// React.memo for pure components
const Child = React.memo(function Child({ name }) {
  return <div>{name}</div>;
});
長いリストの仮想化
import { FixedSizeList } from "react-window";

<FixedSizeList height={600} itemCount={items.length} itemSize={100} width="100%">
  {({ index, style }) => <Item style={style} data={items[index]} />}
</FixedSizeList>

データベースクエリの最適化

インデックスの追加
// packages/database/src/schema/cars.ts
export const cars = pgTable("cars", {
  make: text("make").notNull(),
  month: text("month").notNull(),
}, (table) => ({
  makeIdx: index("cars_make_idx").on(table.make),
}));
N+1 クエリの回避
// ❌ N+1 queries
for (const post of posts) {
  post.author = await db.query.users.findFirst({ where: eq(users.id, post.authorId) });
}

// ✅ Single query with relation
const posts = await db.query.posts.findMany({ with: { author: true } });
必要な列のみを選択
// ❌ Selects all columns
const users = await db.query.users.findMany();

// ✅ Select specific columns
const users = await db.select({ id: users.id, name: users.name }).from(users);
ページネーションの使用
const cars = await db.query.cars.findMany({
  limit: 20,
  offset: (page - 1) * 20,
});

キャッシュ

Redis キャッシュ
import { redis } from "@motormetrics/utils/redis";

export async function getCarsWithCache(make: string) {
  const cacheKey = `cars:${make}`;
  const cached = await redis.get(cacheKey);
  if (cached) return JSON.parse(cached as string);

  const cars = await db.query.cars.findMany({ where: eq(cars.make, make) });
  await redis.set(cacheKey, JSON.stringify(cars), { ex: 3600 });
  return cars;
}
Next.js のキャッシュ
// Revalidate every hour
export const revalidate = 3600;

// Or use fetch with caching
const data = await fetch(url, { next: { revalidate: 3600 } });

画像の最適化

import Image from "next/image";

// ✅ Optimized image with priority for above-fold
<Image
  src="/hero.jpg"
  alt="Hero"
  fill
  sizes="(max-width: 768px) 100vw, 50vw"
  priority
/>

Vercel Function の最適化

// API route with optimized config
export const config = {
  maxDuration: 60, // seconds
};

// Use edge functions for low-latency endpoints
export const runtime = "edge";

プロファイリング

パフォーマンスミドルウェア
// apps/api/src/middleware/performance.ts
export const performanceMiddleware = async (c: Context, next: Next) => {
  const start = performance.now();
  await next();
  const duration = performance.now() - start;

  c.header("X-Response-Time", `${Math.round(duration)}ms`);

  if (duration > 1000) {
    log.warn("Slow request", { path: c.req.path, duration: Math.round(duration) });
  }
};
クエリのプロファイリング
const start = performance.now();
const result = await db.query.cars.findMany();
const duration = performance.now() - start;

if (duration > 100) {
  console.warn(`Slow query: ${duration}ms`);
}
Vercel Analytics

Vercel Dashboard → Analytics で以下を確認します。

  • 関数の実行時間
  • エッジの応答時間
  • Web Vitals の指標
  • リクエスト数とエラー

負荷テスト

// load-test.js (k6)
import http from "k6/http";
import { check } from "k6";

export const options = {
  stages: [
    { duration: "2m", target: 100 },
    { duration: "5m", target: 100 },
    { duration: "2m", target: 0 },
  ],
  thresholds: {
    http_req_duration: ["p(95)<500"],
    http_req_failed: ["rate<0.01"],
  },
};

export default function() {
  const res = http.get("https://api.motormetrics.app/api/v1/cars/makes");
  check(res, { "status 200": (r) => r.status === 200 });
}

パフォーマンスチェックリスト

  • バンドルサイズ < 200KB(初回読み込み時)
  • LCP < 2.5s、FID < 100ms、CLS < 0.1
  • API の応答 < 500ms
  • データベースクエリ < 100ms
  • 画像を最適化(WebP/AVIF)
  • コード分割を実装
  • キャッシュ戦略を導入
  • 長いリストを仮想化
  • 圧縮を有効化

すぐにできる改善

  1. 圧縮:gzip/brotli を有効化(サイズを 60~80% 削減)
  2. 画像の最適化:Next.js Image コンポーネントを使用
  3. キャッシュ:負荷の高い処理を Redis にキャッシュ
  4. インデックス:頻繁にクエリされる列にインデックスを追加
  5. コード分割:負荷の高いコンポーネントを遅延読み込み
  6. メモ化:負荷の高い処理に useMemo/useCallback を使用

参考資料

コメント