2021.01.06

Next on Netlifyプラグインを使ってNext.jsのサーバ機能をNetlifyへデプロイする

こんにちは。F.S.です。

近年、Webフロントエンド界隈ではJamstackによる静的サイトの生成(SSG)&ホスティングが盛んになってきている印象を受けます。筆者はReactの経験はあれどどちらかというとサーバサイドエンジニアをメインにやっていてSSGはそれほど明るくないのですが、サーバレスの潮流もあってこの分野に注目し始めました。

Jamstackサイトのホスティングについては、Jamstackというワードを生み出したパイオニアであるNetlify、Next.jsの開発元であるVercelがありますが、CDN等で著名なCloudflareも同機能を準備しているのではないかという情報もあります。

今回はバージョン10が登場して勢いがあるNext.jsで作られたWebサイトを、相性が良いVercelではなくNetlifyでホスティングするにあたり、2020年1月に初版がリリースされたNext on Netlifyを試してみたいと思います。

1. Next on Netlify プラグインの役割

Next.js で生成される静的サイトをNetlifyにデプロイする上ではプラグインは不要ですが、Next on Netlifyプラグインを使うと

  • SSR(Server-side rendering)
  • ISR(Incremental Static Regeneration)互換機能
  • プレビューモード
などの、Vercelにデプロイするのと同様の機能が実現できるようです。

Netlifyでもサーバ実行環境であるNetlify Functions(AWS Lambdaベース)が利用できるので、個別にセットアップすれば前述のような機能が実現できると思われますが、これを容易にデプロイできるようにするのがこのプラグインの役割になります。

(参考)Announcing one-click install Next.js Build Plugin on Netlify

筆者は Next.js については “ReactサイトをSSRするために必要” くらいの知見しかなく利用するのも初めてなので、まずは公式の学習コースを進めていき、最終的にプラグインを使ったNetlifyへのデプロイを検証することにします。

参考までに、この学習コースではNext.jsの下記機能に触れることができます。

  • ファイルシステムベースのページナビゲーション
  • アセット、メタデータ(headタグ)、CSSの扱い
  • 2種類(SSG, SSR)のプリレンダリングの仕組み
    ※SSRは座学のみで実践なし
  • 外部リソース(ファイルシステムなど)からのデータフェッチ
  • 動的URLへの対応
  • Web APIの作成
  • Vercelへのデプロイ

2. デプロイするWebサイト

学習コースを最後まで進むと、次のようなサイトができます。

yarn build のログよりサイトの構成をみてみます。
サイトトップと2つのブログポストがSSG(●)となり、1つのAPIがSSR(λ)として構成されることがわかります。

% yarn build        
yarn run v1.22.4
$ next build
info  - Creating an optimized production build  
info  - Compiled successfully
info  - Collecting page data  
info  - Generating static pages (4/4)
info  - Finalizing page optimization  

Page                              Size     First Load JS
┌ ● /                             658 B          71.4 kB
├   /_app                         0 B            60.2 kB
├ ○ /404                          2.75 kB        62.9 kB
├ λ /api/hello                    0 B            60.2 kB
└ ● /posts/[id]                   426 B          71.2 kB
    ├ /posts/pre-rendering
    └ /posts/ssg-ssr
+ First Load JS shared by all     60.2 kB
  ├ chunks/commons.286c5a.js      13 kB
  ├ chunks/framework.492e61.js    39.9 kB
  ├ chunks/main.f094e2.js         6.24 kB
  ├ chunks/pages/_app.0d3d3f.js   296 B
  ├ chunks/webpack.95c2b2.js      751 B
  └ css/01f9665b67bf7a10dc45.css  264 B

λ  (Server)  server-side renders at runtime (uses getInitialProps or getServerSideProps)
○  (Static)  automatically rendered as static HTML (uses no initial props)
●  (SSG)     automatically generated as static HTML + JSON (uses getStaticProps)
   (ISR)     incremental static regeneration (uses revalidate in getStaticProps)

✨  Done in 4.02s.

API(/api/hello)はAPI Routes機能を学ぶだけのためのもので、本質的な機能はない(”Hello”を返すだけ)ですが、これが動作するかどうかで Next on Netlify プラグインが効いてるかどうかがわかります。

3. デプロイ検証

3-1. Vercel

Next.js 学習コースの最後がVercelへのデプロイということもあり、まずはVercelで正解を見てみます。VercelはNext.js開発元のホスティングサービスなので、何も気にすることなくNext.jsの機能が活用できます。

/api/hello にアクセスすると、APIのレスポンスが得られます。

3-2. Netlify(プラグインなし)

プラグインを利用しないでNetlifyにデプロイすると、/api/hello は 404 になってしまいます。

補足

Netlify で Next.js SSGをデプロイする際にデフォルトの設定で進めてしまうとデプロイに失敗します。(一見、デプロイ成功に見えますが、サイトにアクセスするとPage Not Foundになってしまいます)

5:52:58 PM: Deploy directory 'dist' does not exist
5:52:58 PM: Failing build: Failed to build site
5:52:58 PM: Failed during stage 'building site': Deploy directory 'dist' does not exist
5:52:58 PM: Finished processing build request in 1m3.373313342s

デプロイログの最後の部分を確認すると、デプロイ設定の公開ディレクトリに指定されている dist ディレクトリが存在しないというエラーが出ています。

こちら記事で解説されているように、デプロイコマンドと公開ディレクトリを修正することでデプロイが成功します。

(参考)Deploy Next.js project to Netlify

3-3. Netlify(プラグインあり)

公式のブログを参考にデプロイします。

(参考)
How to Deploy Next.js Sites to Netlify
Announcing one-click install Next.js Build Plugin on Netlify

実際には下記の設定ファイルをプロジェクトに追加するだけです。

netlify.toml
[build]
  publish = ""
  command = "yarn build"

[[plugins]]
  package = "@netlify/plugin-nextjs"

[[plugins]]
  package = "netlify-plugin-cache-nextjs"
※最後のCacheプラグインは今回の本題とは関係ないですが、これを使ってないとデプロイログにwarningとして出てくるので入れてます。

デプロイログを見ると、サイトのビルド後にプラグインによりサーバサイドのセットアップが行われていることがわかります。

11:24:59 AM: ────────────────────────────────────────────────────────────────
11:24:59 AM:   4. onBuild command from @netlify/plugin-nextjs                
11:24:59 AM: ────────────────────────────────────────────────────────────────
11:24:59 AM: ​
11:24:59 AM: ** Running Next on Netlify package **
11:25:00 AM: 🚀 Next on Netlify 🚀
11:25:00 AM:    Functions directory:  netlify-automatic-functions
11:25:00 AM:    Publish directory:  out
11:25:00 AM:    Make sure these are set in your netlify.toml file.
11:25:00 AM: 🌍️ Copying public/ folder to out
11:25:00 AM: 💼 Copying static NextJS assets to out
11:25:00 AM: 💫 Setting up API endpoints as Netlify Functions in netlify-automatic-functions
11:25:00 AM: 💫 Setting up pages with getInitialProps as Netlify Functions in netlify-automatic-functions
11:25:00 AM: 💫 Setting up pages with getServerSideProps as Netlify Functions in netlify-automatic-functions
11:25:00 AM: 🔥 Copying pre-rendered pages with getStaticProps and JSON data to out
11:25:00 AM: 💫 Setting up pages with getStaticProps and fallback: true as Netlify Functions in netlify-automatic-functions
11:25:00 AM: 💫 Setting up pages with getStaticProps and revalidation interval as Netlify Functions in netlify-automatic-functions
11:25:00 AM: 🔥 Copying pre-rendered pages without props to out
11:25:00 AM: 🔀 Setting up redirects
11:25:00 AM: ​
11:25:00 AM: (@netlify/plugin-nextjs onBuild completed in 134ms)
11:25:00 AM: ​
11:25:00 AM: ────────────────────────────────────────────────────────────────
11:25:00 AM:   5. Functions bundling                                         
11:25:00 AM: ────────────────────────────────────────────────────────────────
11:25:00 AM: ​
11:25:00 AM: Packaging Functions from netlify-automatic-functions directory:
11:25:00 AM:  - next_api_hello/next_api_hello.js
11:25:00 AM:  - next_index/next_index.js
11:25:00 AM:  - next_posts_id/next_posts_id.js
11:25:12 AM: ​
11:25:12 AM: (Functions bundling completed in 12.2s)

/api/hello にアクセスすると、先ほど404だったものが今度はちゃんとAPIのレスポンスが得られました。

補足

プラグインを利用してデプロイすると、Function bundlingの後に下記の依存エラーが出ました(2021年1月2日時点)

6:59:19 PM: ────────────────────────────────────────────────────────────────
6:59:19 PM:   Dependencies installation error                               
6:59:19 PM: ────────────────────────────────────────────────────────────────
6:59:19 PM: ​
6:59:19 PM:   Error message
6:59:19 PM:   A Netlify Function failed to require one of its dependencies.
6:59:19 PM:   If the dependency is a Node module, please make sure it is present in the site's top-level "package.json".
  If it is a local file instead, please make sure the file exists and its filename is correctly spelled.
6:59:19 PM: ​
6:59:19 PM:   In file "/opt/build/repo/netlify-automatic-functions/next_index/next_index.js"
6:59:19 PM:   Cannot find module 'critters'
6:59:19 PM:   Require stack:
6:59:19 PM:   - /opt/buildhome/.netlify-build-nvm/versions/node/v12.16.3/lib/node_modules/@netlify/build/node_modules/@netlify/zip-it-and-ship-it/src/resolve.js
6:59:19 PM:   - /opt/buildhome/.netlify-build-nvm/versions/node/v12.16.3/lib/node_modules/@netlify/build/node_modules/@netlify/zip-it-and-ship-it/src/dependencies.js
6:59:19 PM:   - /opt/buildhome/.netlify-build-nvm/versions/node/v12.16.3/lib/node_modules/@netlify/build/node_modules/@netlify/zip-it-and-ship-it/src/main.js
6:59:19 PM:   - /opt/buildhome/.netlify-build-nvm/versions/node/v12.16.3/lib/node_modules/@netlify/build/src/plugins_core/functions/index.js
6:59:19 PM:   - /opt/buildhome/.netlify-build-nvm/versions/node/v12.16.3/lib/node_modules/@netlify/build/src/commands/get.js
6:59:19 PM:   - /opt/buildhome/.netlify-build-nvm/versions/node/v12.16.3/lib/node_modules/@netlify/build/src/core/main.js
6:59:19 PM:   - /opt/buildhome/.netlify-build-nvm/versions/node/v12.16.3/lib/node_modules/@netlify/build/src/core/bin.js

crittersが見つからないと言われています。crittersはcritical CSS(ファーストビューに必要なスタイルをインラインにして残りをあとでロードするようにする機能)のモジュールのようです。

(参考)Critters

下記コマンドでpackage.jsonのdevelopment dependencyに追加することでエラーは解消しました。

% yarn add -D critters

4. 最後に

今回、NetlifyのプラグインでNext.jsのサーバ機能のデプロイ検証をしました。Netlifyでもサーバ機能の簡単デプロイができるということは確認しましたが、Next.jsにおいてはどうしてもという理由がなければVercelを使うのが最も確実なのかなという印象です。デプロイログにはNetlifyではNext.jsの最新バージョン10の対応はまだ実験的という警告も出てきます。

SSGについては毎回全ページ再ビルドの問題や、動的ページとの共存はどうすれば良いのかという疑問を持っていましたが、Next.jsはSSG、SSRを一つのプロジェクトに混在させることができ、まだ弱いとはいえサーバサイドで必要な機能を取り込んできていることで、この先Next.jsでWebアプリ全体を開発するような時代も来るのかもしれません。

最初はただのViewフレームワークだったはずのReact自体、React Server Componentsというサーバサイドのリソースに直接アクセスするための機能を発表しており、Webアプリケーションの開発がフロントエンド中心に変わっていく方向性は大いにありそうです。

それでは、また。

 

次世代システム研究室では、アプリケーション開発や設計を行うアーキテクトを募集しています。アプリケーション開発者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ 募集職種一覧 からご応募をお願いします。

Pocket

関連記事