Jamstackを既存のシステムに導入するかを検討する機会があった。 紆余曲折したものの、未だに暫定的な結論しか出ていない。 とはいえ、わりと頑張った。 今回は
- Jamstackとはなんぞや?
- Jamstackの特徴
- Jamstackの技術
- 弱みを解決する策
- 実際に検討した話
を雑に紹介したい。
個人的なメモなので、間違っているところがあるのを考慮願いたい。
Jamstackとは?
JamstackのJamは以下の頭文字をとっている。
- JavaScript
- APIs
- Markup
まず、フロントエンドを持たないAPI群がある。APIはブラウザのJavaScriptから叩かれるかもしれないし、後述するようなSSG =「Static Site Generator」のフレームワークが叩くかも知れない。どちらにせよユーザーに配信されるのはSSGが出力した、Markup。つまりプリレンダリングされたHTMLである。そして、動的な部分はJavaScriptで補う。
というのが一般的な解釈。だと思っている。
ポイントは「静的HTML」という点だ。動的に出力されるページとは違い、あらかじめファイルとしてレンダリングされたHTMLをユーザーに返却する。
Jamstackは懐かしい匂いがする
最初、「モダン」な仕組みとしてJamstackを知った時にMovableTypeを思い出してしまった。 「JamstackってようはWordPressじゃなくてMovableTypeじゃん」ってなった。 WordPressはPHPが動的に動いてDBにあるコンテンツをHTMLとしてダイナミックにレンダリングする。 MovableTypeでは編集が終わると管理画面でパブリッシュ。書き出されたHTMLをApacheなりで配信する仕組みなっている。
気取ると
Jamstackは懐かしい匂いがする
のである。
CDNに乗せる
Jamstackでは静的HTMLをCDNに乗せるのが実際問題よくやられている。CDNから配信すれば当然速いし、なによりサーバーの管理が必要なくなる。
Jamstackの特徴
というわけで、Jamstackはなんぞや?が分かるとその特徴が見えてくる。
- 速い(CDN)
- サーバー管理が要らない(サーバーレス)
- セキュア
- SEOに強い
SEOに強いってのはキャッチーな謳い文句っぽいが、根拠がある。 まず、CDN配信だからTTFBが短くなる。また、最近のJamstackでは、SPAのようにJavaScriptが動的にDOMを作るのではなく、SSGが頑張って、なるべくHTML本体にコンテンツを埋め込むような風潮がある。細かい仕組みは知らないが、検索エンジンのボットに対してその方が優しい。
逆にダイナミックな処理が苦手だ。「ベタに」Jamstackをやろうとすると、以下の3つの条件では難しくなる。
- 更新頻度が高い
- ページ数が多い
- ページの生成に時間がかかる
この件を解決するためにISRという仕組みがある。後述する。
Jamstackを構成する技術
Jamstackを実現させるために以下の3つの技術がある。
- ヘッドレスCMS
- SSGフレームワーク
- エッジサービス(エッジサーバー)
ヘッドレスCMS
フロントエンドを持たないCMSである。APIのエンドイポイントが生えていて、叩くとJSONが返ってくる。ただそれだけのCMSだ。SSGがこれを叩く。
調べてみると、たくさんのヘッドレスCMSサービスがあって驚いた。特にmicroCMSとcontentfulが気に入った。CMSとしての出来がよい。
SSGフレームワーク
SSG = Static Site Generator。静的なHTMLを吐くためのフレームワークだ。
ちなみにこのブログ、実はJamstack構成をとっている。中でもSSGフレームワークとしてHugoを使用している。HugoはMarkdownで書かれたコンテンツをテンプレートを適応しつつ静的サイトとして吐く。プレビュー用のサーバーが付属しているのが便利だ。
Hugoに似たようなものにJekyllがある。他にも上記したヘッドレスCMSのAPIを叩いてサイトを作るフレームワークがある。特にNext.jsに注目している。Next.jsはSSR = Server Side RenderingすることもSSGとしての機能も兼ね備えている。エッジサービスのVercelと相性がよい。
エッジサービス
CDNだけではなく、SSGを実行する環境を持っているもの。具体的には
- Vercel
- Netlify
がある。また、AWSのlambda@edgeを活用してCloudFrontで配信しても同じようなことが出来る。
このブログはSSGとしてHugoを使い、エッジサービスにはVercelを使っている。連携しているGitHubのレポジトリに push
したのをフックにVercel上でHugoが動き、生成されたHTMLでCDNのコンテンツをごそっと取り替えてくれる。
弱みを解決する
上記した通り、「ベタな」、つまり一度にサイト全体のHTMLを生成してCDNに乗せるJamstackの場合以下に弱い。
- ページの生成に時間がかかる
- 更新頻度が高い
- ページ数が多い
ISR
解決するためにNext.jsのISR = Incremental Static Regeneration機能がある。
ページの更新ごとに全てのページをレンダリングするのはかったるい。そこで、ユーザーがリクエストしたページ単位でページを生成する。リクエストされた際、CDNにHTMLがない = キャッシュがない時に、新しくレンダリングしてからそのHTMLを返すのではなく、古いキャッシュを返す。バックグラウンドでページ生成が走っていて、ページ生成が完了したら、それを返すようにする。という仕組みだ。
Stale-While-Revalidate
SWR = Stale-While-Revalidate。もともとは「キャッシュ戦略」。上記したISRの挙動と考えてよい。
HTTPのレスポンスヘッダーに含めることにより、SWRをサポートしているブラウザやCDNでSWRに準拠したキャッシングが実現出来る。
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Cache-Control には stale-while-revalidat
の項目について以下のように書かれている。
クライアントが古いレスポンスを受け入れ、新しいレスポンスをバックグラウンドで非同期にチェックすることを示します。
SWRに対応したCDNにはfastly、Google Cloud CDNなどがある。
検討した
Jamstackを理解した上で、某サービスでJamstackが導入できるかを検討してみた。 ひとまず「どのサービスか」は分からないように記載する。 前提条件としてはこうだ。
- バックエンドにAPIがある
- フロントエンド = 「APIを叩いてHTMLにレンダリングするところ」は一部Next.jsで作られている
- パスごとにアプリが分かれている。
/hoge/*
がNext.jsで動いている - AWSにある
- CDNはかませてない。その代わり、前段にVarnishを置いてレスポンスをキャッシュしている
- ざっくりアーキテクチャを紹介すると以下
User <=> ELB/ALB <=> Varnish <=> フロント(Next.js) <=> API
- 更新頻度は高くないが、ページ生成に時間がかかる。アプリによってはページ数が膨大(x0万オーダー)である
さて、こんな紆余曲折になった。
- 最初の動機は「nodeサーバーの面倒をみたくない」だった
- Jamstackを見つけて「Jamstackアツい!検証してみよう!」となる(つまりJamstackのサーバーレスの部分に注目したことになる)
- Next.jsでJamstackやるならVercelだな
- CDN持っている
- SSGとしてNext.jsを実行してくれる
- 出来上がったHTML等をCDNに置いてくれる
- ISRに対応
- プレビュー機能など充実している
- 全てVercelで完結じゃん!
- ただし… パスの問題が出る
/hoge/*
のみをVercelのCDNに向ける、とか無理!
- Reverse Proxyの下にVercelを置く?
- 否。Reverse Proxyの下にVercelを置くべきではない https://github.com/vercel/vercel/discussions/5769
- Vercelむりぽ(Vercelを一番前段において、Next.jsでルーティングさせれば出来る)
- Vercel以外で検討してみる
- 単純なJamstackをするのであれば、一度SSGで書き出したHTMLをCDNに置いてそれを参照させればよいんだよな〜
- でもISRしたいよね〜
- 毎度全ページをSSGして書き出すのは大変だ
- Vercelを使わずISRっぽくするにはSWRをサポートしたCDNを使えばよい
- 現にZennではVercelからGCPに移行している https://zenn.dev/catnose99/articles/5e9547a5c207e3
- ただし… AWS使ってるけど、CloudFrontではSWRをサポートしていない><
- でも、Lambda@EdgeでNext.jsを実行させてごにょればISRなんとかなる!
- 「Serverless Next.js Component」 https://github.com/serverless-nextjs/serverless-next.js なんてあるじゃん!
- あれ?ってか、そもそも現状CDN使ってないじゃん(アセットはCDNに置いてたりする)
- CloudFrontなり、他のSWRをサポートしたCDNを前段に置かないと!
- ん?でもVarnishもSWR対応してたよね。そうそうGraceモードってのSWR出来る。なんなら
stale-while-revalidate
ヘッダーもみる。で、Varnishはもうすでに導入しているよね。CDN無視すれば(使わなければ)、もうできてんじゃんw - ってことはJamstackを導入するかどうか判断する点は「CDNで配信したいかどうか」だけなんじゃないか?
- …
で、強引にまとめると
- Jamstack全ての要素を叶えるのはキツい
- 現状、Varnishを使ってうまいことやってるよね
- あとはCDNに載せたいかどうか?だ!
となりました。紆余曲折終わり!
CDNを導入する場合は大きなアーキテクチャ変更が必要だからペンディングだな!
まとめる
以上、Jamstackとはなんぞや?から始まり、Jamstackをめぐる紆余曲折を紹介した。逆に
- 更新頻度が低い
- ページ数がそんなに多くない
サイトは、Jamstackに向いてるので、導入を検討するとよいだろう。
Next.jsやVercelなどで大きなアップデートが続いていたり、lambda@edgeでうごく「Serverless Next.js Component」が絶賛開発中だったりと、様子を見てもいいかもと思った。
個人的には、「Jamstackは懐かしい匂いがする」ってのが気に入った。