Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
10周年を迎えたZEN Study Webフロントエンドの次の10年へ向けた工夫
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
tris
May 25, 2026
Programming
54
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
10周年を迎えたZEN Study Webフロントエンドの次の10年へ向けた工夫
TSKaigi 2026 スポンサーセッション
https://2026.tskaigi.org/talks/85
tris
May 25, 2026
More Decks by tris
See All by tris
TypeScript だけを書いて Tauri でデスクトップアプリを作ろう / Tauri with only TypeScript
tris5572
2
3.1k
Other Decks in Programming
See All in Programming
AIが無かった頃の素敵な出会いの話
codmoninc
1
230
コーディングルールの鮮度を保ちたい for SRE NEXT 2026 / keep-fresh-go-internal-conventions-sre-next-2026
handlename
0
150
What's New in Android 2026
veronikapj
0
200
分散システム、なんですぐ死んでしまうん?耐障害性を高めたいあなたのためのレジリエンスパターン入門
mshibuya
7
6.9k
Built Our Own Background Agent at LayerX #aidevex_findy
layerx
PRO
8
3.4k
アルゴリズムは何を圧縮しているのか ─ Haskell から育った「圧縮代数」というメンタルモデル
naoya
16
3.6k
【やさしく解説 設計編・中級 #6】良いアーキテクチャとは ~ 一本の登り道の、行き先 ~
panda728
PRO
0
180
ビデオ通話が繋がる0.2秒で何が起きているのか
supurazako
2
150
JAWS-UG横浜 #102 AWSサ終供養LT会 成仏できない AWS サービスたち 〜本日、三体供養します〜
maroon1st
0
240
【やさしく解説 設計編 #1】「ドメイン駆動」と「実装駆動」ってなに? 〜設計の考え方を、たとえ話で学ぼう〜
panda728
PRO
1
130
音楽のための関数型プログラミング言語mimiumにおける多段階計算の活用
tomoyanonymous
1
360
これからAgentCoreを触る方へトレンドはGatewayです
har1101
6
500
Featured
See All Featured
AI Search: Where Are We & What Can We Do About It?
aleyda
0
7.7k
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
1k
Mind Mapping
helmedeiros
PRO
1
290
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6k
Discover your Explorer Soul
emna__ayadi
2
1.2k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
55
3.4k
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
65
57k
New Earth Scene 8
popppiees
3
2.4k
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
470
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.5k
Context Engineering - Making Every Token Count
addyosmani
9
1k
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.7k
Transcript
10周年を迎えた ZEN Study Webフロントエンドの 次の10年へ向けた工夫 株式会社ドワンゴ 教育事業 (小松) 2026/05/23 TSKaigi 2026
スポンサーセッション
自己紹介 • ドワンゴ 教育事業本部 e-learningプロダクト開発部 ZEN Study Webフロントエンドセクション 小松(こまつ) • ここ2年ほど
ZEN Study の開発に従事 • 趣味の1つ: ドライブ【距離ガバ】 日本本土の海沿いを走破+α 2 ニコニコ超会議の 配信に映り込む 不審なスタッフ
本セッションのサマリー • ドワンゴ教育事業とZEN Studyの概要 ◦ 事業・サービス ◦ これまでの10年のごく簡単な振り返り • 次の10年に向けてメンテナンス性を向上するために行なっている取り組みの一
部を、主にTypeScriptの観点から紹介 ◦ 外部とのやり取りの結果として、判別可能なユニオン型を活用し、状態を詳細にモ デリング (お昼休憩中なので、頭を使う難しい話ではなく、基本的な話をします) 3
ドワンゴの教育事業とは (1) • 主にN高グループとZEN大学に対して、教育に関するシステムを提供 N高グループ(N・S・R高) KADOKAWAとドワンゴが創るネットの高校 2016年4月開校(N高) 生徒数 3万人規模 ZEN大学
日本財団とドワンゴが創るオンライン大学 2025年4月開学 学生数 7千人規模 4
ドワンゴの教育事業とは (2) • 生徒・学生が直接使用するアプリや、学校の運営に関するシステムを提供 ◦ 学習アプリ ZEN Study ▪ 各学校向けだけではなく、一般向けにも月額課金で提供
◦ 教材作成・学校運営に関連するシステム 5
ZEN Study 10周年 🎉 • 2016年4月に「N予備校」としてサービス開始 10周年記念サイト https://www.nnn.ed.nico/pages/10th_anniversary サービスの歴史 ドワンゴ教育サービス開発者ブログ
https://blog.nnn.dev/ 10年を技術面から振り返る記事 6
10年の歴史がある = 10年の積み重ねがある • 最初の構築が急ピッチで行なわれた ◦ N高の開校と同時にリリースする必要があった ▪ 期限が決まっている中で、完全新規プロダクトの構築が行なわれた ◦
開発スピードを優先した選択が行なわれた ▪ メンテナンス性の優先度は低かった • 複雑性が積み重なっている ◦ 新機能の追加 ◦ 対象となる学校の増加 ▪ S高・R高の開校、大学の開学 ◦ リファクタリングが追い付いていないコード 7
ZEN Study 関連の開発体制 • フロントエンドはプラットフォームごとに分かれている • バックエンドは別チーム 8
ZEN Study Webフロントエンドの主な技術スタック • TypeScript + React SPA ◦ (Reactを10年使っているサービスは古参と言えそう)
• TanStack Query • React Router • React Redux 9
ZEN Study Webフロントエンド 10年間の技術変遷 次の10年を見据えて、コードベースの改善を継続して行なっている 10
次の10年に向けて メンテナンス性を向上するための 取り組み(の一部) 11 判別可能なユニオン型を利用して、状態を正確にモデリングする
判別可能なユニオン型を利用する方法の概要 • 外部 API 呼び出しの結果は、成功/失敗のケースがある • それなら汎用的な Result<T, E> を使うのが良さそう
😊 ◦ みんな Result を好きすぎて無限に Result の話をしている • でも Result<T, E> だけではツラいケースがある 😰 ◦ ローディング状態をどう表現する? ◦ エラーの中身をどうやって分ける? ◦ 使う側(コンポーネント)で細かい判定が必要で扱いにくい? ⇩ 判別可能なユニオン型を利用して、状態を正確にモデリングする方法を採用 (本質的には、外部への依存と内部実装とを切り分けている) 12
パターン1. コンポーネントで直接データを取得する • 🔥 データ取得と表示の責務が分離されていない • 🔥 コンポーネントのテストが大変 ◦ 通信をモックする必要がある
13
パターン2. データ取得をカスタムフックに分離する • 👍 データ取得と表示の責務が分離された • 👍 コンポーネントのテストが楽になった ◦ 通信ではなくカスタムフックをモックすれば良い
• 🔥 コンポーネントでの表示のために相変わらず色々と気にする必要がある ◦ 例:ローディングの判定のために特定のフラグを見る必要がある 14
パターン3. 汎用 Result (neverthrow) を使ってみる • 👍 コンポーネントでの表示時に気にすることが減った ◦ 特定の値(フラグ)を元にした判定が不要
• 🔥 ユニオン型と Result の使い分けが必要 ◦ 成功/失敗は Result だが、それ以外はユニオン型なので扱い方が異なる 15 ローディング中は undefined を 返すこととする
パターン4. カスタムフックが返す型としてユニオン型を利用 • この方式を我々は採用 • 👍 データ取得などの外部とのやり取りと表示の責務がより分離された • 👍 数々の嬉しさがある(後述)
判別可能なユニオン 16
ユニオン型を使う嬉しさ (1) • コンポーネントで switch 文によって状態ごとに容易に出し分けられる ◦ 複数の値を元にした状態の判別が不要 ◦ データの取得状態/結果とコンポーネントの表示を明確に関連付けられる
• 網羅性チェックを行える ◦ パターン追加時の堅牢性が上がる 網羅性チェックを実施可能 switch 文により単純に出し分け可能 判別可能なユニオン 17
ユニオン型を使う嬉しさ (2) • データ取得成功時のみ値があることが型で保証される ◦ 値を取得できていない状態のときは、値にアクセスできない • SUCCESS のときのみ値がある •
LOADING や ERROR のときには値がない ERROR などの状態で 値にアクセスを試みると 型エラーになる 18
ユニオン型を使う嬉しさ (3) • エラーの状態を細かく表現可能 その1 ◦ 例:リロードによる復旧可否を分ける リロードしても意味がないエラーと リロードにより復旧できるエラーを 別の型にする
リロードボタンの表示を容易に出し分けられる 19
ユニオン型を使う嬉しさ (4) • エラーの状態を細かく表現可能 その2 ◦ 例:HTTPステータスコードからコンポーネントを分離 ▪ エラーコードへ依存しないので、REST API
から GraphQL へ切り替えるような 場合も、コンポーネントに手を入れる必要がない HTTPステータスコードを直接利用するのではなく、 エラー種別ごとに型を作って、ステータスコードに 依存しないようにする 20
ユニオン型を使う嬉しさ (5) • エラーの状態を細かく表現可能 その3 ◦ 例:追加読み込みを行う API/コンポーネントにおいて、初回読込み時のエラーと追 加読込み時のエラーを区別する ▪
状況によりエラーの表示方法を変えるのが容易(初回読込み時だとエラーペー ジ、追加読込み時だとスナックバー、など) ▪ 単純に useInfiniteQuery を使うより複雑になっているとも言えるが、何を 優先するかのトレードオフ次第 21
ユニオン型を使う嬉しさ (6) • 実現方法の切替をコンポーネントと分離可能 ◦ 例:状態(履歴など)をサーバーに保存して全クライアントで同期したいが、ひと まず暫定的にローカルストレージへ保存して最低限改善する ▪ まずは暫定対応として、ローカルストレージへ保存するカスタムフックを実装 ▪
バックエンドの準備が完了し次第、API 呼び出しによりサーバーへ保存するカ スタムフックを実装 ▪ この前後で、カスタムフックの型シグネチャ(特に成功/失敗を表す戻り値の ユニオン型)が変わらなければ、コンポーネントの実装ではこの切替を意識す る必要がない 22 Webフロントエンド バックエンド まずは暫定的に ローカル保存 バックエンドの準備完了後、 サーバーへ保存 コンポーネント 呼び出しの型シグネチャに変更がなければ 意識せずに切替可能
まとめ • ZEN Study は10周年を迎えることができた 🎉 • 10年の積み重ねを踏まえつつ、次の10年に向けて改善を重ねている • TypeScriptの強力な機能である、判別可能なユニオン型を活用して、データ取
得などの状態(外部への依存)を正確にモデリングすることにより、堅牢でメ ンテナンスしやすくできた • 他にも様々な工夫・改善を行なっています! 23 めでたし めでたし 🤟