Next.jsのSSR/SSG/CSRについて考える
現状のNext.jsは
- ルーティング毎に
getServerSideProps
を使ってた場合はSSR、getStaticProps
getStaticPaths
を使った場合はSSG(fallbackを指定してない場合)、そしてnext exportをしたらstatic exportと言うような形になっている
Next.jsは割とSSGに肯定的でその方向に向かっているように捉えられる。確かにこれは良いことで、実際パフォーマンスはSSGが10倍程よいみたいなとこまで来ている(NodeサーバーのReactのSSRがめっちゃ効率悪いみたいなのもあり)。
ただし僕はSSGはビルド時に解決してしまうためいくつか柔軟性を欠いている部分があると思っている。
代表的な例は認証である。
例えばJWTによる認証をしていて、それをCookieなりに保存しているとする。完全にNodeサーバーのことだけを考えるのであればユーザーのページであろうがNodeでレンダリングしてHTMLを返すのだが、昨今は前段にCDNを挟んで効率よくユーザーに届くように配慮するケースが多数ある。
こうなった場合、ユーザー情報をNodeでレンダリングして返してしまうとその結果のHTMLをキャッシュしたCDNは同じリクエストが来た時にそのキャッシュから返してしまう。こうなると個人情報が漏洩してしまうため、普通ユーザーの情報に関するものはCSRを行う(ユーザーのブラウザで実行されるJavaScriptでユーザー情報の取得などをしてレンダリングを実行する)
これはSSGでもSSRでも同じことだと言える。
ただし違う点としてCookieに詰めたJWTはそれが有効期限かどうかを判定することでログイン済みのユーザーなのかそうではないのかをNodeのサーバーの時点で見分けることができる(もっと言えばCDNでロジックが動けばそこで解決し切ることも出来る)
これがあるかないかはかなりユーザー体験としては変わると思っていて、例えばヘッダーにログイン済みのユーザーであればマイページへのリンク、ログインしてない人はログインへの導線のリンクみたいなUIを使っている場合、どうしてもSSGでは後者の方を初回にレンダリングする(またはローディング中であることがわかるUIをレンダリングする)、その後CSRのタイミングでログイン済みの導線へ書き換えるみたいな処理の順番になってしまう。
先程Nodeのサーバーで判断できると言ったが、Nodeのサーバーで判断することが出来れば初回のレンダリングの時点でそのユーザーの状態に沿ったヘッダーをレンダリングすることができる。
またログイン状態をX-loggedinのようなヘッダーで持っておけばVaryヘッダーにX-loggedinを指定することでキャッシュも適切にすることができる。
つまり最適解はHeaderはSSRするんだけど、ページの中身はSSGすると言った両方とも使った状態を簡単な実装で行えるような設計がNext.jsに求めたいものである。
ではこの実装を考えてみる。
そもそもHeaderなどの実装は良く _app.tsx と言うファイルにすることになると思うが、 _app.tsx のJSXの中で部分的にSSRをして、部分的にSSGしたいのである。
これはつまりどう言うことかと言うと事前にビルドした静的なファイルを返すためのSSRを書いているような状態になる。
react-dom/serverのrenderToStringをビルド時にやってたものを使って返すような実装になると言うことだ。
つまり目指したいのは事前に処理が済んでいるSSRである。
ReactのServer Componentによって部分SSRができるようになったのでキャッシュ効率を考えた設計ができたりするんじゃないだろうか...と思ったりしているが、ReactのServer Componentの実態がまだ分からない(まだアイデアレベルであるため)からなんとも言えないな
でも上記は結局SSRなので、SSGのように単純に静的アセットを配信するだけでは上手くかないのでSSGよりもパフォーマンスは悪いけどSSRよりは良いみたいな間が個人的にはもう少し未来になったら来ると思う。名前が欲しいな。
Prebuild Server Rendering
とかどうなんだろうか。
pSSR(prebuild Server Side Rendering) もいいな。
現状のNextはSSR or SSGでなんとかって感じだし、基本CSRはcDMとかでデータfetchすりゃええやろ。みたいなスタンスっぽい(vercel/swrとかもいい例)ので、自分の思想と合わないところがちょくちょくあるなーと思う
一旦特に議論とかがあるわけでもないし、自分の思いをぼやいただけなのでcloseする
これServerSide Componentsが出る前のスクラップだけど、やりたいことはServerSide Componentsで出来そう