2025年人気No.1のNext.jsで、最新ツールとウェブを作る！ | エピソード8 パフォーマンスとSEOの最適化

## 目標
- Next.jsでパフォーマンスを向上させる方法を学ぶ。
- `next/image`で画像を最適化する。
- SEOを強化するためにメタデータを追加する。
- Incremental Static Regeneration (ISR)で効率的な更新を実現する。

---

## 1. 画像の最適化
ウェブサイトの読み込み速度を向上させるため、`next/image`を使って画像を最適化します。

### 手順：
1. `public/`にサンプル画像（例：`sample.jpg`）を追加。
2. `app/page.tsx`を編集して画像を表示：
   ```tsx
   import { supabase } from '../lib/supabase';
   import Image from 'next/image';

   export default async function Home() {
     const { data: posts } = await supabase
       .from('posts')
       .select('*')
       .order('created_at', { ascending: false });

     return (
       <div className="min-h-screen flex flex-col items-center bg-gray-100 py-8">
         <h1 className="text-4xl font-bold text-blue-600 mb-8">データベースからの投稿</h1>
         <Image
           src="/sample.jpg"
           alt="サンプル画像"
           width={600}
           height={400}
           className="mb-8 rounded"
         />
         <ul className="space-y-4 max-w-2xl w-full">
           {posts?.map((post) => (
             <li key={post.id} className="bg-white p-4 rounded shadow">
               <a href={`/posts/${post.id}`} className="text-xl font-semibold text-blue-500 hover:underline">
                 {post.title}
               </a>
               <p className="text-gray-600">{post.content}</p>
             </li>
           ))}
         </ul>
       </div>
     );
   }
   ```

**ポイント**：
- `next/image`は自動で画像を圧縮し、遅延読み込み（lazy loading）を適用。
- `width`と`height`を指定してレイアウトシフトを防ぐ。

---

## 2. メタデータでSEOを強化
検索エンジンに最適化されたメタデータを追加します。

### `app/layout.tsx`の更新：
```tsx
import './globals.css';
import Link from 'next/link';
import { auth } from 'next-auth';
import { signIn, signOut } from 'next-auth/react';

export const metadata = {
  title: 'Next.js 2025ブログ',
  description: '2025年の最新技術で作られたブログサイト',
  openGraph: {
    title: 'Next.js 2025ブログ',
    description: 'Next.jsとSupabaseで構築されたモダンなブログ',
    url: 'https://your-site.com',
    images: ['/sample.jpg'],
  },
};

export default async function RootLayout({ children }) {
  const session = await auth();

  return (
    <html lang="ja">
      <body className="font-sans antialiased">
        <header className="bg-blue-800 text-white p-4">
          <nav className="max-w-4xl mx-auto flex justify-between items-center">
            <Link href="/" className="text-xl font-bold">ホーム</Link>
            <div className="space-x-4">
              <Link href="/about" className="hover:underline">About</Link>
              <Link href="/add-post" className="hover:underline">投稿追加</Link>
              {session ? (
                <>
                  <span>{session.user?.name}</span>
                  <button onClick={() => signOut()} className="hover:underline">ログアウト</button>
                </>
              ) : (
                <button onClick={() => signIn('google')} className="hover:underline">Googleでログイン</button>
              )}
            </div>
          </nav>
        </header>
        <main>{children}</main>
        <footer className="bg-gray-200 p-4 text-center text-gray-600">
          © 2025 Next.js学習シリーズ
        </footer>
      </body>
    </html>
  );
}
```

**ポイント**：
- `openGraph`でSNS共有時の表示を最適化。
- 個別ページでは`generateMetadata`を使って動的にメタデータを設定可能。

---

## 3. ISR（Incremental Static Regeneration）の利用
ISRを使えば、静的ページを定期的に更新できます。

### `app/page.tsx`の更新：
```tsx
import { supabase } from '../lib/supabase';
import Image from 'next/image';

export const revalidate = 60; // 60秒ごとに再生成

export default async function Home() {
  const { data: posts } = await supabase
    .from('posts')
    .select('*')
    .order('created_at', { ascending: false });

  return (
    <div className="min-h-screen flex flex-col items-center bg-gray-100 py-8">
      <h1 className="text-4xl font-bold text-blue-600 mb-8">データベースからの投稿</h1>
      <Image
        src="/sample.jpg"
        alt="サンプル画像"
        width={600}
        height={400}
        className="mb-8 rounded"
      />
      <ul className="space-y-4 max-w-2xl w-full">
        {posts?.map((post) => (
          <li key={post.id} className="bg-white p-4 rounded shadow">
            <a href={`/posts/${post.id}`} className="text-xl font-semibold text-blue-500 hover:underline">
              {post.title}
            </a>
            <p className="text-gray-600">{post.content}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}
```

**ポイント**：
- `revalidate`で指定した秒数ごとにページが再生成。
- 静的生成の速度と動的更新の柔軟性を両立。

---

## 4. パフォーマンスの確認
GoogleのLighthouseを使ってパフォーマンスを測定します。

### 手順：
1. Chrome DevToolsを開き、「Lighthouse」タブを選択。
2. `http://localhost:3000`でテストを実行。
3. スコアを確認し、改善点を検討（例：画像サイズの削減、CSSの最適化）。

---

## 実践：最適化されたブログ
- **画像**: `next/image`で最適化されたサンプル画像を追加。
- **SEO**: メタデータで検索エンジンとSNSに対応。
- **ISR**: 投稿リストを60秒ごとに更新。

**確認**：
- Lighthouseスコアが80以上になるように調整。

---

## エピソード8の終了
- パフォーマンスとSEOを最適化しました。
- ウェブサイトがより高速で検索エンジンに優しくなりました。

**次回のエピソード**：Vercelにデプロイし、実際に公開します。

---

この記事が役に立ったと思ったら、ぜひ「いいね」を押して、ストックしていただければ嬉しいです！次回のエピソードもお楽しみに！