Next.jsについて調べてみる

Next.jsについて調べてみる Programming

はじめに

この記事はZennのスクラップで書いた内容をそのまま転記したものになります。→ こちら

まとめ記事もいいのですが、調べてる途中の思考も参考になったりするんじゃないかなと思ったので、あえてそのまま転記しています。

調べる

1. Next.jsは、(もう)SSRするだけのフレームワークではない

  • Reactのフレームワーク
    • CRA(create-react-app)と同じようにWebpackやBabelなどのライブラリを搭載している
  • 現在の最新バージョンは10.0.1
  • SSR・SSGができる
  • 動的ルーティングに対応している
  • ISR(Incremental Static Regeneration)によって、SSGに対して重きを置いている
  • 現時点でのNext.jsの主な用途として2つ選択肢がある
    • SSRに対応した汎用的なReact製フレームワーク
    • React製のSSGツール

とまぁなんとなくこんな感じらしいけど、そもそもSSRとかSSGとかの理解が微妙ということで、ググる。

この記事がわかりやすかった。

CSR(Client Side Rendering)

  • CSRはSPA(Single Page Application)とほぼ同意義
  • ブラウザからリクエストを受けると、ビルドされたJSファイルと最小限のHTML要素しか含まれないファイルを返却する
  • 初期表示は何もされないので、ブラウザ上でHTML要素をレンダリングする

メリット

  • ページ遷移ごとにリクエストが発生しないので高速
  • HTMLとJSファイルのみがホスティングサーバーにあれば、ページ配信が可能

デメリット

  • 初期ローティングに時間がかかる
  • 最小限のHTML要素しかないので、SEOで不利な場面がある(最近のクローラーは解析してくれるらしい?)
  • 動的なOGP対応できない場合がある

SSR(Server Side Renderring)

  • ブラウザからリクエストを受けると、初期ページだけサーバー側でレンダリングをしHTMLファイルを返却する
  • 初期以外のデータはAPIなどを用いて取得をし、ブラウザ上でレンダリングをする

メリット

  • サーバー上でレンダリングをするので、初期表示がCSRに比べて早い
  • SEOが向上する
  • 動的なOGP対応ができる

デメリット

  • サーバー側でレンダリングをするのでWebサーバーが必要になる

SSG(Static Site Generator)

  • アプリケーションをビルドする時にあらかじめデータを取得しHTMLを最初に生成する。
  • ブラウザからリクエストを受けると生成済みのHTMLファイルを返却する
  • 先にサーバーでビルドする時にデータを取得してレンダリングすることを「プリレンダリング」という
  • 生成された各HTMLはそのページに必要な最小限のJavaScriptコードと関連付けられ、ブラウザによってページが読み込まれるとそのページのJavaScriptコードが実行される(このプロセスをハイドレーションという)

メリット

  • 静的サイトを配信するためレスポンスが高速
  • SEOが向上する
  • 動的なOGP対応ができる
  • HTMLとJSファイルのみがホスティングサーバーにあれば、ページ配信が可能

デメリット

  • ページ量が多い・頻繁に更新がある・APIを頻繁にリクエストするサイトには向かない
  • ページ数が多くなればなるほどビルド時間が遅くなる

なるほど🤔
ここまでみると、用途によってはSSGが良さそうだけど、SSRの方が使い勝手がいい気もするな。

とりあえず基本的なことがざっとわかったので、また読み進めてみる。

2. Next.jsでは、基本的にServer-side Renderingをするべきでない

いきなり、SSRをするべきではないときた。
理由として以下の3つがあるとのこと。

  1. SSR用のサーバーを持たなければならない
  2. サーバーサイドのセキュリティの危険性をWebフロントエンジニアがかかえなければならない
  3. パフォーマンスの観点でもSEOの観点でも、SSGで十分、むしろSSGの方がより優れている

解説をみると、ざっとこんな感じ。

  • サーバーを持つこと自体がデメリット
    • サーバーサイドに対する知見や経験の少ないフロントエンジニアだと、サーバーを建てるのは危険
    • CDNのキャッシュ設定ミスによって秘匿情報が含まれたページが漏洩する恐れがある

1, 2 はまぁデメリットではあるけど、今回気になってるのは3だな。🤔

優れている理由がまだでてないので、とりあえず再度読み進める。

そもそもNext.jsの開発元のVercelがSSGを推奨しているらしい。

その根拠はNext.js 9以降からSSG周りの機能を強化しているから、Vercelの思想がそのままNext.jsにあらわれているとのこと。

この時点では、まだSSGの方がいいってのはわからないな。

で、まぁすすめると、Next.jsは静的ファイルのビルド方法として2種類の方法を設けているらしい。

  • Static Build
    • コンポーネントなどをBabel/Webpackを通して静的ファイルに変換する
  • SSG
    • コンポーネント内で取り扱う具体的なデータを含めた静的ファイルに変換する

で、動的コンテンツをSSGする方法としていくつか例がある。

  • getStaticPropsというSSG専用のビルド前に呼ばれるライフサイクルメソッドを使用する
    • 例えば、このメソッドで記事一覧を取得するAPIを叩き、コンポーネントのPropsにその内容をわたし、HTMLを生成する
  • 記事一覧ではなく詳細ページのように動的ルーティングを前提とした場合は、getStaticPathsというgetStaticPropsよりも先に実行されるメソッドを使用する
    • 事前ビルド時に生成すべきURLを列挙させ、対応するHTMLを生成する
    • 詳細ページを生成する場合、事前にすべての記事のIDを取得できるAPIを作成するのも1つの手である
  • 膨大な動的コンテンツは事前ビルド時にフェッチしたりビルドすることはできないため、 ISR(Incremental Static Regeneration) を利用する
    • ISRは動的コンテンツを事前ビルドせずに、初めてページにアクセスした時にビルドするというもの
    • また、ビルドした内容に有効期限を設けることができ、有効期限を過ぎたページにアクセスされた場合は、前回ビルドされたコンテンツを返しつつも、バックグラウンドで再ビルドされることとなる
    • 膨大な動的コンテンツを含むWebサービスも、コンテンツが頻繁に追加されるWebサイトでも、静的ビルドさせることが可能となる。(事前ビルドではない)

ここでは具体的なビルドの方法を書いてくれてる。
ちょっとISRについてもうちょい調べてみる。

ISRとは

動的コンテンツを“事前”ビルドせずに、初めてページにアクセスしたときにビルドするというものです。

段階的な静的サイトのように訳されていて、SSGのように事前に全てのページを生成するのではなく1度アクセスされた際にレスポンス内容が生成され、次回以降そちらの内容がレスポンスされます。

Next.js の Incremental Static Regeneration を理解する より引用

過去に生成したページであっても、一定期間を経た後に再生成することができるようになります。

getStaticPropsでrevalidate期間を返すことで期間を設定し、ページが生成されてから設定した期間を超えると再生成されます。

Next.js 9.4 Fast RefreshとIncremental Static Regeneration等のアップデート – Qiita より引用

SSGのデメリットを補うもの

  • 静的なページを生成する際にページ数が多いとビルドに時間がかかる
    → アクセス時に初めて生成されるので初回ビルドが高速
  • 1度しかビルドしないので、再度全てのページをビルドしなおさないと内容が更新されない
    → 再度アクセスがあった際に次回以降の内容をビルドするため内容が更新される

ISRを利用することでSSGのデメリットを補うみたいな感じっぽいけど、まだよくわかってない🤔
アクセスがあったときにビルドをするってサーバーが必要ってこと?

今となっては、SSRのメリットはほとんどない

Vercelも述べるように、ビルドによって生成された静的コンテンツのキャッシュは、オンデマンドに生成される動的コンテンツのキャッシュと比べて、格段に容易になります。CDNとの相性もよく、エッジロケーションから直接クライアントまでコンテンツを配信できるようになります。さらに言ってしまえば、『Amazon S3』のようなオブジェクトストレージにビルドされたファイルを置くだけでデプロイが完了します。

静的ビルドされるということは、HTMLファイル内にコンテンツの内容が含まれることになります。これは(OGPのためも含む)SEOの面でも、パフォーマンスの面でも良い結果をもたらしてくれるでしょう。とりわけパフォーマンスの向上目的でSSRを導入しているプロジェクトにおいては、First Viewパフォーマンスの観点で事前ビルドに敵う余地はありません。

なるほど、ここみると確かにSSGの方が優れてそうだな。
SSGではサーバーを持つというデメリットもないし、ISRでSSGのデメリットも補われてるって訳か。(ISRの理解がまだ微妙。)

あとは認証が必要なページの場合はSSG不要とのこと。

また「認証が必要なページのSSGはどうするの?9」と疑問に思うかもしれません。 getStaticProps から返すPropsに決め打ちでユーザIDなどを入力することは要件上できませんし、そのライフサイクル内でCookieを参照することもできません。すなわち、認証が必要なページのSSGはできないわけです。逆に、認証が必要なページのSSGは不要と言えるでしょう。その代わり getStaticProps などを用いないStatic Build─認証処理はWebブラウザでオンデマンドに処理して、事前ビルド時にはコンポーネント構造だけをHTML化する─という方法があります。Next.jsにおける、従来のSSGです。

そもそも認証が必要なページには秘匿情報が含まれていますから、SEOさせる必要がありません。秘匿情報をHTMLに埋め込まなければ、検索エンジンのクローラに収集されることもありませんし、CDNによる個人情報のキャッシュミスなども起こりません。コンテンツが埋め込まれていない状態でのコンポーネントの静的ビルドは済んでいますから、First Viewパフォーマンスに対する影響も大きくないはずです。

ここまで読んできて、なんとなくSSGの方が良さげってのはわかった。

ちょっといまひとつなところもあるけど、そのまま進む。

3. Next.jsは、『Vercel』でのホスティング前提のフレームワークだと認識する

Next.jsはISRを利用できるようになったり、最近ではオンデマンドでの画像最適化に対応したのでAPIで取得したURLの画像ファイルすら対応できるようになったりとしてますと。

で、

一方でこれらの機能はNext.js単体で完結するかと言うと、そうではありません。2章では散々「Webフロントエンドエンジニアがサーバを持つな」なんて述べましたが、結局サーバなしではISRによる静的ビルドも画像最適化のための画像生成も行うことはできません。そこで、必然的に利用すべきサービスとなるのが『Vercel(ヴァーセル)』です。

やっぱサーバーいるんだよね。😮

VercelはDNSやCDNも展開しているホスティングサービスで、サービス名と同じ企業名の「Vercel」が開発してる。
Next.jsもVercelが開発してるから親和性が高いのかな?

Next.jsは『Vercel』と併用して初めて“完成”するフレームワーク

詳しくは省くけど、Next.jsの色んな機能の設定を自動的にしてくれるとか。
また、ISRに対応したプラットフォームがVercelしかないため、事実上Next.jsはVercelでしか使えないらしい。

Next.jsもVercelも同じVercel(企業名)が開発してるし、Next.jsの新機能もいち早く対応するサービスだから事実上VercelありきでNext.jsを使うってことかな?

Next.jsは『Vecel』というサービスを利用して初めて完成するフレームワークであると言えます。Next.jsを使うということは、“『Vercel』に依存する”ということが前提になることを強く意識してください。

それでもNext.jsを他プラットフォームでホスティングしたい場合

VercelはSSRするページと静的ページを適切な場所にデプロイもしてくれるらしい。

SSRを使用しないもしくはSSGを使用しないとか色々制限すればVercel以外でも利用できるらしいけど、そこまでして他のサービスを使うメリットはわからないな。

余談:1ページ1サーバレスファンクションの戦略

Next.jsは1ページごとにJSファイルが作成され、初回アクセス時にそのページ以外の情報を読み込む必要がなくなるため、First Viewパフォーマンスが向上する。

Vercelはページ単位ごとに1つのサーバーレスファンクションに配置している。
理由としては

  • コールドスタートの時間を小さくする(プログラムの容量を削減しているため)
  • 特定ページのレンダリングのみ担うという形式に絞ることで、システム全体の負荷を分散させる
    • 一般的に高性能なサーバーを用意するよりも、そこそこの性能のサーバーを複数用意する方が費用対効果が高いとされている

Vercelを利用することで初めてNext.jsの色んな機能が使えるって感じだな。

4. Next.jsでAPIサーバを建てない/『Vercel』でAPIサーバを建てない

  • Next.jsはバージョン9からAPIルーティングに対応した。
    • Next.js内でNode.jsのWeb APIサーバーを作成できる
  • APIルーティングは動的ルーティングに対応している
    • /pages/api/users/[userId].jsというファイルを作成すれば/api/users/123のURLに対応したAPIの実装が可能

Next.jsでサーバーまで簡潔できるならすごくいいと思うけど、避けた方がいいらしい。

Web APIフレームワークではないので、機能的に貧弱

  • あくまでReactを用いることが前提のフレームワークなので、APIルーティングはおまけ程度
    • リクエストメソッドのフィルタリングやバリデーションなども自前で用意する必要がある
  • 本格的なWeb APIサーバーを開発する場合は、Node.jsに特化したフレームワークの「NextJS」や、Next.jsを拡張した「Blitz.js」を利用するのが良い

サーバレスファンクションとRDSの相性が悪い

  • Next.jsはサーバーレスファンクションを前提とした設計していて、大量の台数までスケールアウトされるサーバーレスファンクションとRDBMSはコネクション数の点で相性が悪い

『Vercel』のサーバレスファンクションと帯域制限の相性が悪い

  • ファイルアップロードが可能なサービスの場合は、すぐに帯域制限の上限に達成してしまう
    • そのため別途ブラウザから他のストレージへアップデート処理を用意しなければならなくなる
    • その場合ストレージとDBのトランザクション管理が必要になるため、実装/管理コストがかかる
  • Vercelのプランをアップグレードすれば済むかも知れないが、1ユーザーあたり月40ドルを払う必要があるので、小規模の場合はアップグレードは現実的ではない

なるほど。

APIサーバーあって動的ルーティングが可能だけど

  • 機能的に貧弱
  • 自前でバリデーション機能などを用意する必要がある
  • RDBMSとコネクション数の点で相性が悪い
  • ファイルアップロードを利用しようとするとVercelのプラン的にすぐに上限に達成してしまう

って理由からAPIサーバーは自前で用意しようねってことですかね。

5. 『Vercel』を使うなら next dev ではなく vercel dev を用いる

  • Vercelを利用する場合、ローカル環境の開発ではnext devコマンドではなく、vercel devコマンドを使用する
    • vercel devnext devと同じくHMRなどのリロード機構を備えていて、vercel.jsonの設定に応じて Vercelの挙動をエミュレートしてくれる
      • HMR(Hot Module Replacement)とは、Webpackの提供する仕組みで、ページ全体を更新しなくても変更したJSのみをブラウザに適用してくれる機能
      • これは、変更を行ってもアプリケーション内の状態(ステート)は保持されることを意味します
  • 新たにリリースされたvercel env pullコマンドを利用することで、Vercelの管理画面から設定した開発時向けの環境変数をプルすることができる

開発環境では常にSSRされることに留意する

  • Next.jsを使う以上は、vercel devでもnext devでも開発環境では常にSSRされる

SSGでも開発時はSSRになるらしい

まとめ

項目6からはちょっとNextの細かい話になっているので、ここで垂れ流すNext.jsとは?というのは一旦ここまでとする。

ざっくりまとめると、

  • Reactのフレームワークで、SSRとSSGに対応している
  • SSRよりもSSGの方がパフォーマンスやSEOで優れている
  • またISRによってSSGのデメリットも補われ、かつ開発元のVercelもSSGを推奨している
  • Next.jsのメリットである機能などはVercelを利用しないと使えないものもあり、また開発元が同じで親和性が高いことから、ホスティングサービスとしての利用が前提となる
  • APIサーバーの機能も搭載しているが、機能が貧弱だったりその他理由からAPIサーバーとしては使用しない方がよい

とまぁざっくり色々調べてみてきた感じだいぶ理解が進んだ。
これからは実際にNext.js触って勉強していこうと思います。