Next.js getServerSidePropが動作しない原因と解決方法【初心者向け完全ガイド】

React / Next.js

Next.js getServerSidePropsが動作しない原因と解決方法

Next.jsを使用していて、getServerSidePropsがうまく動作しないという問題に直面した経験はありませんか?サーバーサイドレンダリング(SSR)を実装する際に非常に便利な機能ですが、いくつかの注意点があります。本記事では、getServerSidePropsが機能しない原因と具体的な解決方法を初心者向けに解説します。

そもそもgetServerSidePropsとは

getServerSidePropsはNext.jsの機能の一つで、ページがリクエストされるたびにサーバー側でデータを取得し、そのデータをページコンポーネントに渡す関数です。SEO対策が必要な場合や、常に最新のデータを表示する必要がある場合に重宝します。

getServerSidePropsが動作しない主な原因

原因1:ファイルの配置場所が間違っている

getServerSidePropsは「pages」ディレクトリ内のファイルにのみ記述できます。これはNext.jsの重要なルールです。もしも「components」ディレクトリや「utils」ディレクトリに記述した場合、この関数は実行されません。

正しい構造:

project/
├── pages/
│   ├── index.js
│   ├── posts/
│   │   └── [id].js  ← getServerSidePropsはここに記述
│   └── about.js
├── components/
└── utils/

原因2:関数をエクスポートしていない

getServerSidePropsは必ず「named export」でエクスポートする必要があります。default exportでエクスポートすると、Next.jsが認識できません。

原因3:開発サーバーを再起動していない

getServerSidePropsを追加または変更した後、開発サーバーを再起動しないと変更が反映されません。

原因4:動的ルートの設定ミス

動的ルート([id].jsなど)でgetServerSidePropsを使用する場合、paramsの処理を忘れると問題が生じます。

原因5:非同期処理の誤り

getServerSideProps内でAPIを呼び出す場合、Promiseの処理が完了するまで待機する必要があります。

原因別の解決手順

解決手順1:ファイル配置の確認と修正

まず、getServerSidePropsを記述しているファイルが「pages」ディレクトリ内にあることを確認してください。別のディレクトリに配置している場合は、pagesディレクトリに移動させます。

解決手順2:エクスポート形式の確認

以下のようにnamed exportを使用してください。

// 正しい方法
export async function getServerSideProps() {
  return {
    props: {}
  }
}

// 間違った方法(これではダメ)
export default function getServerSideProps() {
  return {
    props: {}
  }
}

解決手順3:開発サーバーの再起動

ターミナルで以下を実行してサーバーを再起動します。

npm run dev

解決手順4:ブラウザキャッシュのクリア

ブラウザのキャッシュが原因の場合もあります。DevToolsを開き、キャッシュを無効化してリロードしてください。

実践的なコード例

基本的な使用例

// pages/posts.js
export default function Posts({ posts }) {
  return (
    <div>
      <h1>ブログ記事一覧</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  )
}

export async function getServerSideProps() {
  try {
    const response = await fetch('https://api.example.com/posts')
    const posts = await response.json()

    return {
      props: {
        posts
      },
      revalidate: 60 // ISR: 60秒ごとに再検証
    }
  } catch (error) {
    console.error('Error fetching posts:', error)
    return {
      notFound: true // 404ページを表示
    }
  }
}

動的ルートの例

// pages/posts/[id].js
export default function Post({ post }) {
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  )
}

export async function getServerSideProps({ params }) {
  try {
    // paramsからidを取得
    const { id } = params

    const response = await fetch(`https://api.example.com/posts/${id}`)
    
    // ステータスコードが404の場合
    if (response.status === 404) {
      return {
        notFound: true
      }
    }

    const post = await response.json()

    return {
      props: {
        post
      }
    }
  } catch (error) {
    return {
      notFound: true
    }
  }
}

クエリパラメータの取得例

// pages/search.js
export default function Search({ results }) {
  return (
    <div>
      <h1>検索結果</h1>
      {results.length === 0 ? (
        <p>結果が見つかりません</p>
      ) : (
        <ul>
          {results.map((item) => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
      )}
    </div>
  )
}

export async function getServerSideProps({ query }) {
  const { keyword } = query

  if (!keyword) {
    return {
      props: {
        results: []
      }
    }
  }

  try {
    const response = await fetch(
      `https://api.example.com/search?q=${encodeURIComponent(keyword)}`
    )
    const results = await response.json()

    return {
      props: {
        results
      }
    }
  } catch (error) {
    return {
      props: {
        results: []
      }
    }
  }
}

リダイレクトの例

// pages/old-page.js
export default function OldPage() {
  return <div>このページは表示されません</div>
}

export async function getServerSideProps() {
  // 別のページにリダイレクト
  return {
    redirect: {
      destination: '/new-page',
      permanent: true // 301リダイレクト
    }
  }
}

よくある間違いと対処法

間違い1:getServerSidePropsでlocalStorageを使用

サーバーサイドで実行される関数のため、ブラウザのAPIであるlocalStorageは使用できません。

// ❌ 間違い
export async function getServerSideProps() {
  const token = localStorage.getItem('token') // エラー
  return {
    props: {}
  }
}

// ✅ 正解
export async function getServerSideProps({ req }) {
  const token = req.cookies.token // クッキーから取得
  return {
    props: {
      token
    }
  }
}

間違い2:propsの値が非シリアライズ不可の場合

propsに渡すデータはJSON形式でシリアライズ可能である必要があります。関数やDateオブジェクトはそのまま渡すことはできません。

// ❌ 間違い
export async function getServerSideProps() {
  return {
    props: {
      date: new Date(),
      callback: () => {} // 関数はシリアライズできない
    }
  }
}

// ✅ 正解
export async function getServerSideProps() {
  return {
    props: {
      date: new Date().toISOString(), // 文字列に変換
      timestamp: new Date().getTime() // 数値に変換
    }
  }
}

間違い3:APIの応答時間を考慮していない

外部APIの呼び出しが遅い場合、ページの読み込み時間が長くなります。タイムアウトを設定することが重要です。

// ✅ タイムアウト付きAPI呼び出し
export async function getServerSideProps() {
  try {
    const controller = new AbortController()
    const timeoutId = setTimeout(() => controller.abort(), 5000) // 5秒でタイムアウト

    const response = await fetch('https://api.example.com/data', {
      signal: controller.signal
    })

    clearTimeout(timeoutId)
    const data = await response.json()

    return {
      props: { data },
      revalidate: 60
    }
  } catch (error) {
    return {
      props: { data: null },
      revalidate: 10 // エラー時は短い間隔で再試行
    }
  }
}

間違い4:環境変数へのアクセスミス

クライアントサイドではNEXT_PUBLIC_プレフィックスが必要ですが、getServerSidePropsではすべての環境変数にアクセスできます。

// pages/api-test.js
export default function Page({ apiKey }) {
  return <div>APIキー設定済み</div>
}

export async function getServerSideProps() {
  const apiKey = process.env.SECRET_API_KEY // プレフィックスなしでアクセス可能

  return {
    props: {
      apiKey
    }
  }
}

間違い5:getStaticPropsとの混在

同じページでgetServerSidePropsとgetStaticPropsの両方を定義することはできません。どちらか一方を選択する必要があります。

// ❌ エラー(両方は定義できない)
export async function getStaticProps() {
  return { props: {} }
}

export async function getServerSideProps() {
  return { props: {} }
}

// ✅ どちらか一方を選ぶ
export async function getServerSideProps() {
  return { props: {} }
}

デバッグ方法

コンソールログを活用

getServerSideProps内でconsole.logを使用すると、ターミナルに出力されます(ブラウザの開発者ツールには表示されません)。

export async function getServerSideProps() {
  console.log('getServerSideProps実行中...') // ターミナルに表示
  const data = await fetchData()
  console.log('取得データ:', data)

  return {
    props: { data }
  }
}

ネットワークタブで確認

ブラウザの開発者ツールのNetworkタブを確認して、APIリクエストが実際に送信されているか確認しましょう。

パフォーマンス最適化のポイント

getServerSidePropsを使う際は、以下の点を考慮してパフォーマンスを最適化してください:

  • キャッシング戦略:可能な限りISR(増分静的再生成)の使用を検討
  • APIの最適化:複数のAPIコールが必要な場合は並列実行
  • エラーハンドリング:APIエラー時のフォールバック処理を実装
  • リクエストタイムアウト:長時間の処理は避ける
// 複数のAPIを並列実行
export async function getServerSideProps() {
  try {
    const [postsRes, usersRes] = await Promise.all([
      fetch('https://api.example.com/posts'),
      fetch('https://api.example.com/users')
    ])

    const posts = await postsRes.json()
    const users = await usersRes.json()

    return {
      props: {
        posts,
        users
      },
      revalidate: 300
    }
  } catch (error) {
    return {
      notFound: true
    }
  }
}

まとめ

Next.jsのgetServerSidePropsが動作しない問題は、以下の原因がほとんどです:

  • ファイルがpagesディレクトリにない:必ずpagesディレクトリに配置
  • エクスポート形式の誤り:named exportを使用
  • サーバーの再起動:コード変更後は必ず再起動
  • 非シリアライズ可能なデータ:propsに渡すデータはJSON形式で
  • 環境の設定ミス:環境変数の参照を確認

これらのポイントを押さえれば、getServerSidePropsの問題の大半は解決できます。開発を進める際には、ターミナルのログとブラウザの開発者ツールを活用して、段階的にデバッグすることをお勧めします。

また、最近のNext.jsではgetServerSidePropsよりもApp Routerを使用した実装が推奨されています。プロジェクトの特性に応じて、最適な方法を選択してください。

タイトルとURLをコピーしました