SSG と SSR で理解する Next.js のページレンダリング

2021/03/17に公開2

始め

Pre-renderingって全部同じ Pre-rendering じゃなかったですね


1. Pre-rendering

SPAではページをロードする時、まず空の html を読み込んで JS ファイルも読み込んでその JS が画面をレンダリングします。このやり方では SEO でデメリットがあったりファーストビューが遅くなったりする問題があります。

この問題を解決するため、 Next.js は基本的にすべてのページを Pre-rendering します。これはクライエント側の JS がレンダリングする代わりに、各ページに対して html を予め作っておくことを意味します。そうするとパーフォーマンスでも SEO でもより良い結果が出せます。

こうやって生成された html は必要最小限の JS コードに繋げられます。ページがブラウザにロードされるときにその JS コードも実行され、ページは完全にインタラクティブになります(このプロセスを Hydration と呼びます)。

公式サイトから借りてきた絵がすごくわかりやすいです。


何らかのデータを読み込む時、Plain React だとuseEffectを使うと思います。ですが、そうすると下の絵のように最初一瞬空のページがパチっと見えてしまいます。ローディング画面でごまかすこともできますが、Pre-rendering するとファーストビュー自体が早くなるメリットがあります。

もちろんファーストビューが表示されても JS がロードされる前の間はインタラクティブではないという注意点もありますが、画面にコンテンツが表示されるのが早いのでユーザーの離脱を防げる効果があるでしょう。

そしてこの Pre-redndering にも SSG(Static Site Generation)、SSR(Server-side Rendering)の2種類があります。

2. SSG

2-1. SSGとは

SSG は Static Site Generation の略で、言葉通り静的サイトを生成する Pre-redndering です。この方式では html がビルド時に生成され、各リクエストに再利用されます。最初にページがロードされる時にすぐ html を作るということでしょう。

2-2. getStaticProps

もしロードするページが外部データに全然依存していないならデータをフェッチする必要もないのでただ単に静的な html を生成すればよいです。

しかし、外部データに依存する時もあります。公式サイトの例をそのまま借りると、ブログページで記事リストを読み込むときなどが該当します。こういう時はgetStaticProps関数を使います。サンプルコードを見てみましょう。

function Blog({ posts }) {
   return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}

// この関数はビルド時に実行される
export async function getStaticProps() {
  // posts を取得するため外部 API endpoint を読み込む
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // { props: { posts } }を返すことで、
  // Blog コンポーネントはビルド時に`posts`を prop として受け取る
  return {
    props: {
      posts,
    },
  }
}

export default Blog

このように同じファイルでgetStaticPropsを使うと外部データを props として渡すことができます。

2-3. 使う場面

2-1で SSG はビルド時にデータを読み込むと説明しました。これはビルド時1回だけデータフェッチして、ビルドされた後にデータを変更することができないという意味でもあります。ですので、SSG はビルド後にデータを変更する必要がないページに向いてます。

公式サイトによると、「ユーザのリクエストより先にこのページを Pre-rendering することができますか?」の質問に YES の時 SSG を使うようにと言ってます。以下の場合が当てはまります。

  • マーケティングページ
  • ブログ記事
  • ECサイト商品リスト
  • ヘルプページ・ドキュメント

確かにブログの記事やヘルプページなどは最初に読み込んだら別にデータ変更する必要などないのでなるほど、と思いました。

ここでもうちょっと考えてみたらユーザのリクエストなしでレンダリングできない場合に SSG はいい選択ではないことにも気づきます。この場合使える方法の1つは、SSG と CSR(Client-side Rendering)を一緒に使うことです。ページの中で一部だけ Pre-rendering スキップして、そこはクライエント側の JS で読み込む方法です。

もう1つの方法は SSR(Server-side Rendering)を使うことです。

3. SSR

3-1. SSR とは

SSRは Server-side Rendering の略です。この方式では html が各リクエストの度に生成れます

リクエストが起こる度に html を生成するので当然 SSG より遅いですが、Pre-rendering されたページを常に最新状態に維持することができるというメリットがあります。

3-2. getServerSideProps

SSR ではgetServerSidePropsという関数を使います。Next.js 9.3 以前のバージョンで使われてたgetInitialPropsとほぼ同じだと聞きました(私は最近勉強始めたのでよくわかりませんが)。

function Page({ data }) {
  // Render data...
}

// すべてのリクエストの度に実行される
export async function getServerSideProps() {
  // 外部APIからデータフェッチ
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  // props を通じて Page に data を渡す
  return { props: { data } }
}

export default Page

使い方はgetStaticPropsとほぼ同じです。違いはgetServerSidePropsはビルド時ではなくてすべてのリクエストの度に実行されるということです。

3-2. 使う場面

SSR はユーザーのリクエストなしでは Pre-rendering できないページ、最新状態を維持する必要があるページに向いてます。

SNS のタイムラインページを思い出してみましょう。タイムラインはユーザーによってデータが代わり画面も変わるのでレンダリングの前にユーザー情報を読み込む必要があります。ですので、リクエストなしではレンダリングできません。

つまり、タイムラインやマイページなど接続するユーザーによって変わる画面は SSR が適切です。

そしてツイッターを考えてみると何か呟いた時にすぐタイムラインに反映されますね。ページを常に最新状態に維持してるということです。SSR は SSG と違ってビルドされた後にもデータを更新できるので、こういうページに向いてます。

4. まとめ

Pre-renderin には 2種類あると話しましたが、1つに統一する必要はありません。あるページは SSG にして他は SSR にすることができます。

各ページの特性を考えて、向いてる方法でカスタムすればハイブリッドな Nex.js アプリケーションが作れます。

「SSG と SSR の中でどちらを優先して使うべきか?」という問いについては、公式サイトでパフォーマンス的な理由に SSG をおすすめしています。

We recommend using Static Generation over Server-side Rendering for performance reasons. Statically generated pages can be cached by CDN with no extra configuration to boost performance.

SSR はすべてのリクエストの度に処理が走るので、ビルド時に生成したページをキャッシングして再利用する SSG より遅いことは当然です。

しかし…今日のウェブアプリケーションでビルド後にデータ更新しないしユーザーによって画面が変わることもないページはそう多くないのでは?🤔ということが私の正直な感想でした。

However, in some cases, Server-side Rendering might be the only option.

公式サイトでもある場合は SSR が唯一の手かも!と言ってますね


終わり

getStaticPathsは割愛させていただきました^_^

getInitialPropsgetStaticProps(+getStaticPaths)とgetServerSidePropsに細分化されて1年前程立ちましたが、以外に情報があまりなくて結局公式サイトだけ見て記事作成しました。実際はどんな感じで使われてるのか気になりますね。

そしてNext.jsの公式サイトがめっちゃ親切でびっくりしました。短いのにわかりやすい…!

GitHubで編集を提案

Discussion