Upgrade to Pro — share decks privately, control downloads, hide ads and more …

React Server Components の疑問を解き明かす

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for mizdra mizdra PRO
August 09, 2024

React Server Components の疑問を解き明かす

builderscon 2024 で発表した資料です。

動画はこちら: https://www.youtube.com/watch?v=_Prly-RAF7A&list=PLZjwpOgJkJXteFpwAbcVoQXfd2f5q-OPp

---

React Server Component は、サーバーサイド上でレンダリングされる React コンポーネントです。React Server Component を用いると、コンポーネントの中から DB にアクセスしたり、ファイルシステムにアクセスしたりできます。

一方で、今までにも erb や pug といったテンプレートエンジンのように、サーバーサイド上でテンプレートをレンダリングする技術というのは存在しました。また、React でも SSR を用いれば、(Client) Component をサーバー上でレンダリングできました。そのためこれらの技術とどう違うのか、疑問を抱いている方も多いでしょう。

しかし、React Server Component は従来の技術への単なる回帰ではありません。今まで存在していたいくつもの問題を解決し、優れた開発体験をもたらす技術なのです。

このトークでは、React Server Compomnent とは何か、どんなことができるのか、そして従来の技術とどう違うのか、どう開発スタイルが変わるのかについて話します。このトークが、React Server Component に関する様々な疑問を解消する手助けになるはずです。

Avatar for mizdra

mizdra PRO

August 09, 2024
Tweet

More Decks by mizdra

Other Decks in Technology

Transcript

  1. React Server Components (RSC) • 新しい種類の React コンポーネント • 特徴

    ◦ サーバで"のみ"でレンダーされる ◦ サーバ専用 API が使える ◦ ブラウザ向けの bundle size を削減できる • Next.js などで利用可能 4
  2. 目次 第1部: SPA と SSR 👈 NEXT 第2部: SSR が抱える問題

    第3部: RSC の誕生 第4部: 従来の技術との違い 第5部: 開発スタイルがどう変わるか 10
  3. Single-page Application (SPA) • 単一のページでリッチな Web アプリを作る手法 ◦ fetch API

    で動的にサーバからデータを取得 ◦ 取得したデータを React などで描画 ◦ JavaScript で画面遷移を制御 • すべて JavaScript で制御することで... ◦ リッチなユーザ体験を実現する 12
  4. SPA が抱える問題 • コンテンツが出るまで遅い ◦ ページアクセス直後は真っ白 ◦ JS が実行され始めて、ようやく出る •

    クローラーとの相性が悪い ◦ 素朴なクローラーだと、空のページと解釈される ▪ Google や Bing なら問題ないけど... ◦ X で og:image が出なかったり 14
  5. Hydration とは? • カピカピの HTML に水を与えて、もとに戻す • だから Hydration (水和)

    19 <button> <button onClick={...}> @HirokiOmote さんの記事の Hydration のたとえを参考に描きました。 https://www.estie.jp/blog/entry/2024/08/05/183235
  6. 目次 第1部: SPA と SSR 第2部: SSR が抱える問題 👈 NEXT

    第3部: RSC の誕生 第4部: 従来の技術との違い 第5部: 開発スタイルがどう変わるか 20
  7. SSR が抱える課題 • いくつか課題がある ◦ 不必要な bundle size の増大 ◦

    データ取得が煩雑 • Next.js で実装した記事詳細ページを例に解説 21
  8. 不必要な bundle size の増大 • <ArticlePage> は、ブラウザ向けに bundle される ◦

    Hydration や、ブラウザ上でのレンダーに必要なので • <ArticlePage> の依存も bundle 対象 ◦ marked, sanitize-html ▪ marked: 11.2 kB (gzipped) ▪ sanitize-html: 80.8 kB (gzipped) • bundle size が大きくなる 23
  9. 不必要な bundle size の増大 • ところで <ArticlePage> は... ◦ ユーザ操作などによって変化しない

    ◦ static なので、サーバでレンダーして終わりで良い ◦ Hydration も不要なはず • 技術的には、bundle に含めないようにできるはず ◦ しかし SSR は、問答無用で bundle に含めてしまう 24
  10. しかし、これはできない • ./db.js がブラウザの bundle に含まれてしまう ◦ ブラウザでは実行できず、エラーになる • 細かい話だけど...

    ◦ そもそもコンポーネントで async/await 使えない (*1) 26 *1: 厳密には使えるが非推奨 (参考: https://github.com/reactjs/rfcs/pull/229)
  11. 目次 第1部: SPA と SSR 第2部: SSR が抱える問題 第3部: RSC

    の誕生 👈 NEXT 第4部: 従来の技術との違い 第5部: 開発スタイルがどう変わるか 27
  12. React Server Components (RSC, SC) • こうした問題を解決するために誕生 • サーバーで"のみ"レンダーされるコンポーネント •

    特徴 ◦ サーバーでしか実行できないコードが書ける ◦ ブラウザの bundle に含まれない ▪ bundle size が削減 ◦ async/await が使える 28
  13. Client Components (CC) • Server Components の対になるもの • その正体は... ◦

    ブラウザでレンダーしていた従来のコンポーネント • Server Components 登場によって... ◦ 名前の整理が行われただけ ◦ 今まで書いてたやつが、Client Components と呼ばれるように 30
  14. 'use client' ディレクティブ • RSC の世界では、デフォルトで SCになる (*2) • CC

    にするには、 ‘use client’ を付ける 31 *2: 厳密には何もつけないと SC / CC のどちらでもない、未確定の状態になる。 コンポーネント利用側に応じて SC / CC どちらになるか、後から決まる。
  15. SC と CC の違い 32 Server Components Client Components ブラウザの

    bundle 含まれない 含まれる async/await ✅ 使える ❌ 使えない (*1) イベントリスナ ❌ 登録できない ✅ 登録できる サーバ専用 API ✅ 使える ❌ 使えない ブラウザ専用 API ❌ 使えない ✅ 使える React Hooks ❌ 使えない ✅ 使える *1: 厳密には使えるが非推奨 (参考: https://github.com/reactjs/rfcs/pull/229)
  16. Client Components は無くならない • 従来の CC だけあった世界に、SC が加わるだけ ◦ できることが増える

    • ブラウザ上でないとできないことは、CC でやる ◦ SC / CC は共存するもの 33 https://github.com/reactwg/server-components/discussions/4 より引用
  17. SC から CC にデータを props で渡せる 35 • シリアライズ可能なものなら OK

    (*3) • シリアライズは React が自動でやってくれる ◦ シームレスに SC と CC を接続できる *3 厳密にはシリアライズ不可能だが渡せるもの (Promise や Server Actions) がある。
  18. RSC と組み合わせて使える機能 • RSC と組み合わせて使える機能がある ◦ Server Actions ◦ Taint

    API • RSC と併用すると... ◦ より優れた開発体験が得られる 36
  19. SC の中で定義して、CC に渡せる • CC に prop で渡せる • 本来

    CC にはシリアライズ可能なものしか渡せないが... ◦ Server Actions は特別に許可されてる • Server Actions は RSC と密に結合されてる ◦ 非常に柔軟なコードが書ける 39
  20. 前提知識 • SC から CC に渡した props はユーザから丸見え!!! ◦ props

    が丸ごとシリアライズされ、HTML に埋め込まれてる 41 コードは以下の記事を参考にしつつ、改変してます。 https://zenn.dev/cybozu_frontend/articles/react-taint-apis
  21. そこで Taint API 42 • 任意の値を汚染 (taint) できる • 汚染された値を

    CC に渡すと... ◦ 実行時エラーが発生する • 誤って CC に機密情報を渡してしまうのを防げる
  22. 改めて: RSC でできること • サーバでのみレンダーされるコンポーネントを作れる • CC でできなかったことが、できるように ◦ サーバでのみ動くコードを書ける

    ◦ bundle size 削減, async/await 使える • Server Actions や Taint API と組み合わせると... ◦ 柔軟なコードを書けたり、よくある間違いを防げたり 44
  23. 目次 第1部: SPA と SSR 第2部: SSR が抱える問題 第3部: RSC

    の誕生 第4部: 従来の技術との違い 👈 NEXT 第5部: 開発スタイルがどう変わるか 45
  24. RSC の場合 • サーバもクライアントも React で書けるから... ◦ 同じやり方でどっちもいける ◦ データの受け渡しも自然

    ◦ Counter をレンダーした状態でサーバから返せる • どっちも React で書けるからこその強み 53
  25. SSR • よく比較されるけど...全く別物 ◦ SSR: Client Components をサーバでレンダー ◦ RSC:

    Server Components そのもの ◦ レンダー手法 ⇔ 特定の種類のコンポーネント 54
  26. RSC を使っていても、SSR できる • SC と CC が混在するツリーに対し... ◦ CC

    部分を SSR する、ことは可能 ▪ 右図の青部分が SSR される • RSC 利用時も SSR は推奨 ◦ Next.js はデフォルトでそういう挙動 ◦ RSC と併用していくもの 55 画像は https://www.plasmic.app/blog/how-react-server-components-work より引用
  27. 目次 第1部: SPA と SSR 第2部: SSR が抱える問題 第3部: RSC

    の誕生 第4部: 従来の技術との違い 第5部: 開発スタイルがどう変わるか 👈 NEXT 56
  28. 変わること ①: data fetch の仕方 • 今までは... ◦ getServerSideProps などから

    data fetch してた • これからは... ◦ RSC から直接 data fetch する 58
  29. 結局のところ... • data fetch のコードを書く場所は変わったけど... ◦ getServerSideProps => RSC •

    data fetch コードの抽象化の仕方は変わっていない ◦ 今まで通りやればよい 63
  30. 変わること ②: SC / CC を使い分ける • 今までは、全部 CC だった

    • これからは... ◦ SC にできるところは SC に ▪ bundle size を減らせるので • 面倒に感じるかもしれないが... ◦ ディレクティブを付けないところは SC になる (*2) ◦ interactive にしたいところに 'use client' を付ければ OK 64 *2: 厳密には何もつけないと SC / CC のどちらでもない、未確定の状態になる。 コンポーネント利用側に応じて SC / CC どちらになるか、後から決まる。
  31. まとめ: RSC とは何か • RSC とは ◦ サーバだけで動くコンポーネント • RSC

    を使うと... ◦ サーバーでしか実行できないコードが書ける ◦ async/await が使える ◦ bundle size を削減できる 66
  32. まとめ: 変わること・変わらないこと • 従来の技術から変わってないこと ◦ サーバーでレンダーされる ◦ data fetch コードの抽象化の仕方

    • 変わったことも ◦ サーバー・クライアント両方を同じ技術 (React) で書ける ▪ 学習コスト軽減, データの受け渡しが自然に ◦ Server Actions や Taint API が使える ◦ CC だけ使う => SC / CC を使い分ける 67
  33. ディレクティブを何もつけない場合は? • SC / CC のどちらでもない、未確定の状態になる • import 元に応じて、後から決まる ◦

    SC から import された時: SC になる ◦ CC から import された時: CC になる • SC /CC どちらとしても使えるコンポーネントを作れる ◦ Shared Components と呼ばれる 70
  34. SC のためのディレクティブは? • 存在しない • ‘use server’ は Server Actions

    のためのもの • SC にするには... ◦ FW が SC だと決め打ちしてるファイルに書く ▪ Next.js なら layout.tsx, page.tsx ◦ もしくは、SC から import する • 若干ややこしい 71
  35. Server Actions の仕組み • bundler がサーバ・クライアントのコードを分離する ◦ 'use server' 部分:

    サーバ向けの bundle ◦ 'use client' 部分: クライアント (ブラウザ) 向けの bundle • action={...} は サーバに POST するコードに置換 72
  36. RSC で他にできること • 他にも色々とできることがある ◦ 段階的なレンダーと Streaming ▪ <Suspense> を境界にして、ツリーの一部分だけレンダー

    ▪ 残りは後からレンダーして、クライアントに Streaming ◦ ビルド時に SC をレンダーできる ▪ Hydration 無しで SSG 相当のことが可能 • 詳しくは RFC を参照 ◦ https://github.com/reactjs/rfcs/blob/main/text/0188-se rver-components.md 73
  37. Islands Architecture • SC/CC と非常によく似ている • ページを static 部分、interactive 部分に分割できる

    ◦ static 部分は hydration されない ◦ ネストもできる • 本質的には、Islands Architecture と SC/CC は同じ! ◦ 同じことができる ◦ では SC/CC の存在意義は何なのか 75
  38. Islands Architecture に対する SC/CC の存在意義 • React 世界における、Islands Architecture の標準化

    ◦ …だと僕は思っている • Islands Architecture は FW ごとにルールがバラバラ ◦ Fresh: islands/*.ts が interactive ◦ Astro: client:* ディレクティブつけたものが interactive • ルールがバラバラだと... ◦ FW ごとに使い方を覚えないといけない ◦ どの FW でも動く interactive component を作るのが困難 76
  39. • 「React ならこのやり方で!」を決めたのが SC/CC • 標準化されることで... ◦ どの FW でも

    (React ベースなら) 同じやり方で OK ◦ Server Component 向けのライブラリが作れる • 標準規格の上に、追加の機能を設けられる ◦ Server Action, Taint API ◦ async/await 77 Islands Architecture に対する SC/CC の存在意義
  40. GraphQL は RSC で使える? • YES • GraphQL は data

    fetch の一手段でしかない • 主な data fetch の手段 ◦ fetch(...) ◦ db.user.get(...) ◦ fetchQuery(...) (GraphQL) • db.user.get(...) を fetchQuery(...) に置き換えたら OK 78
  41. data fetch に GraphQL を使う利点はあるか • まとめてデータを取得できる ◦ ユーザ情報 +

    記事一覧 + 記事ランキング ◦ 1回のリクエストで複数のデータを取れる • fetch (...) を複数回呼んで data fetch するのと比べて... ◦ 総通信時間が減る ◦ "理論上は" GraphQL のほうが速い 79
  42. data fetch に GraphQL を使う利点はあるか • ただし、その速度向上はほんの僅か ◦ AWS なら同一

    AZ 内のマシンとの RTT は 数百 μs 未満 (*5) ▪ 数往復減ったところで、精々 1 ms の短縮になるだけ ◦ 総通信時間の短縮のために GraphQL を使う意味はあまりない • そもそもの話、RSC 実行環境が DB にアクセス可能なら... ◦ db.user.get(...) のほうがずっと効率的なはず 80 *5: 日本国内の AZ においての話。 https://zenn.dev/tsumita7/articles/aws-mesuring-latency-among-az-2024
  43. RSC のイマイチなところは? • SC にする方法がややこしい ◦ ‘use server’ は SC

    にするためのディレクティブではない ◦ ディレクティブをつけないのが正解 • サポートする FW がまだ少ない ◦ Next.js, Waku, Redwood ▪ 安定してるのは Next.js だけ ◦ Vike で実装中 ◦ Remix: v3 (次期 major) でサポートすることに前向き(*4) 81 *4: https://remix.run/blog/remix-v2 より
  44. どのフレームワーク使うと良い? • RSC サポートしてる Production Ready な FW は... ◦

    Next.js (App Router) しかない • RSC 使いたいなら... ◦ Next.js App Router 使うしかない 82
  45. どのフレームワーク使うと良い? • けどフィットするかは、プロダクトによると思う ◦ Edge 最適化のために色々 API が制限されてる ▪ Middleware

    で node:fs モジュール使えないとか (*6) ◦ ルーティング最適化のために API が制限されてる ▪ layout.tsx で searchParams 触れない ◦ 積極的なキャッシュ (v15 で廃止予定ではある) ◦ Server Actions のエンドポイント URL がデプロイ毎に変わる ▪ Vercel の Skew Protection (有料) 導入したら良いらしいが... ▪ そもそもエンドポイントの URL そのままにして欲しい... 83 *6: 最近になって Node.js API をサポートする動きがあるので、そのうち使えるようになるかも https://github.com/vercel/next.js/discussions/46722#discussioncomment-10262088
  46. どのフレームワーク使うと良い? • そこが気に入らなければ... ◦ 他のフレームワークを検討しよう • 例えば Remix とか ◦

    Next.js と大体同じことができる ◦ RSC はサポートされていないが、サポートの計画はある ◦ RSC サポートされたら多少書き方変わると思うけど... ▪ いざとなったらガッツを出して移行しよう 84
  47. RSC 時代の漏洩ポイント • RSC 時代の主な漏洩ポイントは、以下 2 つ ◦ CC のソースコード

    ▪ ブラウザの bundle に含まれるため (P23 参照) ◦ SC から CC に渡した props ▪ HTML にシリアライズされるため (P41 参照) 86
  48. RSC 登場以前の漏洩ポイント • 実は RSC 登場以前も、似たような漏洩ポイントがあった ◦ CC のソースコード ▪

    RSC 登場以前は全て CC だから...つまり全てのコンポーネント! ◦ getServerSideProps から返した pageProps (Next.js) ▪ Remix の loader でも同じ ◦ _buildManifest.js (Next.js) ▪ 全ページのパスとサブリソース (JS, CSS) の URL が載ってる ▪ ページを IP アドレス制限していても丸見えになってる! 87
  49. 漏洩リスクの変化 • 以前のほうが漏洩ポイントは多い ◦ そういう点では、RSC 時代は改善してる • CC や、CC に渡す

    props に機密情報を含めなければ OK ◦ けど、CC かどうか一目で分かりづらい ▪ 'use client' が付いてるものは CC だけど... ▪ 付いてないものは SC にも CC にもなりうる (P70 参照) • そもそも RSC ではサーバ専用 API が気軽に使えるので... ◦ 機密情報を扱う機会も増えてる ◦ 一概に RSC 時代のほうが安全、とも言えない 88
  50. 機密情報の漏洩への対策 • まず漏洩リスクを意識しながらコードを書こう ◦ 当たり前だけど大事 • その上で... ◦ CC や、CC

    に渡す props に機密情報を含めない ◦ Taint API を 使う ◦ import 'server-only' を使う ▪ クライアントの bundle に含まれてしまうのを防げる ◦ 機密情報が漏れてないか、定期的にチェックする ▪ bundle を grep したり、chrome devtools の検索機能を使ったり (*7) 89 *7: https://www.mizdra.net/entry/2023/01/13/123211