Next.jsアプリを[email protected]で超簡単にSSRできるserverless-next.jsのご紹介


Next.js をSSR対応でホスティングしたいなら Vercel(旧Now) を使うのが定石だと思います。
Next.jsとVercelはどちらも Vercel社 (旧ZEIT社) が開発しているため一緒に使った際のDXは大変良いものです。
しかし、AWS内で完結できるならそうした方が嬉しい事が多いのでいい方法はないかと調べた所、
serverless-next.jsというServerless Componentのプラグインがよさそうだったのでご紹介したいと思います。

以下のドキュメントの要点をまとめつつ、掘り下げたものです。
https://github.com/danielcondemarin/serverless-next.js/
https://serverless.com/blog/serverless-nextjs/

serveless-next.js

機能と特徴

  • SSRを[email protected]で行う
  • Dynamic Routing (Next.js 9からの機能)と互換性のあるルーティングを行う
  • Automatic pre-rendering (Next.js 9からの機能) のサポート

    • SSR不要なページはNextによって静的HTMLに書き出される
    • serverless-next.jsはこれをS3にホスティングする
    • アクセスがあると[email protected]がS3にリクエストをフォワードする
  • Nextが生成したファイル(jsのチャンクファイル等)のホスティング

  • static フォルダ内のアセットのホスティング

    • 上記と同様CloudFrontを通してS3でホスティングする

これらの機能をゼロコンフィグで行える。

デプロイについて

  • pageは[email protected]に、静的ファイルはS3にデプロイされる。
  • デプロイは通常1分以内に終わる。
  • 初回のみディストリビューションの有効化に時間がかかるが、それ以降は高速に終わる。

アーキテクチャ

Cloud Frontに3つのキャッシュビヘイビアが作成される。

Defalut(*) の挙動

① SSRされるページ(getInitialPropsありのページ) を処理する
② Automatic pre-renderingされたHTMLページをS3にフォワード
③ favicon.ico などのルート直下のファイルをS3にフォワード

②,③をする理由

ファイルごとにディストリビューションを作成しないため。
(ちなみにCloudFrontは最大25個しかディストリビューションを作れない)

デプロイする

serverless.yml
myApp:
 component: serverless-next.js
デプロイ
$ npx serverless

削除
$ npx serverless remove

これだけでとりあえず https://<文字列>.cloudfront.net のようなURLでアクセス可能な状態になる。

ドメインを割り当てたいなら

serverless.yml
myApp:
  component: serverless-next.js
  inputs:
    domain: "example.com"
    # domain: ["sub", "example.com"] # サブドメインもこんな感じで指定できる

舞台裏

CloudFormationは使ってません

  • 高速なデプロイのため
  • CloudFormationは記述できるリソースが200個までと制限がある

ほとんどの作業はserverless-component の以下の4つが行っており、
serverless-next.js はこれらのオーケストレーションを単純に行っているだけ。

1つ懸念があるとすると

serverless-next.js自体はbetaではないが、その裏側のserverless-compoentがよく見るとbetaではある。
正式リリースされた模様です

実際にリソースの作成を行っている箇所のソースを見てみる

serverless-nextjs-component/serverless.js

serverless-componentはymlによるCloud Formationによる管理だけでなく、
プログラマティックにリソースを操作することも可能。
例えば S3の例は以下。

const bucket = await this.load('@serverless/aws-s3')

// deploy
await bucket({
  accelerated: true
})

// upload directory
await bucket.upload({ dir: './my-files' })

// upload file
await bucket.upload({ file: './my-file.txt' })

S3のリソース作成
https://github.com/danielcondemarin/serverless-next.js/blob/master/packages/serverless-nextjs-component/serverless.js#L272

Cloud Frontのリソース作成
https://github.com/danielcondemarin/serverless-next.js/blob/master/packages/serverless-nextjs-component/serverless.js#L407

[email protected]のリソース作成
https://github.com/danielcondemarin/serverless-next.js/blob/master/packages/serverless-nextjs-component/serverless.js#L391

その他メモ

デプロイの度にCloudFrontディストリビューションをdestroy/createせず、updateしたい場合

.serverless ディレクトリでリソースのstateが管理されており、これを一緒にgitでcommitしておく必要がある。

※ ちなみに現在 serverlessチームはstate管理をリモートストレージでできるように改修を行っているので、将来的にはgit管理しなくてよくなる。


以上です

vercel以外にNext.jsアプリをサクッとデプロイできる選択肢が増えるのは嬉しい事なので、今後も開発が続く事を願っています。