🎨【保存版】Next.jsのImageコンポーネント完全ガイド！WebPで実現する最適な画像表示📱

# `Next.js`で`WebP`を使用して画像最適化を実現する方法

私は`Next.js`と`tailwindcss`を使用し、フロントエンド開発を行っている初心者です。これまで`PNG`フォーマットを主に使用していましたが、`WebP`の存在を知り、その優れた特性についてもっと理解したいと考えるようになりました。そこで、`WebP`についていろいろ調べる中で得た知識や実践方法を、今回の記事でまとめてご紹介します。

## 目次
1. [はじめに](#はじめに)
1. [基本的な使用例](#基本的な使用例)
1. [複数サイズのWebP画像の生成について](#複数サイズのwebp画像の生成について)
1. [トラブルシューティング](#トラブルシューティング)
1. [発展的な使用例](#発展的な使用例)
1. [まとめ](#まとめ)

## はじめに
`WebP`（ウェブピー）は、Googleが開発した新しい画像フォーマットで、従来の`JPEG`や`PNG`と比較して大幅なファイルサイズの削減を実現しながら、高品質な画像表示を可能にします。これにより、Webサイトの読み込み速度を向上させ、ユーザーエクスペリエンスを大幅に改善することが可能になります。
しかし、一部の古いブラウザでは`WebP`形式をサポートしていないため、互換性の確保が課題となっています。また、開発者にとって`WebP`画像の生成や管理も新たな負担となる可能性があります。
`Next.js`の`Image`コンポーネントを使用することで、これらの課題を効率的に解決し、最適な画像表示を実現できます。今回は、`Next.js`での`WebP`画像の実装方法と、`Image`コンポーネントを活用した画像の最適化手法について、具体的なコード例を交えながら解説します。

## 基本的な使用例

### `Next.js`の`Image`コンポーネントの基本実装

```typescript
import Image from 'next/image'

export default function Hero() {
  return (
    // 最適化された画像コンポーネントを表示
    <Image
      // 画像のソースパス
      src="/hero-image.jpg"
      // 画像の代替テキスト
      alt="Hero image"
      // 画像の幅
      width={1200}
      // 画像の高さ
      height={600}
      // 優先的に読み込むように設定
      priority
      // 画面サイズに応じた画像サイズの指定
      sizes="(max-width: 768px) 100vw, 800px"
    />
  )
}
```

このコードでは、`Next.js`が自動的に画像を`WebP`形式に変換し、ブラウザがサポートしていない場合は元の形式にフォールバックします。

### `Image`コンポーネントのプロパティ

- `src`: 表示する画像のパス
- `alt`: 画像が表示されない場合に表示される代替テキスト
- `width`: 画像の表示幅
- `height`: 画像の表示高さ
- `priority`: 画像の優先度を指定
（ページ表示時に優先的に読み込む必要がある画像に設定）
- `placeholder`: 画像の読み込み中に表示するプレースホルダーを指定
- `quality`: 画像の品質を指定
（デフォルトは75）
- `sizes`: レスポンシブデザインにおける画像表示サイズを指定
（画面幅に応じて異なるサイズの画像を提供）

### `placeholder`属性について

`placeholder`属性は、画像の読み込み中に表示するプレースホルダーを指定するものです。これを使うことで、ユーザーは画像が完全に読み込まれるまでの間、ローディング状態を視覚的に認識できるようになります。`Next.js`の`Image`コンポーネントでは、`empty`（空）または`blur`（ぼかし）のいずれかを指定できます。

- `empty`: プレースホルダーを表示しません
    
    画像が読み込まれるまで何も表示しないため、シンプルなローディング体験を提供します。
    （画像読み込み中は空のスペースを表示）

- `blur`: 画像のぼやけたバージョンをプレースホルダーとして表示します

    `blurDataURL`プロパティと組み合わせて、事前に生成されたbase64形式のぼかし画像を設定する必要があります。
    ローディング中に視覚的な手がかりを与えることで、ユーザー体験を向上させます。
    （静的にインポートした画像の場合は、自動的に`blurDataURL`が生成される）

#### base64データとは？
base64とは、画像などのデータをテキスト形式に変換する方法の一つです。通常、画像はバイナリデータというコンピュータが理解できる形式で保存されています。しかし、Webページで直接バイナリデータを扱うのは難しいので、base64を使ってテキスト形式に変換します。
このテキスト形式に変換されたデータは、URLの中に直接埋め込むことができます。`blurDataURL`では、このbase64形式の小さなぼやけた画像のデータを設定することで、画像読み込み中にぼかし表示を実現しています。
ざっくり、base64データは「画像データを文字で表したもの」と理解すれば大丈夫です。

##### base64データの作成方法
base64データを作成する方法はいくつかありますが、オンラインツールを使用するのが最も簡単です。
画像をアップロードし、生成されたbase64コードをコピーして使用します。

## 複数サイズの`WebP`画像の生成について

`Image`コンポーネントは、`sizes`プロパティに応じて、自動的に複数サイズの`WebP`画像を生成します。
    例えば、`sizes="(max-width: 768px) 100vw, 500px"`と指定した場合、768px以下の画面幅では画面幅いっぱいのサイズで、769px以上の画面幅では500pxのサイズで画像が表示されます。

### `sizes`プロパティの詳細    

`sizes`プロパティは、`srcset`属性と組み合わせて、レスポンシブな画像の表示を制御します。これは、メディアクエリと対応する画像サイズを指定する文字列です。

- `(max-width: 768px) 100vw`: 画面幅が768px以下の場合、画像の幅はビューポートの幅（100vw）になるように指定
- `500px`: 画面幅が769px以上の場合、画像の幅は500pxになるように指定

`sizes`プロパティを使うことで、異なる画面サイズに対して最適な画像サイズをブラウザに選択させることができます。`Image`コンポーネントは、この設定に基づき、自動で適切なサイズの画像を作成し、`srcset`属性にセットしてくれます。

### `srcset`プロパティの詳細

#### `srcset`属性の基本構造と使用方法

`srcset`プロパティは、HTMLの`<img>`タグの属性の一つで、レスポンシブな画像表示を実現するために、ブラウザに複数の画像ソースとそれぞれのサイズを提示するものです。ブラウザは、この`srcset`属性と`sizes`属性の情報を基に、最適な画像を自動的に選択して表示します。`Next.js`の`Image`コンポーネントでは、この`srcset`属性を自動で生成してくれます。

`srcset`属性は、以下のような形式で記述します。

```html
<img
  src="image.jpg"
  srcset="image-320w.jpg 320w, image-480w.jpg 480w, image-800w.jpg 800w"
  sizes="(max-width: 320px) 280px, (max-width: 480px) 440px, 800px"
  alt="Description of the image"
/>
```

上記の例では、`srcset`属性の値はカンマで区切られた複数の画像ソースと幅記述子のペアです。

- `image-320w.jpg 320w`: 幅が320pxの画像image-320w.jpgを指定します。
- `image-480w.jpg 480w`: 幅が480pxの画像image-480w.jpgを指定します。
- `image-800w.jpg 800w`: 幅が800pxの画像image-800w.jpgを指定します。
ブラウザは、`sizes`属性で指定された条件（メディアクエリなど）と`srcset`属性で指定された画像ソースのリストを照らし合わせて、最適な画像を選択します。

#### `Next.js`での`srcset`属性の自動生成

`Next.js`の`Image`コンポーネントでは、`sizes`プロパティを設定すると、この`srcset`属性を自動で生成してくれます。これにより、開発者は複雑な設定をすることなく、レスポンシブな画像表示を簡単に実装できます。

```typescript
import Image from 'next/image';

const MyImage = () => {
  return (
      <div className="relative w-full h-[600px]">
        <Image
          src="/my-image.jpg"
          alt="My image"
          width={800}
          height={600}
          // レスポンシブなサイズ指定
          sizes="(max-width: 320px) 280px, (max-width: 480px) 440px, 800px"     
          className="object-contain"
        />
      </div>
  );
}
```

##### `object-fit`プロパティに関する設定を説明

- `object-contain`: 画像は元の縦横比を維持したまま、親要素内に収まるように拡大縮小される
（画像が親要素を完全に覆うわけではなく、余白ができる場合があります。画像が歪まないように注意する場合に使用）
- `object-cover`: 画像の縦横比を維持したまま、親要素を完全に覆うように拡大縮小する
（画像の一部が、切り取られる場合があります。ヒーロー画像など、要素を完全に覆いたい場合に使用）
- `object-fill`: 画像を親要素に合わせて変形させる
（画像の縦横比が維持されないため、歪んで表示されることがあります。要素いっぱいに表示したい場合に使用）
- `object-none`: 画像を拡大縮小せず、元のサイズで表示する
（画像が親要素からはみ出す場合があります。元のサイズを維持したい場合に使用）
- `object-scale-down`: 画像を縮小して、 object-none または object-contain のどちらか小さい方のサイズで表示する
（画像が大きすぎる場合に縮小するのみ）

## トラブルシューティング

### 1. 画像が表示されない場合の対処
    
    `onError`プロパティを使用して、画像読み込み失敗時の代替表示を設定できます。

    ```typescript
    // 画像表示コンポーネント
    const ImageWithFallback = () => {
      // エラー状態を管理する useState
      const [isError, setIsError] = useState(false)

      return (
        // 三項演算子でエラー状態に応じて表示する画像を切り替え
        !isError ? (
          // エラーがない場合のメイン画像
          <Image
            src="/main-image.jpg"
            alt="メイン画像"
            width={500}
            height={300}
            // エラー発生時にステート更新するハンドラー
            onError={() => setIsError(true)}
          />
        ) : (
          // エラー時の代替画像を表示
          <Image
            src="/fallback-image.jpg"
            alt="代替画像"
            width={500}
            height={300}
          />
        )
      )
    }
    ```

### 2. ドメインエラー
  
    外部画像を使用する場合は、`next.config.js`でドメインを登録する必要があります。

    ```typescript
    // next.config.js
    module.exports = {
      images: {
        // 画像を読み込む許可をするドメインを指定
        domains: ['example.com'],
        // 画像フォーマットの設定
        formats: ['image/webp']
      }
    }
    ```
### 3. サイズエラー
   
    親要素で表示エリアのサイズを決め（`relative w-full h-80`）、その中に画像を広げ（`fill`）、画像の形を崩さずにエリアを埋める（`className="object-cover"`）設定をします。これで画像が、はみ出さずに表示されます。

    ```typescript
    // 親要素に合わせてサイズ調整される画像コンポーネント
    const ImageWithSize = () => {
      return (
        // 画像を包む親要素にrelativeポジションとサイズを設定
        <div className="relative w-full h-80">
          // 親要素いっぱいに広がる画像を配置
          <Image
            src="/my-image.jpg"
            alt="画像"
            // 親要素に合わせてサイズを調整（親要素には`relative`が必要）
            fill
            className="object-cover"
          />
        </div>
      )
    }
    ```
## 発展的な使用例

### 複数の画像を切り替えて表示

```typescript
// 画像切り替えコンポーネント
const ImageSwitcher = () => {
  // 現在の画像インデックスを管理するステート
  const [currentImageIndex, setCurrentImageIndex] = useState(0);

  // 表示する画像のリスト
  const images = [
    '/image1.jpg',
    '/image2.jpg',
    '/image3.jpg',
  ];

  // 次の画像を表示する関数
  const nextImage = () => {
    // インデックスを更新（リストの最後なら最初に戻る）
    setCurrentImageIndex((currentImageIndex + 1) % images.length);
  };

  return (
    <div>
      {/* クリックで画像を切り替えるボタン */}
      <button onClick={nextImage}>次の画像</button>
       {/* 現在の画像を表示 */}
      <div className="relative w-full h-[300px]">
          <Image
            // 表示する画像のパスをリストから取得
            src={images[currentImageIndex]}
            alt={`Image ${currentImageIndex + 1}`}
            fill
            className="object-contain"
          />
      </div>
    </div>
  );
};

export default ImageSwitcher;
```

### ローディング状態を詳細に制御

```typescript
// ローディング制御コンポーネント
const LoadingImage = () => {
  // ローディング状態を管理するステート
  const [isLoading, setIsLoading] = useState(true);

  // 画像の読み込み完了時に実行される関数
  const handleLoadingComplete = () => {
      // ローディング状態をfalseに設定し、ローディングアニメーションを終了させる
    setIsLoading(false);
  };

  return (
    <div>
      {/* ローディング中はメッセージを表示 */}
      {isLoading && <p>Now Loading...</p>}
      {/* ローディング完了後に画像を表示 */}
      <Image
        src="/loading-image.jpg"
        alt="ローディング画像"
        width={500}
        height={300}
        // ローディング完了時に実行する関数を指定
        onLoadingComplete={handleLoadingComplete}
      />
    </div>
  );
};

export default LoadingImage;
```

`onLoadingComplete`プロパティを使うことで、画像の読み込み完了時に特定の処理を実行できます。
（画像の読み込みを完了した際に呼び出される コールバック関数 を設定するためのイベントハンドラープロパティ）

## まとめ
`Next.js`の`Image`コンポーネントを使用した`WebP`画像の最適化は、Webサイトのパフォーマンス向上に大きく貢献します。自動的な形式変換、レスポンシブ対応、遅延読み込みなどの機能により、開発者は複雑な画像最適化を簡単に実装できます。特にモバイルユーザーにとって、ページロード時間の短縮と帯域幅の節約は重要な利点となります。

もし記事の内容に間違いがあれば、コメントでご指摘いただけますと幸いです。また、より良い方法や代替手段をご存知の方がいらっしゃいましたら、ぜひ共有していただければと存じます。例えば、CDNを活用した画像配信やキャッシュ戦略、さらなるパフォーマンス最適化のテクニックなど、皆様の知見やベストプラクティスをお聞かせいただければ幸いです。