Slide 1

Slide 1 text

App Router時代のデータ 取得アーキテクチャ 2023-08-02 What's "Next" JS Meetup 基調講演 uhyo (株式会社バベル プリンシパルエンジニア)

Slide 2

Slide 2 text

登壇者紹介 uhyo 2022年10月から株式会社バベル勤務。 設計やレビューを中心としつつ、 何でも屋として活動。 TypeScript入門書 好評発売中! 今年も増刷した→

Slide 3

Slide 3 text

会社紹介 株式会社バベル 社名のわりにJavaScriptのビルドに Babelを使っていないことで有名。 “世界中の人々の役に立つ事業を創り続ける”をミッションとする

Slide 4

Slide 4 text

プロダクト紹介 商談解析クラウド セールス分野をターゲットとし、営業現場の業務効率化 と「売れる」営業人材の育成を支援する。 • オンライン商談を録画し、AIが解析 • 効率的に見返しフィードバックすることができる • 議事録にもなる

Slide 5

Slide 5 text

復習のコーナー

Slide 6

Slide 6 text

Next.jsって何だっけ? Reactアプリケーション向けに、 主にルーターとサーバー実装を提供してくれる フレームワーク • ルーター: URLに応じてコンテンツを出し分ける機構 • サーバー実装: いわゆるSSRやキャッシュの機能を担当 あとバンドルの最適化まわりを頑張ってくれる

Slide 7

Slide 7 text

App Routerって何だっけ? Next.js 13で追加された、新しいルーターの実装。 従来のPages Routerと対比してApp Routerと 呼ばれる 特徴: • React Server Components (RSC) の上に作られている • Pages Routerには無いさまざまな機能

Slide 8

Slide 8 text

レンダリング 最も単純なルーター location.href switch文的な何か ルーティング定義 DOM

Slide 9

Slide 9 text

レンダリング ファイルシステムベースルーティング ルーティング定義がファイルシステム上で行われるもの。 フレームワークが好んで採用する傾向にある location.href switch文的な何か ルーティング定義 ファイル システム DOM

Slide 10

Slide 10 text

レンダリング SSR機能 サーバー クライアント リクエストURL switch文的な何か HTML文字列 DOM バンドルされた JavaScript ハイドレーション クライアント側ナビゲーションのときも追加のバンドルを取得したりする動きがあるが省略。 以降もナビゲーションの話は特筆すべき事情がない限り省略します レンダリング

Slide 11

Slide 11 text

データフェッチングって? アプリケーションの外部からデータを取得し、表示に 利用すること。 SSRしないアプリケーションであれば、普通にクライア ントからfetchしてあとは煮るなり焼くなりすれば いい。 SSRする場合は話がややこしく、フレームワークが サポートしてくれる。 ※ そもそもSSRすべきかどうかの話は別の話題なので、場外乱闘はお控えください

Slide 12

Slide 12 text

React 18より前の事情 サーバーサイドレンダリング用のAPI(renderToStringとか)は 同期的なレンダリングを行うAPIである。 → レンダリングの最中にデータフェッチングを行うのは不可。 → Reactアプリケーションの外部のAPIを使うことになる。 • getServerSidePropsとかそのあたり

Slide 13

Slide 13 text

SSR + データフェッチング サーバー クライアント リクエストURL switch文的な何か バンドルされた JavaScript データフェッチング レンダリング HTML文字列 DOM ハイドレーション レンダリング getServerSidePropsとか

Slide 14

Slide 14 text

React 18の事情 Suspenseのサポートにより、SSR時も非同期的な レンダリングが可能になった(Streaming対応SSR) • SSRの最中にサスペンドしたら待ってくれて、サスペンド解消したら追 加のコンテンツを出力してくれる • レンダリングの最中にデータフェッチングを行えるように • アーキテクチャ的には、Suspenseの導入によりステートに頼らずに ローディング中を表現できるようになったことが大きい (SSR中にステートを変化させるのは無理なので)

Slide 15

Slide 15 text

レンダリング Streaming SSR + データフェッチング サーバー クライアント リクエストURL switch文的な何か バンドルされた JavaScript データフェッチング HTML文字列 DOM ハイドレーション レンダリング データフェッチング

Slide 16

Slide 16 text

レンダリング Streaming SSR + データフェッチング サーバー リクエストURL switch文的な何か バンドルされた JavaScript データフェッチング HTML文字列 DOM ハイドレーション レンダリング データフェッチング Suspense対応により、データ フェッチングがレンダリングの 一部になった。

Slide 17

Slide 17 text

レンダリング Streaming SSR + データフェッチング サーバー リクエストURL switch文的な何か バンドルされた JavaScript データフェッチング HTML文字列 DOM ハイドレーション レンダリング データフェッチング SSRではサーバーとクライアン トで同じものをレンダリングす るので、データフェッチングの ロジックも両側に必要になった

Slide 18

Slide 18 text

レンダリング Streaming SSR + データフェッチング サーバー リクエストURL switch文的な何か バンドルされた JavaScript データフェッチング HTML文字列 DOM ハイドレーション レンダリング データフェッチング リクエストの重複排除などを考 えるとめちゃくちゃ面倒。 Streaming SSRだけに頼る のはきつい キャッシュを 渡す?

Slide 19

Slide 19 text

RSCがもたらした変化

Slide 20

Slide 20 text

RSCって何だっけ? React Server Components (RSC) では、 サーバー側コンポーネントとクライアント側コンポーネ ントという概念が導入される。 Reactアプリケーション全体で見れば、サーバー側で実 行される部分とクライアント側で実行される部分に分割 されている。 宣伝: 一言で理解するReact Server Components

Slide 21

Slide 21 text

レンダリング RSCのレンダリング ※ SSRしない場合の例です サーバー クライアント DOM Reactアプリケーション サーバー側 + クライアント側 レンダリング Reactアプリケーション ただのHTML + クライアント側 バンドルされた JavaScript

Slide 22

Slide 22 text

レンダリング RSCのレンダリング ※ SSRしない場合の例です サーバー DOM Reactアプリケーション サーバー側 + クライアント側 レンダリング Reactアプリケーション ただのHTML + クライアント側 バンドルされた JavaScript 2ヶ所のレンダリングで、それ ぞれサーバー側コンポーネント とクライアント側コンポーネン トのレンダリングを行う。

Slide 23

Slide 23 text

RSC + SSR RSCはフレームワークを介して使うことが多い。フレー ムワークは普通SSRもサポートしているので、実用上は RSCとSSRを併用することが多い。 ※ いわゆるSGとかその辺りはSSRに対する最適化とみなして、 このトークでは全部まとめてSSRとして取り扱います

Slide 24

Slide 24 text

レンダリング (サーバー側) RSC + SSR サーバー クライアント DOM Reactアプリケーション サーバー側 + クライアント側 Reactアプリケーション ただのHTML + クライアント側 バンドルされた JavaScript レンダリング (クライアント側) HTML文字列 レンダリング (クライアント側) ハイドレーション

Slide 25

Slide 25 text

レンダリング (サーバー側) RSC + SSR サーバー DOM Reactアプリケーション サーバー側 + クライアント側 Reactアプリケーション ただのHTML + クライアント側 バンドルされた JavaScript レンダリング (クライアント側) HTML文字列 レンダリング (クライアント側) ハイドレーション ここがSSR要素 (本来クライアント側で行うレ ンダリングをサーバー側でも行 う)

Slide 26

Slide 26 text

RSCとデータフェッチング RSCは、サーバー側コンポーネント内で直接データ フェッチングできることを売りのひとつとしている。 • コンポーネントをasync関数にできる • →やはり状態管理無しでデータフェッチング可能

Slide 27

Slide 27 text

サーバー クライアント レンダリング(サーバー側) RSC + SSR DOM Reactアプリケーション サーバー側 + クライアント側 Reactアプリケーション ただのHTML + クライアント側 バンドルされた JavaScript レンダリング (クライアント側) HTML文字列 レンダリング (クライアント側) ハイドレーション データフェッチング

Slide 28

Slide 28 text

RSCとデータフェッチングのポイント データフェッチングがレンダリング(サーバー側)に 含まれているため、SSRしてもサーバー側とクライアン ト側で処理が重複しない。 ハイドレーションのために面倒なことを考える必要が ない。 レンダリング(サーバー側) データフェッチング

Slide 29

Slide 29 text

昔のNext.jsとの比較 昔のNext.js RSC(サーバー側) リクエスト データフェッチング レンダリング HTML文字列 getServerSidePropsとか レンダリング(サーバー側) レンダリング (クライアント側) HTML文字列 データフェッチング リクエスト

Slide 30

Slide 30 text

昔のNext.jsとの比較 データフェッチングに着目して比較すると、 共通点: • (クライアント側の)レンダリングより前に データフェッチングを済ませてしまうので、 ハイドレーション周りの面倒ごとが無い。 異なる点: • データフェッチングをReactアプリのレンダリングの一部 として行うか否か。

Slide 31

Slide 31 text

昔のNext.jsとの比較 Q. RSCでは、なぜデータフェッチングがReactアプリの 外からReactアプリの中に移動したのか?

Slide 32

Slide 32 text

昔のNext.jsとの比較 Q. RSCでは、なぜデータフェッチングがReactアプリの 外からReactアプリの中に移動したのか? A. データフェッチングの方法の標準化のため ※もちろん理由は1つではないが、この理由は確かDan先生がTwitter(当時)で言っていた

Slide 33

Slide 33 text

昔のNext.jsとの比較 昔のNext.js リクエスト データフェッチング レンダリング HTML文字列 getServerSidePropsとか レンダリング(サーバー側) レンダリング (クライアント側) HTML文字列 データフェッチング リクエスト RSCの導入により、Next.jsの独 自概念が要らなくなった

Slide 34

Slide 34 text

昔のNext.jsとの比較 Q. RSCでは、なぜデータフェッチングがReactアプリの 外からReactアプリの中に移動したのか? A. データフェッチングの方法の標準化のため 昔のgetServerSideProps等はReact外の概念だったが、 新しいAPI (headers()とか)はRSCの上に作られている。 フレームワーク独自の機構が減り、フレームワーク間での共通化が進んだ。 (Reactが定める“標準”に合わせた機構となった) (RSCを受け入れないフレームワークは別)

Slide 35

Slide 35 text

App Router時代の データ取得アーキテクチャ

Slide 36

Slide 36 text

データ取得のポイント 目指すべき目標: サーバー側とクライアント側の連携をRSCに任せ、 ユーザーランドで苦労しない。 ユーザーランドの苦労の例: GraphQLクライアントのキャッシュをサーバー側からクライアント側に コピーするとか

Slide 37

Slide 37 text

サーバー クライアント レンダリング(サーバー側) RSC + SSR(再掲) DOM Reactアプリケーション サーバー側 + クライアント側 Reactアプリケーション ただのHTML + クライアント側 バンドルされた JavaScript レンダリング (クライアント側) HTML文字列 レンダリング (クライアント側) ハイドレーション データフェッチング

Slide 38

Slide 38 text

サーバー クライアント レンダリング(サーバー側) RSC + SSR(再掲) DOM Reactアプリケーション サーバー側 + クライアント側 Reactアプリケーション ただのHTML + クライアント側 バンドルされた JavaScript レンダリング (クライアント側) HTML文字列 レンダリング (クライアント側) ハイドレーション データフェッチング RSCによるサーバー・クライア ント連携部分

Slide 39

Slide 39 text

ユーザーランドで苦労しないために データ取得はサーバー側とクライアント側の2種類に 分けてけて考えよう。 • サーバー側データ取得: SSR結果(=初期レンダリング結果)に反映されてほしいもの • クライアント側データ取得: 初期レンダリングには含まれなくていいもの

Slide 40

Slide 40 text

サーバー クライアント レンダリング(サーバー側) DOM Reactアプリケーション サーバー側 + クライアント側 Reactアプリケーション ただのHTML + クライアント側 バンドルされた JavaScript レンダリング (クライアント側) HTML文字列 レンダリング (クライアント側) ハイドレーション データフェッチング サーバー側データフェッチング クライアント側データ フェッチングを発火

Slide 41

Slide 41 text

サーバー クライアント レンダリング(サーバー側) DOM Reactアプリケーション サーバー側 + クライアント側 Reactアプリケーション ただのHTML + クライアント側 バンドルされた JavaScript レンダリング (クライアント側) HTML文字列 レンダリング (クライアント側) ハイドレーション データフェッチング ポイント: ここで データフェッチング をしない

Slide 42

Slide 42 text

ユーザーランドで苦労しないために ポイント: Reactアプリ内でデータ取得を行えるというRSCの利点 を生かしつつSSR周りの苦労をしないために、 サーバー側で行う必要があるデータ取得は全部 Server Component内で行う。 RSCがやってくれる以外でサーバー側と クライアント側のデータ共有をしようとしない。

Slide 43

Slide 43 text

Server Component内の データフェッチングについて 各論1

Slide 44

Slide 44 text

Fetch on render でいいのか? Fetch on renderは、コンポーネントがレンダリング されたらそのコンポーネントが必要とするデータの取得 が始まることを指す。 レンダリング → fetch → レンダリング → 次のfetch ……という流れで通信を直列化させる問題がある。

Slide 45

Slide 45 text

Fetch on render でいいのか? サーバーサイドでは、fetchにかかる時間が短いことが多 い。(Next.jsサーバーとDBが同じデータセンターにあ る場合など) この場合、サーバー側コンポーネントはFetch on renderでも問題ないと考えられる。 (少なくとも、パフォーマンスボトルネックが他の場所に移る可能性は高そう)

Slide 46

Slide 46 text

SC内のデータフェッチング Fetch on Renderが許容されるので、上流コンポーネン トに取得をまとめるといった工夫を考える必要がない。 よって、Server Component内ではデータを必要とする コンポーネントが各々勝手にデータを取得してもよい。 ※リクエストをまとめたい別の事情があることも考えられるので、最終的には各自の事情を鑑みて判断しよう

Slide 47

Slide 47 text

Server Componentの テスト 各論2

Slide 48

Slide 48 text

データフェッチング周りのテスト BFF的なロジックをReact内で書くとなると、 それに対するユニットテストを書きたくなる。 Server Componentのテストはどうするのが良いか?

Slide 49

Slide 49 text

データフェッチング周りのテスト BFF的なロジックをReact内で書くとなると、 それに対するユニットテストを書きたくなる。 Server Componentのテストはどうするのが良いか? A. クライアント用コンポーネントのテストと 考え方は同じ

Slide 50

Slide 50 text

テストに対する考え方2案 • 普通にSCをレンダリングしてテストする。 • 今はまだできないが、そのうちできるようになる cf. https://github.com/testing-library/react-testing-library/issues/1209 • コンポーネントそのものではなく、ロジックを関数に 抜き出してテストする。 • レンダリングされるHTMLをユニットテストする必要がない場合も多 く、ロジックをフックに抜き出してテストすれば十分だったりする。 • SCではフックですらなくただのasync関数として抜き出せるので、 むしろテストしやすいかも

Slide 51

Slide 51 text

クライアント側の 状態管理との連携 各論3

Slide 52

Slide 52 text

RSCと状態管理の懸念 従来のReactでは、データフェッチングの結果は一種の 「状態」として扱われてきた。 cf. 「3種類」で管理するReactのState戦略 一方、SCでのデータフェッチング結果は、クライアント から見たら「上からpropsで降ってくる値」であり、 もはや状態ではない。

Slide 53

Slide 53 text

RSCと状態管理の懸念 問題: SCがフェッチした状態に関してsource of truthが React内にあるので、一部の状態管理アーキテクチャと の相性が微妙 • ReduxとかRecoil型アーキテクチャでは状態管理をReactコン ポーネントから分離しているので、Reactの中→外 という微 妙なデータの流れが必要になる。 ※Recoilでは正確にはRecoilRootの中にあるが、アーキテクチャ的な話

Slide 54

Slide 54 text

RSCと状態管理の懸念 答えはまだない。 RSCと相性抜群な(クライアント用)状態管理アーキテ クチャを発明したらウケるかも?

Slide 55

Slide 55 text

まとめ App Router時代のデータ取得のポイントは、とにかく ハイドレーション周りの苦労を減らすこと。 そのためには、サーバー側にデータ取得を寄せるのが有効。 いわゆるFetch on Renderもわりと許容できる。 クライアント側状態管理との連携どうしたものか。誰か教えて ほしい

Slide 56

Slide 56 text

お約束

Slide 57

Slide 57 text

バベルでは商談解析クラウド を一緒に開発・ 運用するエンジニアを募集しています • TypeScriptでフロント・バックエンド両方書く感じ • テックリード・EMになりたい方も歓迎! • メールやTwitter(通称)でご連絡お待ちしています • カジュアルな面談できます 📧 hr@babel.jp 𝕏 @uhyo_ 採用情報