SSRみたいなフロント用語の使い方改めませんか運動
sumirenです。
背景
フロントエンド界隈はベンダやコミュニティ主導で新しいアーキテクチャや技術的手法がどんどん出ていて素晴らしいです。
一方、そうして量産されてきた用語が、界隈の変化に置いていかれている側面もあるように思います。例えば、SSRという用語を取り上げると、コンポーネントからHTMLへの変換を指すこともあれば、サーバー側でデータを取得することを指すこともあります。
実際、業界のパイオニアであるVercel/Next.jsも、そうした現状に対する懸念を持っているように思われます。実際、Next.js App Routerでは、以下のような変化が見られます。
- getXXXPropsが廃止され、fetchに対する引数で表現されるようになった
- SSRやISRという「レンダリング」という言葉を、フェッチの文脈で使わなくなっている
しかし、そっとドキュメントを書き換えた程度では、人々に定着した言葉の使い方が変わることはないでしょう。
用語体系が風化していることの弊害
用語体系が風化していることで、当然ながらコミュニケーションコストが高くなっています。
アーキテクチャを説明するときにも、SSRやCSRという言葉が出るたびに「それはどういう意味のSSRだ?」と聞く方は考えなければいけませんし、説明する側も一々「これはCSRです。この文脈におけるCSRとは...」と説明する必要があります。
また、人間は概念に名前をつけることで、頭の中を整理しています。用語体系が貧弱であることで、明瞭な思考ができなくなっている側面もあります。
蛇足
同じようなことはちょうど「REST」という用語にも言えると思います。RESTは定義と世の中のユースケースとの間にギャップがありすぎて使い物にならなくなった類の用語だと思いますが、フロント界隈の用語は単にベンダが散らかしたまま誰も整理整頓をしていない状態かな、という感想です。
この記事の目的と指針
この記事の目的は、皆さんがフロントエンドの用語の定義について考えるきっかけを改めて作ることです。
そのために、「例えばこんな用語体系だったらどうだろうか」というたたき台を提案したいと思います。個人的には「データフェッチとレンダリングを分けて考えよう」というのが一番言いたいことなのですが、せっかくなら皆さんがゼロベースで考えられたほうがより良いと思うためです。
なお、この用語体系はあくまでたたき台です。筆者はReact/Next.js以外のフロントエンドライブラリには明るくないですし、(Next.jsはともかく)Reactそのものの歴史や内部構造にも詳しくありません。そのため、この記事で挙げる用語体系がベストだとは思いません。議論のスタート地点と考えていただければと思います。
5つの新しい概念の軸
結局用語体系を貧弱している理由は「軸」が足りていないことだと思っています。例えば、上述のSSRという用語には、レンダリングとフェッチの少なくとも2軸が含まれてしまっています。
そこで、この章でこうした概念の分解が甘い部分を軸として分解したうえで、次の章で肉付けをしたいと思います。
以下がこれまで単純にSSRと呼んでいたものを例に、概念を5つの軸に分解した図です。
1. データフェッチが発生するサイド
まず、データフェッチがクライアントサイドで発生するかサーバーサイドで発生するかというのが1つ目の軸です。
Reactの場合でSWRやTanStack Queryなどでフロントエンドから直接バックエンドAPIを叩いたとしたら、それはクライアントサイドでフェッチしています。
Next.jsのgetServerSidePropsはサーバーサイドでのフェッチです。App Routerのfetchにはフェッチ頻度が指定できますが、それらは全てサーバーサイドでのフェッチです。
この軸にはレンダリングに関する概念が一切入っていないことがポイントです。例えばNext.jsでdynamic import(ssr: false)
を用いれば、データフェッチはサーバーサイドで発生するが、コンポーネントのレンダリングは大部分がクライアントサイドで発生するといったこともありえます。
2. データフェッチが発生するタイミング
2つ目の軸はデータフェッチが発生するタイミングです。これはちょうどApp Routerにおけるfetchのオプションと同等です。
例えばビルド時に取得、(SGと呼ばれているもの)、定期的にまたはオンデマンドで再取得(ISRと呼ばれているもの)、リクエストごとに取得といった(SSRという言葉が持っている意味の1つ)タイミングがありえます。
データフェッチが発生するサイドとタイミングは軸として分けていますが、ある程度依存関係があります。例えば、データフェッチが発生するサイドがクライアントサイドであれば、データフェッチが発生するタイミングはリクエストごとにならざるを得ないでしょう。
3. レンダリングが発生するサイド
3つ目の軸は、レンダリングが発生する場所です。
この記事におけるレンダリングとは、Reactなどのライブラリでプログラミング言語で表現されたUIをHTMLに変換することを指します。つまり、データの取得とは関係のない概念です。
例えばNext.jsであれば、ほとんどの場合はサーバーサイドでレンダリングが発生し、クライアントサイドにはHTMLが渡されます(リロードにせよページ遷移にせよ)。
先述のとおり、dynamic import(ssr: false)
を用いると特定のコンポーネントをクライアントサイドでレンダリングすることができ、(副作用ではなく)レンダリング時点でブラウザのAPIに依存するようなtReactコンポーネントも利用することができます。
例えばViteのような生のReactを用いた構成の場合、レンダリングはクライアントサイドで行われます。サーバーサイドからは空のHTMLと、ブラウザにおけるJSの実行によりHTMLに変換されるReactコンポーネントなどが配信されます。
4. レンダリングが発生するタイミング
4つ目の軸は、レンダリングが発生するタイミングです。
この軸も、レンダリングが発生するサイドとは分離されていますが、依存関係はあります。レンダリングが発生するサイドがクライアントサイドであれば、レンダリングが発生するタイミングは実行時になるでしょう。レンダリングが発生するサイドがサーバーサイドであれば、ビルド時の実行やリクエストごとの実行などがありえます。
Next.jsの場合、pagesまではデータフェッチとは関係なく、レンダリングは全てビルド時に発生しており、ファイルシステム上にhtmlファイルとjsファイルとして保存されていました(dynamic import(ssr: false)
しているコンポーネント以外)。App Routerからは、データフェッチが発生するタイミングとも依存関係があるようです。
5. ナビゲーションの仕組み
最後に、ナビゲーションがどのように発生するかも1つの軸としてみました。
Next.jsの場合、ブラウザリロードしたときはブラウザのネイティブな仕組みで遷移が発生し(もう少し上手い言い方ができるといいのですが)し、サーバーサイドからはContent-Type: text/html
で直接レンダリング後のページが返ってきます。一方、router.push
や<Link>
などで遷移する場合は、Next.jsがJSベースのリクエストをサーバーサイドに対して行うことでページの情報を取得し、DOMを書き換えています。つまり、Next.jsは2種類のナビゲーションをハイブリッドで用いています。
Viteのような生のReactを用いた構成の場合は、ナビゲーションは全てクライアントサイドで発生します。
用語体系(素案)
本質的には、軸をどう分けるかこそが用語体系の刷新におけるポイントだと思っています。そのため、筆者の洞察の大部分はすでに伝え終わっています。
この記事の残りの部分では、より議論をしやすくしたりイメージが湧くように、上記の軸に対して用語を肉付けして用語体系を作ったうえで、現実世界の会話をシミュレートしてみたいと思います。
以下が最終的な用語体系のたたき台です。
- データフェッチの場所
- クライアントサイドデータフェッチ(CSDF):ブラウザからAPIサーバーに対して直接データを問い合わせること。CSRという言葉が抱えていた意味の1つ
- サーバーサイドデータフェッチ(SSDF):Next.js等のフルスタックFWサーバーで、バックエンドAPIに対してデータを問い合わせること。SSRという言葉が抱えていた意味の1つ
- データフェッチの頻度やタイミング
- スタティックデータフェッチ(SDF):ビルド時にデータを取得する。旧SG的なもの
- インクリメンタルデータフェッチ(IDF):stale-while-revalidateな感じでデータを取得する。旧ISR的なもの
- ダイナミックデータフェッチ(DDF):リクエスト毎にデータを取得するもの。SSRという言葉が抱えていた意味の1つ
- レンダリングの場所
-
クライアントサイドレンダリング(CSR):フロントエンドでReactコンポーネント→HTMLの変換を行う。documentなどブラウザのAPIに依存できるが、遅くなる。SPAや、Next.jsのdynamic importで
ssr: false
を指定した場合がこれ - サーバーサイドレンダリング(SSR):サーバー側でReactコンポーネントをHTMLに変換してフロントエンドに返すこと。Server Componentからの変換も含む
-
クライアントサイドレンダリング(CSR):フロントエンドでReactコンポーネント→HTMLの変換を行う。documentなどブラウザのAPIに依存できるが、遅くなる。SPAや、Next.jsのdynamic importで
- レンダリングのタイミング
- スタティックレンダリング(SR):ビルド時にReactコンポーネント→HTMLの変換を行う
- ダイナミックレンダリング(DR):リクエスト発生時にReactコンポーネント→HTMLの変換を行う
- ナビゲーション
-
クライアントサイドナビゲーション(CSN):ブラウザからJSベースでサーバーサイドにページ情報をリクエストし、JS上でルーティングする。SPAで遷移する場合や、Next.jsで
router.push()
した場合もこれ -
サーバーサイドナビゲーション(SSN):ブラウザリロードやlocation.hrefの更新など、ブラウザの標準的な方法で遷移すること。レスポンスのContent-Typeが基本的に
text/html
になる
-
クライアントサイドナビゲーション(CSN):ブラウザからJSベースでサーバーサイドにページ情報をリクエストし、JS上でルーティングする。SPAで遷移する場合や、Next.jsで
試運転
実際に上記の用語体系を使ってみます。
例として、Next.jsを採用し、SGでデータを取得しつつ、一部のデータをSWRでクライアントサイドから取得しているアプリケーションのアーキテクチャについて会話が行われているとしましょう。
過去の用語体系では、以下のようなちんぷんかんぷんな会話が度々発生していたかと思います。実際に皆さんも経験があるのではないでしょうか。
- Aさん:「Next.jsでSGとSWRを併用しています」
- Bさん:「なるほど。Next.jsのことはよく知らないのですが、Reactはサーバーサイドでレンダリングしているということですか?」
- Aさん:「いえ、SSRではなくSGです」
- Bさん:「うーん、なるほど。SWRを使っている部分は、Reactのレンダリングはサーバーサイドで起きますよね?」
- Aさん:「いえ、そこはCSRなのでクライアントサイドです」
- Bさん:「...」
上記の例は少しばかり極端ですが、仮にAさんに十分な知識があったとしても、旧来の用語体系では正確な回答が難しいことは確かです。これまでの用語体系では、サーバーサイドレンダリングという言葉にはデータフェッチの場所・データフェッチのタイミング・レンダリングの場所の3つの軸が含まれてしまい、曖昧姓のない回答が難しいためです。
新しい用語体系では、上記のアーキテクチャは以下のように説明できます。
- データの種類に応じてクライアントサイドデータフェッチとサーバーサイドデータフェッチの両方のサイドでフェッチをしている。サーバーサイドデータフェッチの発生タイミングは、スタティックデータフェッチである
- レンダリングはサーバーサイドでのスタティックレンダリングのみが行われる(サイドはサーバーサイド、タイミングはスタティック)
- ナビゲーションはNext.js標準搭載の仕組みであるクライアントサイドナビゲーションとサーバーサイドナビゲーションのハイブリッドである
もちろんこの説明は冗長ですが、必要に応じて深掘りできる用語体系が提供されていることに意味があります。例えば、AさんとBさんのやりとりは次のようになるでしょう。
- Aさん:「Next.jsでスタティックデータフェッチとクライアントサイドデータフェッチを併用しています」
- Bさん:「なるほど。Next.jsのことはよく知らないのですが、Reactはサーバーサイドでレンダリングしているということですか?」
- Aさん:「はい、レンダリングはサーバーサイドでのスタティックレンダリングのみです」
このように、2ストロークしても噛み合っていなかった会話が、1ストロークで噛み合うようになるのではないでしょうか。
まとめ
最初に記載したとおり、この用語の整理がベストだとは全く思っていません。筆者が数時間で思いついたものを書きなぐった程度の素案です。軸を分解しすぎて冗長かもしれませんし、逆にまだ分解の甘い概念があるかもしれません。
ただ、この記事を通じて、既存の用語体系の課題が伝わり、皆さんの議論や振り返り、フロントエンド界隈の生産性向上につながれば嬉しいです。
個人的に一番言いたいことは、レンダリングとデータフェッチを分けてほしいということです。サーバーサイドでデータ取得することをSSRと言ったり、クライアントサイドでデータ取得することをCSRと言うのを業界としてやめていってほしい。大事なことなので2回言いますが、とにかくレンダリングとデータフェッチを分けてほしい。
sumirenでした!
Discussion
それはそれできつい
すごく共感して拝読させていただきつつ、ブラウザをつくる側からするとhtmlを解析してosで描画するのがレンダリングなのでそれはそれでモヤモヤしてしまうのです。似た例として「ネイティブ」という言葉もなんとかしたいものですね。
ありがとうございます!
とても良い記事でした、共感が深かったです。
Dan Abramov先生も、SSRやSSGなどの用語が、(主にNext.jsなどの)最近の技術的手法の変化に対して、そぐわなくなってきたことを指摘されていました。
もうこの用語を使うのをやめようぜ、って言ってるみたいです。
しかし、代わりの用語というのは、なかなか難しいですね。
個人的には、SSRかつCSRみたいなやつは『臨機応変レンダリング』と呼んで、お茶を濁しています😇
いづれ公式的な用語が固まるまでは、チーム内で用語を策定する、みたいな工夫は必要そうですね。