2020年も残りわずかとなりました。
本記事は Jamstack Advent Calendar 2020 25日目の記事です。
弊社はmicroCMSというヘッドレスCMSを運営しているため、必然的にJamstack構成をやりたいという要望が舞い込んでくるのですが、その数も去年と比べると格段に増えたと感じています。
Jamstackの本家サイトもリニューアルされました。
いつの間にかロゴも出来てますね。
改めて読んでみると初期のサイトとは内容も一部変わっており、「Jamstackとは呼べない例」や「Webサーバーに依存しない」という記述が無くなりました。
Jamstackの定義は少しずつ時代に合わせて変わってきていると思われます。
Jamstackとは
Jamstackとは、Webをより速く、安全に、簡単に拡張できるように設計されたアーキテクチャです。
SSG(Static Site Generator)により事前に生成されたアセットをCDN(Content Delivery Network)から配信することで高速なWebサービスを実現します。
また、CI/CDをうまく活用することで動的なサービスでもWebサーバーを不要とする構成も実現可能です。
そのため、高速&セキュア&強SEOという三拍子揃ったWebサイトを構築することができます。
良いこと尽くしのように聞こえますが、次のような弱点もあります。
- ページ数が多い場合にビルドに時間がかかる(長いと数十分)
- 〇〇時きっかりにコンテンツを公開というのが難しい
コーポレートサイトや個人ブログであれば特に上記の弱点に引っ掛かることなく運用が可能です。
しかし、メディアサイトなどになってくるとコンテンツが数千件を超える場合もざらにあるため、こちらの課題に直面することになります。
これらの根本的な原因としては、一つのコンテンツを更新したいだけなのに全コンテンツをビルドし直さなくてはならないところにあります。
解決するためには更新したコンテンツと、それに関連するコンテンツのみを差分ビルドする必要がありますが、2020年12月の現時点ではメジャーなフレームワークにおいて実装されておりません。
(アセットを一意なディレクトリ内に生成されるように設定を行うことで差分ビルドを実現することも可能ですが、Jamstackの特徴の一つであるアトミックデプロイ原則に反しています)
Next.jsのIncremental Static Regeneration
ビルド面を解決する手法として、Next.jsのv9.4から Incremental Static Regeneration(ISR)が登場しました。
ISRでは、ユーザーからのリクエストに対してCDNにキャッシュされたコンテンツを返しつつ、そのバックグラウンドで新しいデータをオリジンサーバーに取得しにいき、CDNキャッシュを更新します。
ユーザーからのアクセスによりページ単位でSSGが行われるため、サイトの全ビルドを行う必要がなくなりました。
初回アクセス時にCDNにキャッシュが存在せず、オリジンサーバーにしかコンテンツがないページに関してはNext.jsのフォールバック処理が動作し、CSRまたはSSRにてページが表示されます。(そしてその際にCDNキャッシュされます)
SSG / SSR / CSR の良いとこ取りをした非常に画期的なアーキテクチャですが、SSRを含んでいる点でこれは当初のJamstackの定義からは外れているような印象を受けます。
ここで話を冒頭に戻しますと、Jamstackのサイトから「Jamstackとは呼べない例」や「Webサーバーに依存しない」といった記述がなくなったのはISRを考慮してのことではないかと想像しています。
そもそもJamstackという言葉はNetlify CEOが提唱したもので、Netlifyのマーケティング用語とも言えるので、確固たる定義が必要というよりは技術の進歩に合わせて最適化されていくのではないかと思っています。
今後のJamstackの核は何か?
最近のフロントエンドの関心事の一つにエッジサーバーが挙げられると思います。
エッジサーバーとは?
冒頭から登場しているCDN層がそれにあたります。
クライアントとサーバー間の通信において、通信スピードを高めるためにはそもそも物理的な距離を縮めることが効果的です。
そこでクライアント(端末)の近くに分散配置されたサーバーをエッジサーバーと呼びます。
物理的な距離が近い分、当然通信が速いので出来る限りエッジサーバーからデータを返すことがページパフォーマンスの向上につながります。
エッジサーバーの進化
Web業界に限らず、エッジコンピューティングの重要性は高まっています。
5G時代に突入し、サーバーは今まで以上に膨大なデータを扱う必要が出てきました。
そのため、自動車やIoTなどネットワークの遅延が命取りになる分野において、従来のクラウドコンピューティングによる処理では懸念がありました。
そこでエッジサーバーを中心とした大量データの処理が重要となってきています。
Webにおいて、基本的にエッジサーバーはコンテンツをキャッシュし、クライアントに返す役割を果たしてきました。
最近ではCDNの進化に伴い、エッジサーバー上で様々な処理を行うことができるようになってきています。
Netlify - EdgeHandler
NetlifyのEdgeHandlerを例に挙げてみます。
(2020年12月現在はまだベータ版で、アーリーアクセスの申請が必要です。)
EdgeHandlerでは次のような処理が実行可能になります。
- A/Bテスト
- 広告のパーソナライズ
- 認証のカスタマイズ
- API連携
- コンテンツをローカライズする
- サイトの再ビルドなしに変更を反映
外部APIからデータを取得し、キャッシュしておくこともできるのでいろいろな活用ができそうです。
また位置情報や認証情報によるコンテンツの出し分けをエッジサーバー上で可能になるため、従来のクライアントサーバー間の往復と比較すると高速にコンテンツを配信できます。
Netlify FunctionsがAWS Lambdaを利用しているので、こちらのEdgeHandlerは後述するAWS Lambda@Edgeを内部的には利用しているものと思われます。
AWS - Lambda@Edge
Lambda@EdgeはAmazon CloudFrontに対してコードを実行するためのソリューションです。
すべてのオリジン応答にHTTPセキュリティヘッダーを追加したり、他のAWSサービスと連携することで自動的なスケーリングやバックアップなど様々な応用が可能です。
詳しくはこちらをご覧ください。
Vercel - Edge Network
VercelはNext.jsを開発している企業であり、そのホスティングサービスでもあります。
Vercelは今のところエッジサーバー上で自作した関数を動かすことはできませんが、エッジサーバー上での処理を自動的に最適化してくれている印象があります。
ドキュメントはこちら。
冒頭で述べたNext.jsのISRなどは良い例で、開発者的にはあまりエッジサーバー上での処理を気にすることなく、Next.js側のコードにrevalidate指定を一行追加するだけで実現が可能になっています。
2020年にNext.jsの人気が爆発したのは、このあたりの開発者体験の向上がポイントになったと思っています。
Jamstackとエッジサーバー
Jamstackにおいて今後エッジサーバーが重要となってくるのは間違いありません。
オリジンサーバーにリクエストを到達させずに、どれだけエッジサーバーにて処理を完結できるかという部分がフロントエンド(というかサービス全体?)のパフォーマンスを上げるのにクリティカルに影響を与える部分だからです。
特にISRはJamstackのビルド時間の弱点を克服できるものであり、Next.js以外のプラットフォームにおいても実装が期待されます。
とはいえISRはエッジサーバー側にてキャッシュの破棄を適切に行い、バックグラウンドにてデータの再取得を行うStale-While-Revalidateの挙動が要です。
Vercel社はNext.jsとVercelの両方を保持しているためこれを実現できましたが、その他のフレームワークではCDN側と上手いこと実装をすり合わせていく必要があります。
そのため、エッジサーバーでの処理が可能であるエッジワーカーがより重要になってくると考えられます。
Stale-White-RevalidateにCDN側が対応していなくても、エッジサーバー上での関数実行により自前で同様の処理が実現可能になるのではないかと想像しています。
今後エッジワーカー上でのエコシステムが出来ていくことで、NetlifyのEdgeHandler や Lambda@Edge は汎用性を手にし、どんなフレームワークと組み合わせてもISRが可能になる未来が見えます。
ISRをユーザー視点で見る
一方で、ISRはページを閲覧するユーザー視点で見ると、SSR+Stale-While-Revalidate可能なCDNの組み合わせと実はかなり近しいものです。
仮にSSRモードのNuxt.jsをHeroku+Fastly(Stale-White-Revalidate設定済み)にデプロイした場合、クライアントサイドからは初期アクセス以外は常にCDNからキャッシュされたデータが配信されます。
ユーザーからすると、CDN配信されてくるデータがSSGされたHTMLなのか、SSRをキャッシュしたデータなのかは大差ありません。
異なる点があるとすれば、ISRではビルド時に全ファイルを生成してあらかじめCDNキャッシュに配置できる点でしょうか。
その点に関してはファイル数が非常に多い場合には逆にデメリットにもなり得るため、一概にどちらが優れているかは断言できません。
つまりISRはユーザー視点で見るとそんなに新しいものではないということが言えます。
(Fastlyは2014年9月の時点でStale-While-Revalidateをサポートしています)
開発者視点で言えば、ISRはSSGを主軸とした配信となるため、サーバーレスで最大限CDNを活用できるという文脈がありそうです。
SSG?SSR?CSR?
Jamstackといえば従来のSSGをより昇華させたものというイメージがありますが、ISRの登場によって部分的にSSRを用いた方がユーザー体験、もといコンテンツ提供者体験が良いということが実証されつつあります。
具体的には大部分はSSGで、新しくページを追加する際のみ一部SSRを行う形です。
「サーバーオリジンのリソースをできるだけ使わず、CDN上で完結すること」がやはりJamstackにとって重要なポイントになりそうです。
ではCSR(クライアントサイドレンダリング)は?というと、必要最低限のデータのやり取りによってページ遷移を高速化するSPAにとって不可欠なため、言うまでもなく導入すべきものという認識です。
Next / Nuxt においてSSRやSSGは初期ロード時のページ生成方法を指しており、ページ遷移時は当たり前のようにCSRされます。(数年前から考えると凄すぎる進歩)
おわりに
だいぶISRがメインの記事となってしまいました。
筆者の考察もかなり含まれているため、もし誤っている点がありましたらご指摘ください。
最後に、Jamstackアドベントカレンダーでこんなことを言うのはどうかとも思いますが、「Jamstackであるかどうか」はもはやどうでも良い話なのかもしれません。
「真に効率的なアーキテクチャであるかどうか」の方がよほど重要です。
ユーザーにとっても、開発者にとってもより良いものを模索していきたいですね。
2020年もお疲れ様でした!
-----
弊社ではmicroCMSというヘッドレスCMSを開発しています。
Jamstackなサイト開発に興味のある方はぜひ触ってみてください。
ご意見・ご要望は管理画面右下のチャット、公式Twitter、メールからお気軽にご連絡ください!