Slide 1

Slide 1 text

GraphQL Client + 状態管理ライブラリを調査した話 渡辺 瑞⽣ 1

Slide 2

Slide 2 text

名前 渡辺 瑞生 所属 動画配信開発部 プレミアムグループ 23年新卒 仕事 DMM TV (Webブラウザ) のフロントエンド開発・保守 X (Twitter) @shikachii 趣味 バイク・サウナ・シーシャ よく使う技術 React, Next.js, TypeScript

Slide 3

Slide 3 text

アジェンダ - DMM TV (Webブラウザ) FEの技術構成 - Apollo Clientとは - 気になる点 - ライブラリ調査 - GraphQL Client - 状態管理ライブラリ - まとめ - 宣伝 3

Slide 4

Slide 4 text

技術構成 4 Next.js フレームワーク Apollo Client GraphQL Client + 状態管理ライブラリ GraphQL Server フロントエンド データベース クライアント

Slide 5

Slide 5 text

Apollo Clientとは - GraphQLを使⽤したリモート&ローカルデータの状態管理ライブラリ - スター数:19,118 (2024/1/28現在) 5 Apollo Client is a state management library that simplifies managing remote and local data with GraphQL. https://www.apollographql.com/docs/react/why-apollo より引用

Slide 6

Slide 6 text

Normalized Cache(正規化キャッシュ) 6 { __typename: "Person", id: "hogehoge", name: "shikachii", company: { __typename: "Company", id: "fugafuga", name: "DMM.com" } } { __typename: "Person", id: "hogehoge", name: "shikachii", company: { __ref: "Company:fugafuga" } } { __typename: "Company", id: "fugafuga", name: "DMM.com" } 取得したデータ メモリ内キャッシュ 効率的なキャッシュの更新が可能

Slide 7

Slide 7 text

Reactive Variables - Apollo Clientキャッシュから分離された外部のローカル状態を管理 7 import { makeVar, useReactiveVar } from "@apollo/client" ; const hogeItemVar = makeVar("hoge"); export const HogeComponent = () => { const hogeItem = useReactiveVar (hogeItemVar); // 即反映 // ... } export const FugaComponent = () => { useEffect(() => { hogeItemVar("fuga"); // fugaに更新 }, []) // ... } シンプルな構文

Slide 8

Slide 8 text

気になる点 - 学習コスト - プロダクトが⼤きくなるにつれてキャッシュを把握しづらくなる - 多重リクエストが送られるバグが発⽣し負荷増加した例も - プロダクトの特性 + ライブラリの機能を理解した上で実装 - 新しいReactへの順応が⽐較的遅い - 最近はSuspenseに対応したりFragment Colocationにも⼀部対応 8 他ライブラリの調査を開始

Slide 9

Slide 9 text

調査対象 9 - URQL - Relay - graphql-request - Redux Toolkit - Recoil - Jotai GraphQL Client 状態管理ライブラリ

Slide 10

Slide 10 text

重視する基準 - GraphQL Client 1. 機能⾯ - キャッシュ機構 - ⾃動型⽣成 2. コスト⾯ - 学習コスト - 移⾏コスト - すぐに移⾏するわけではないが⼀応調査 10

Slide 11

Slide 11 text

URQL - 🟢 キャッシュ機構 - Document Caching (無期限に保存) - 拡張で正規化キャッシュも使⽤可能 - 🟢 ⾃動型⽣成 - GraphQL Code Generator - 🟡 学習コスト - 新しいキャッシュ機構の学習が必要 - 🟢 移⾏コスト - ⾃動型⽣成ツールがApollo Clientと同じ 11 🟢: よさそう 🟡: なんとかなりそう 🔴: たいへんそう

Slide 12

Slide 12 text

URQL - Document Caching 12 hash(query+variables) を キーにして結果をキャッシュ query GetPerson($id: ID!) { person(id: $id) { __typename // “Person” id name } } { __typename: "Person", id: "hogehoge", name: "shikachii", } mutation DeletePerson($id: ID!) { deletePerson(id: $id) { __typename // “Person” id // “fugafuga” } } 同じ__typenameのキャッシュを破棄💥 { __typename: "Person", id: "fugafuga", } hash(stringify(query) + stringify({ id: "hogehoge" }))

Slide 13

Slide 13 text

Relay - 🟢 キャッシュ機構 - 正規化キャッシュ - キャッシュのGC - 🟢 ⾃動型⽣成 - Relay Compiler - 🟡 学習コスト - Fragment Colocation - 🔴 移⾏コスト - コンポーネント単位でのデータフェッチ 13

Slide 14

Slide 14 text

graphql-request + (SWR or Tanstack Query) - 🟢 キャッシュ機構 - SWRやTanstack Queryとの併⽤ - 🟢 ⾃動型⽣成 - GraphQL Code Generator - 🟡 学習コスト - SWR, Tanstack Query - 🟡 移⾏コスト - 既存のデータフェッチ部分の検証が必要 14

Slide 15

Slide 15 text

⽐較 - GraphQL Client - 主観 + 定性的に⽐較 15 ※各ライブラリの良し悪しではありません キャッシュ機構 自動型生成 学習コスト 移行コスト Apollo Client 🟢 🟢 🟡 N/A URQL 🟢 🟢 🟡 🟢 Relay 🟢 🟢 🟡 🔴 graphql-request 🔴 🟢 🟢 🟢 + SWR or TanstackQuery 🟢 🟢 🟡 🟡

Slide 16

Slide 16 text

重視する基準 - 状態管理ライブラリ 1. 機能⾯ - 構⽂のシンプルさ - 無駄な再レンダリングが発⽣しない 2. コスト⾯ - 学習コスト - 移⾏コスト - 構⽂がシンプルなら低いので省略 16

Slide 17

Slide 17 text

Redux Toolkit - 🔴 構⽂のシンプルさ - アーキテクチャがしっかりしている分複雑 - Zustandではシンプルに書くことはできる - 🔴 学習コスト - プロダクト特性を理解した上でFluxアーキテクチャの適⽤ 17 Action Dispatcher Store View Action

Slide 18

Slide 18 text

Recoil - 🟡 構⽂のシンプルさ - atomを使うだけであればかなりシンプル - ⼀意のkeyを指定しないといけない - 🟢 学習コスト - useStateと同じような感覚で利⽤可能 18 Selector Atom “shikachii” Selector Atom+”san” import { atom, useRecoilState } from "recoil"; const hogeItemAtom = atom({ key: 'hogeItem', default: 'hoge' }) export const HogeComponent = () => { const [hogeItem, setHogeItem] = useRecoilState (hogeItemAtom); // ... }

Slide 19

Slide 19 text

Jotai - 🟢 構⽂のシンプルさ - Recoilで必要だった⼀意のkey管理が不要 - 🟢 学習コスト - Recoil, useStateと同じような感覚で利⽤可能 19 import { makeVar, useReactiveVar } from "@apollo/client"; const valueVar = makeVar(0); export const HogeComponent = () => { const value = useReactiveVar(valueVar); valueVar(value + 1); // ... } import { atom, useAtom } from 'jotai'; const valueAtom = atom(0); export const HogeComponent = () => { const [value, setValue] = useAtom(valueAtom); setValue(value + 1); // ... }

Slide 20

Slide 20 text

⽐較 - 状態管理ライブラリ - 主観 + 定性的に⽐較 20 シンプルさ 学習コスト Apollo Client 🟢 N/A Redux Toolkit 🔴 🔴 Recoil 🟡 🟢 Jotai 🟢 🟢

Slide 21

Slide 21 text

調査結果をふまえて - DMM TVはコンテンツ配信型のアプリケーション - Mutation < Query - Document Cachingを活かせる → URQLよさそう - 現状複雑な状態管理をしていない - Jotaiの機能があれば必要⼗分 21 他ライブラリの調査を通じてプロダクトに対する解像度が向上

Slide 22

Slide 22 text

まとめ 22 1. DMM TV WebブラウザチームではApollo Clientを使⽤ 2. 気になる点がいくつかあり他ライブラリを調査 3. コンテンツ配信型であればURQL+Jotaiがよさそう 4. Apollo Clientの客観的理解 + ⾃⼰成⻑

Slide 23

Slide 23 text

宣伝 - DMM TVのOP/EDスキップ機能がさらに便利に! - スキップ⽅法を ⾃動‧⼿動‧オフ にカスタマイズ可能! - プレミアムグループでは⼀緒に働く仲間を募集中です! 23

Slide 24

Slide 24 text

補⾜ 24

Slide 25

Slide 25 text

Fragment Colocation - ウォーターフォール問題の改善 - 親で全てのデータを取得 - 関⼼の分離 - コンポーネントが使う値のみ考慮 25 query UserPage($id: ID!) { person(id: $id) { id name company { id name } } } fragment Company on Company { id name } query UserPage($id: ID!) { person(id: $id) { id name company { ...Company } } }