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
Node-AI のリッチな WEB フロントエンドを支える技術
Search
neno
March 10, 2024
Technology
3
1.4k
Node-AI のリッチな WEB フロントエンドを支える技術
NTT Tech Conference 2024 での発表資料
フロントエンドを Vue.js から React にリプレイスしたお話 (前編)
Node-AI
OSS (GitHub)
neno
March 10, 2024
Tweet
Share
More Decks by neno
See All by neno
C# 13 / .NET 9 の新機能 (RC 1 時点)
nenonaninu
1
1.8k
Re:ゼロから始める Observability
nenonaninu
2
770
C# ではじめる OpenTelemetry
nenonaninu
0
4.2k
.NET 8 で既定で有効になった Dynamic PGO について
nenonaninu
2
7.5k
明日から使える ASP.NET Core ロギング術!
nenonaninu
0
8.8k
C# の async/await は実際にどうやって動いているか
nenonaninu
10
25k
C# と HTTP/2 と gRPC
nenonaninu
2
8.1k
SignalR を使ったアプリケーション開発をより快適に!
nenonaninu
1
2.2k
Roslyn とその活用法
nenonaninu
2
1.4k
Other Decks in Technology
See All in Technology
OS 標準のデザインシステムを超えて - より柔軟な Flutter テーマ管理 | FlutterKaigi 2024
ronnnnn
0
170
Oracle Cloud Infrastructureデータベース・クラウド:各バージョンのサポート期間
oracle4engineer
PRO
28
13k
マルチプロダクトな開発組織で 「開発生産性」に向き合うために試みたこと / Improving Multi-Product Dev Productivity
sugamasao
1
310
VideoMamba: State Space Model for Efficient Video Understanding
chou500
0
190
Terraform CI/CD パイプラインにおける AWS CodeCommit の代替手段
hiyanger
1
240
【令和最新版】AWS Direct Connectと愉快なGWたちのおさらい
minorun365
PRO
5
760
生成AIが変えるデータ分析の全体像
ishikawa_satoru
0
160
複雑なState管理からの脱却
sansantech
PRO
1
150
Why App Signing Matters for Your Android Apps - Android Bangkok Conference 2024
akexorcist
0
130
ExaDB-D dbaascli で出来ること
oracle4engineer
PRO
0
3.9k
TypeScriptの次なる大進化なるか!? 条件型を返り値とする関数の型推論
uhyo
2
1.7k
rootlessコンテナのすゝめ - 研究室サーバーでもできる安全なコンテナ管理
kitsuya0828
3
390
Featured
See All Featured
Done Done
chrislema
181
16k
Embracing the Ebb and Flow
colly
84
4.5k
Designing for Performance
lara
604
68k
Testing 201, or: Great Expectations
jmmastey
38
7.1k
Intergalactic Javascript Robots from Outer Space
tanoku
269
27k
We Have a Design System, Now What?
morganepeng
50
7.2k
10 Git Anti Patterns You Should be Aware of
lemiorhan
655
59k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
4
370
Bootstrapping a Software Product
garrettdimon
PRO
305
110k
For a Future-Friendly Web
brad_frost
175
9.4k
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
How to train your dragon (web standard)
notwaldorf
88
5.7k
Transcript
Node-AI のリッチな WEB フロントエンドを支える技術 NTT Tech Conference 2024/03/11 何縫ねの。
自己紹介 1 • 所属: NTTコミュニケーションズ イノベーションセンター • 趣味: C#, OSS,
ドール, 一眼(α7 IV), 映画館 • 執心領域 • C# ⇔ TypeScript • SignalR 何縫ねの。 nenoNaninu nenoMake ブログ https://blog.neno.dev その他 https://neno.dev
OSS 紹介 2 属性を付与するだけ Tapper • C# の型定義から TypeScript の型定義を生成する
.NET Tool/ library • JSON / MessagePack 対応! https://github.com/nenoNaninu/Tapper
OSS 紹介 3 • C# の SignalR Client を強く型付けするための Source
Generator TypedSignalR.Client Before After (using TypedSignalR.Client) こんな SignalR の Hub と Receiver の interface が あったとして… 脱文字列! 全てが強く型付け! https://github.com/nenoNaninu/TypedSignalR.Client
4 • TypeScript の SignalR Client を強く型付けするための .NET Tool /
library TypedSignalR.Client.TypeScript Before After (using TypedSignalR.Client.TypeScript) 脱文字列! 全てが強く型付け! TypeScript 用の型を C# から自動生成 MessagePack Hub Protocol 対応! https://github.com/nenoNaninu/TypedSignalR.Client.TypeScript 属性を付与するだけ! OSS 紹介
5 • SignalR 使ったアプリを快適に開発するための GUI を自動生成する library • 2 step
で利用可能! • http pipeline に middleware の追加 • Hub と Receiver を定義してる interface に属性を付与 • JWT 認証 サポート • パラメータのユーザ定義型サポート • JSON で入力! SignalR 版 SwaggerUI TypedSignalR.Client.DevTools https://github.com/nenoNaninu/TypedSignalR.Client.DevTools OSS 紹介
AspNetCore.SignalR.OpenTelemetry OSS 紹介 6 • トレースのための計装 • 最低限のログ • 接続時
• Transport 層の情報も出力(WebSocket 等) • メソッド呼び出し時 • HubName.MethodName の素朴なログ • メソッド呼び出し毎にログのスコープを追加 • HubName, MethodName, InvocationId を 振っているのでログの検索性が向上 • Duration • 切断時 • 切断時に例外が発生していれば例外もログに出力 Inspired by HttpLogging SignalR のメソッド呼び出し毎に スパンが切られるように https://github.com/nenoNaninu/AspNetCore.SignalR.OpenTelemetry
Node-AI とは 7
Node-AI とは 8 時系列データの分析に特化した、ノーコードツール (WEB アプリ) https://nodeai.io/ β版公開中!
Node-AI とは 9 • NTT Com の Engineers’ Blog にいろいろ書いています
• https://engineers.ntt.com/entry/2024/02/19/055601 フロントエンドを Vue.js 2.x から React にリプレイスしました。
キャンバスの画的な表現 10
キャンバスの画的な表現 11 カードやテキストボックス等が 配置されている領域を「キャンバス」といいます https://nodeai.io/
キャンバスの画的な表現 12 • 普通の(?) HTML • <div> とか • Canvas
element (<canvas>) • SVG element (<svg>) • Scalable Vector Graphics どう表現するか?
キャンバスの画的な表現 13 • 普通の(?) HTML • <div> とか • Canvas
element (<canvas>) • SVG element (<svg>) • Scalable Vector Graphics どう表現するか? 結論: SVG を選択
キャンバスの画的な表現 14 • 普通の(?) HTML • <div> とか • Canvas
element (<canvas>) • SVG element (<svg>) • Scalable Vector Graphics どう表現するか? 結論: SVG を選択 なぜ?
キャンバスの画的な表現 15 ① キャンバス内に表示されるオブジェクト(カード等)の実装方法 • スタイリング/イベント/状態の取り扱いまで含めて、どう作るか。 ② 座標系の取り扱い 大事にしたかった事
キャンバスの画的な表現 16 キャンバス内に表示される要素(カード等)の実装方法 ① 普通の WEB アプリ同様に実装可能なパターン (HTML / SVG)
• SVG の子要素は普通に React で構築できる • SVG には <foreignObject> という要素があり、 <foreignObject> の子要素は普通に HTML を記述できる • そのため、スタイリング/イベント/状態の取り回しに特別な事が不要 ② 別系統の実装が必要なパターン (Canvas element) • スタイリング/状態/イベントの取り扱いが完全に別系統 • 素の React で GUI を表現する事は不可能
キャンバスの画的な表現 17 座標系の取り扱い ① 座標系の権を握れるパターン (SVG / Canvas element) •
DOM の座標系と世界座標系の分離 • 世界座標系上のどこの領域を表示するかの制御 ② 座標系の権を握れないパターン (HTML) • DOM の座標系と世界座標系の分離が困難で自由が効かない • 座標・表示領域にまつわる事ほぼ全て CSS でゴリ押し • 適切な座標系の取り扱いが何かと複雑になりがちで辛い
キャンバスの画的な表現 18 座標系の取り扱い ① 座標系の権を握れるパターン (SVG / Canvas element) •
DOM の座標系と世界座標系の分離 • 世界座標系上のどこの領域を表示するかの制御 ② 座標系の権を握れないパターン (HTML) • DOM の座標系と世界座標系の分離が困難で自由が効かない • 座標・表示領域にまつわる事ほぼ全て CSS でゴリ押し • 適切な座標系の取り扱いが何かと複雑になりがちで辛い 世界座標系 : キャンバス上のオブジェクト(カード等)が 配置されている座標系
キャンバスの画的な表現 19 座標系の取り扱い ① 座標系の権を握れるパターン (SVG / Canvas element) •
DOM の座標系と世界座標系の分離 • 世界座標系上のどこの領域を表示するかの制御 ② 座標系の権を握れないパターン (HTML) • DOM の座標系と世界座標系の分離が困難で自由が効かない • 座標・表示領域にまつわる事ほぼ全て CSS でゴリ押し • 適切な座標系の取り扱いが何かと複雑になりがちで辛い 世界座標系 : キャンバス上のオブジェクト(カード等)が 配置されている座標系 キャンバスの 拡大縮小や ミニマップで表示領域を 可視化するために必須
キャンバスの画的な表現 20 座標系の取り扱い ① 座標系の権を握れるパターン (SVG / Canvas element) •
DOM の座標系と世界座標系の分離 • 世界座標系上のどこの領域を表示するかの制御 ② 座標系の権を握れないパターン (HTML) • DOM の座標系と世界座標系の分離が困難で自由が効かない • 座標・表示領域にまつわる事ほぼ全て CSS でゴリ押し • 適切な座標系の取り扱いが何かと複雑になりがちで辛い 世界座標系 : キャンバス上のオブジェクト(カード等)が 配置されている座標系 キャンバスの 拡大縮小や ミニマップで表示領域を 可視化するために必須 ブラウザ/ライブラリに任せると 座標系の権は握れない
キャンバスの画的な表現 21 SVG のメリット • 標準的な WEB アプリ開発の知識がそのまま使える • DOM
の座標系と世界座標系の分離できる • キャンバス上のオブジェクトの表示領域の制御がしやすい
キャンバスの画的な表現 22 SVG のメリット • 標準的な WEB アプリ開発の知識がそのまま使える • DOM
の座標系と世界座標系の分離できる • キャンバス上のオブジェクトの表示領域の制御がしやすい 具体的にどのように実装するか?
キャンバスの画的な表現 23 SVG の API
キャンバスの画的な表現 24 SVG の API viewBox で 表示領域を制御
キャンバスの画的な表現 25 SVG の API viewBox で 表示領域を制御
キャンバスの画的な表現 26 SVG の API viewBox で 表示領域を制御
キャンバスの画的な表現 27 SVG の API svg の子要素に React コンポーネントの配置可能 viewBox
で 表示領域を制御
キャンバスの画的な表現 28 SVG の API transform で 座標を簡単に制御可能 svg の子要素に
React コンポーネントの配置可能 viewBox で 表示領域を制御
キャンバスの画的な表現 29 SVG の API transform で 座標を簡単に制御可能 foreignObject 内では
通常の HTML を記述可能 svg の子要素に React コンポーネントの配置可能 viewBox で 表示領域を制御
キャンバスの画的な表現 30 SVG の API 画としての表現力は十分 座標系も考えやすい 実装もしやすい transform で
座標を簡単に制御可能 foreignObject 内では 通常の HTML を記述可能 svg の子要素に React コンポーネントの配置可能 viewBox で 表示領域を制御
キャンバスの画的な表現 31 ViewBox を素で扱うのは少々難儀 ① ViewBox の適切な制御を都度考えるのは難しい • e.g., 右下隅に最大までzoom
in してから zoom outした場合の表示領域は… ② スクリーン(viewport)座標系と世界座標系の対応させる必要あり • ViewBox の値に次第で異なるので色々計算する必要あり ③ スクリーンサイズの変化に応じた ViewBox に制御 • 要するにブラウザのウィンドウサイズの変化 世界座標系は結果的に SVG 上での座標系と対応
キャンバスの画的な表現 32 カメラという概念を導入 • 開発者に ViewBox を意識させない。 • 開発者が意識するのは カメラの位置をどう動かすか。
• 細かい適切な制御はカメラの 内部実装で隠蔽。 • 座標変換系のメソッドも用意。 開発者はこれらのメソッド使えば 深い事気にする必要なし。 • ゲームエンジンっぽい API。 馴染みがある人はすんなり 理解できる。 これくらい実装すれば座標系を完全に掌握できる。 ライブラリ不要。
リアルタイム共同編集 33
リアルタイム共同編集 34 Node-AI はデータ分析業務のコラボレーションを推進しています • 1つのキャンバスに複数人で参加する • 分析者同士がいっしょに議論をしながら作業 • ドメイン知識を持っている人やステークホルダーを巻き込んで分析
リアルタイム共同編集 35 Node-AI はデータ分析業務のコラボレーションを推進しています • 1つのキャンバスに複数人で参加する • 分析者同士がいっしょに議論をしながら作業 • ドメイン知識を持っている人やステークホルダーを巻き込んで分析
リプレイス前は1つのキャンバスを 同時に複数人が操作する ユースケースに対応できていなかった
リアルタイム共同編集 36 Node-AI はデータ分析業務のコラボレーションを推進しています • 1つのキャンバスに複数人で参加する • 分析者同士がいっしょに議論をしながら作業 • ドメイン知識を持っている人やステークホルダーを巻き込んで分析
リプレイス前は1つのキャンバスを 同時に複数人が操作する ユースケースに対応できていなかった ブラウザで更新かけないと 他人の操作内容が表示されなかった
リアルタイム共同編集 37 • リアルタイム共同編集 • Undo/Redo リプレイスを機に実現したい事
リアルタイム共同編集 38 • リアルタイム共同編集 • Undo/Redo リプレイスを機に実現したい事 後から実現するのはめちゃ大変
• SignalR: リアルタイムの双方向 RPC library リアルタイム共同編集 39 リアルタイム通信は SignalR Front-end
(TypeScript / React) Back-end (C# / ASP.NET Core) SignalR
• SignalR: リアルタイムの双方向 RPC library リアルタイム共同編集 40 リアルタイム通信は SignalR Front-end
(TypeScript / React) Back-end (C# / ASP.NET Core) SignalR WebSocket / SSE / Long Polling
• SignalR: リアルタイムの双方向 RPC library リアルタイム共同編集 41 リアルタイム通信は SignalR Front-end
(TypeScript / React) Back-end (C# / ASP.NET Core) SignalR Tapper TypedSignalR.Client.TypeScript WebSocket / SSE / Long Polling
• SignalR: リアルタイムの双方向 RPC library リアルタイム共同編集 42 リアルタイム通信は SignalR Front-end
(TypeScript / React) Back-end (C# / ASP.NET Core) SignalR Tapper TypedSignalR.Client.TypeScript TypeScript 側は こんな感じで記述 (C# からコードが生成される) WebSocket / SSE / Long Polling
• SignalR: リアルタイムの双方向 RPC library リアルタイム共同編集 43 リアルタイム通信は SignalR Front-end
(TypeScript / React) Back-end (C# / ASP.NET Core) SignalR Tapper TypedSignalR.Client.TypeScript TypedSignalR.Client TypedSignalR.Client.DevTools AspNetCore.SignalR.OpenTelemetry WebSocket / SSE / Long Polling TypeScript 側は こんな感じで記述 (C# からコードが生成される)
• キャンバス毎にコネクションをグルーピング リアルタイム共同編集 44 リアルタイム通信は SignalR Client SignalR Hub (back-end)
Client Client 同一のキャンバスに複数人 参加している場合
• キャンバス毎にコネクションをグルーピング リアルタイム共同編集 45 リアルタイム通信は SignalR Client SignalR Hub (back-end)
Client Client 同一のキャンバスに複数人 参加している場合
• キャンバス毎にコネクションをグルーピング リアルタイム共同編集 46 リアルタイム通信は SignalR Client SignalR Hub (back-end)
Client Client broadcast 同一のキャンバスに複数人 参加している場合
• キャンバス毎にコネクションをグルーピング リアルタイム共同編集 47 リアルタイム通信は SignalR Client SignalR Hub (back-end)
Client Client broadcast 同一のキャンバスに複数人 参加している場合
リアルタイム共同編集 48 • Command パターンは「命令」を「オブジェクト」にするパターン • Undo/Redo は操作履歴を stack に積む必要があるのでこのパターンが必須
Undo/Redo に必要なのは Command パターン
リアルタイム共同編集 49 • Command パターンは「命令」を「オブジェクト」にするパターン • Undo/Redo は操作履歴を stack に積む必要があるのでこのパターンが必須
Undo/Redo に必要なのは Command パターン
リアルタイム共同編集 50 • Command パターンは「命令」を「オブジェクト」にするパターン • Undo/Redo は操作履歴を stack に積む必要があるのでこのパターンが必須
Undo/Redo に必要なのは Command パターン 典型的な Command パターンは React に不適切
リアルタイム共同編集 51 • Command パターンは「命令」を「オブジェクト」にするパターン • Undo/Redo は操作履歴を stack に積む必要があるのでこのパターンが必須
Undo/Redo に必要なのは Command パターン 典型的な Command パターンは React に不適切 この interface を実装した class に 変更対象のオブジェクトの 参照を握らせ、 execute() 実行時に副作用を起こし 状態/描画に影響を及ぼす
リアルタイム共同編集 52 • Command パターンは「命令」を「オブジェクト」にするパターン • Undo/Redo は操作履歴を stack に積む必要があるのでこのパターンが必須
Undo/Redo に必要なのは Command パターン 典型的な Command パターンは React に不適切 この interface を実装した class に 変更対象のオブジェクトの 参照を握らせ、 execute() 実行時に副作用を起こし 状態/描画に影響を及ぼす React の思想/実装 双方の面で噛み合わない
リアルタイム共同編集 53 どうすれば React に うまく落とし込めるか?
リアルタイム共同編集 54 どうすれば React に うまく落とし込めるか? 前提: キャンバスの状態管理に Redux を用います
リアルタイム共同編集 55 React に適した パターンを考える
リアルタイム共同編集 56 React に適した パターンを考える 基本は command パターン。 これを分解し React
に適合させつつ 自分たちのアプリに 適切な設計に落とし込む
リアルタイム共同編集 57 React に適した パターンを考える Message (データ) Service (処理) 基本は
command パターン。 これを分解し React に適合させつつ 自分たちのアプリに 適切な設計に落とし込む
リアルタイム共同編集 58 React に適した パターンを考える Message (データ) Service (処理) 基本は
command パターン。 これを分解し React に適合させつつ 自分たちのアプリに 適切な設計に落とし込む React は関数型を指向している 親和性 UP!
リアルタイム共同編集 59 React に適した パターンを考える Message (データ) Service (処理) 各
command 毎に存在する 既存の状態を受け取り 新しい状態を返す各副作用の無い service ① command と stack の管理 ② 適切な CommandExecutor の呼び出し 基本は command パターン。 これを分解し React に適合させつつ 自分たちのアプリに 適切な設計に落とし込む CommandExecutor CommandRunner React は関数型を指向している 親和性 UP!
リアルタイム共同編集 60 型としては概ねこんな感じ
リアルタイム共同編集 61 型としては概ねこんな感じ Commnad を stack するしない
リアルタイム共同編集 62 型としては概ねこんな感じ Commnad を stack するしない 操作手段が 追加されるたび メンバが追加される
リアルタイム共同編集 63 先ほどの型の使い方 1つの OperationType に 1つの Command 1つの Command
に 1つの CommandExecutor
リアルタイム共同編集 64 Command を dispatch する際のコード 例えばカード配置時の Callback はこんな感じ Reducer
のコード
リアルタイム共同編集 65 Command を dispatch する際のコード 例えばカード配置時の Callback はこんな感じ Reducer
は同期メソッドなので 非同期処理を済ませてから dispatch する Reducer のコード
リアルタイム共同編集 66 Command を dispatch する際のコード 例えばカード配置時の Callback はこんな感じ Reducer
は同期メソッドなので 非同期処理を済ませてから dispatch する Reducer のコード CommandRunner.run() の中では ① Stack の操作 ② 対象の command に適した CommandExecutor の実行
リアルタイム共同編集 67 Undo/Redo するコード
リアルタイム共同編集 68 Undo/Redo するコード CommandRunner.undoAsync() の中では ① Stack の操作 (同期)
② 対象の command に適した SignalR の RPC 呼び出し(非同期) ③ 対象の command に適した CommandExecutor の実行 (同期)
リアルタイム共同編集 69 Undo/Redo するコード Undo/Redo は非同期が生じる。 Redux の世界で非同期を実行するには Redux Thunk
や redux-saga を導入する必要性ある。 それは実現したい事に対してヘビー。 CommandRunner.undoAsync() の中では ① Stack の操作 (同期) ② 対象の command に適した SignalR の RPC 呼び出し(非同期) ③ 対象の command に適した CommandExecutor の実行 (同期)
リアルタイム共同編集 70 Undo/Redo するコード Dispatch する前に非同期処理を 実行しておく CommandRunner.undoAsync() の中では ①
Stack の操作 (同期) ② 対象の command に適した SignalR の RPC 呼び出し(非同期) ③ 対象の command に適した CommandExecutor の実行 (同期) Undo/Redo は非同期が生じる。 Redux の世界で非同期を実行するには Redux Thunk や redux-saga を導入する必要性ある。 それは実現したい事に対してヘビー。
リアルタイム共同編集 71 実はリアルタイム共同編集に対応する場合も違いはこれだけ…! 他ユーザの操作 自分の操作
リアルタイム共同編集 72 実はリアルタイム共同編集に対応する場合も違いはこれだけ…! 他ユーザの操作 CommandType を変えればいいだけ 自分の操作
リアルタイム共同編集 73 実はリアルタイム共同編集に対応する場合も違いはこれだけ…! 他ユーザの操作 CommandType を変えればいいだけ 自分の操作 自分だろうと他人だろうと 同一の操作に対する処理は 同一の
CommandExecutor で処理する (共通化)
リアルタイム共同編集 74 機能追加は簡単でなければいけない ① OperationType (enum)にメンバを追加し ② 追加した OperationType に対応する
Command を作成し ③ 追加した Command に対応する CommandExecutor を実装し ④ CommandExecutor を配列に追加する
リアルタイム共同編集 75 機能追加は簡単でなければいけない ① OperationType (enum)にメンバを追加し ② 追加した OperationType に対応する
Command を作成し ③ 追加した Command に対応する CommandExecutor を実装し ④ CommandExecutor を配列に追加する Stack の操作等は 開発者に意識させない
リアルタイム共同編集 76 機能追加は簡単でなければいけない ① OperationType (enum)にメンバを追加し ② 追加した OperationType に対応する
Command を作成し ③ 追加した Command に対応する CommandExecutor を実装し ④ CommandExecutor を配列に追加する OperationType で switch とかは書かない Stack の操作等は 開発者に意識させない
Node-AI は時系列データの分析に特化したノーコードツール • β版公開中!是非使ってね! https://nodeai.io/ キャンバスの画的な表現 • SVG を活用 •
カメラという抽象 リアルタイム共同編集 • リアルタイム通信は SignalR • リアルタイム共同編集 + Undo/Redo のための command パターンベースの設計 まとめ 77 提示したコードや設計は あくまで「エッセンス」です