Slide 1

Slide 1 text

2024年2月 エンジニア イベント資料 カーナベル株式会社

Slide 2

Slide 2 text

本日は遠方よりご参加ありがとうございます! 本日のアジェンダ ● 前半の部 ○ イントロ ○ カーナベルを支える技術について (〜11:15) ○ Web Components(Lit)を利用したモダンなUI開発体験 (~12:30) ● 休憩 (~13:30) ● 後半の部 ○ Deck Makerのブログ埋め込み機能を作ろう (~15:30)

Slide 3

Slide 3 text

自己紹介 ● 安藤真 (@andoshin11) ● 役職:テックリード ● 入社:2021年4月 ○ ex: LINE, メルカリ, Folio, 朝日新聞社, etc… ● 趣味:遊戯王(マスターデュエル), FPS, グルメ, ウィスキー ● 好きな技術:TS, Vue.js, Nuxt.js, k8s, Terraform, GO

Slide 4

Slide 4 text

01 02 モダンな技術を利用したプロダクト開発の楽しさを知ってもらう 本日のゴール 「トレカ × Tech」に挑戦する弊社について知ってもらう - 世間的にみても結構進んだ取り組みをしていると思う - あわよくば友人やネットでみなさんに宣伝して欲しい - Web Components(Lit) + TypeScript + Storybook - みなさんが実務でも利用できる部分があると嬉しいです

Slide 5

Slide 5 text

カーナベルを 支える技術

Slide 6

Slide 6 text

Deck Maker

Slide 7

Slide 7 text

Deck Maker コアとなる技術 1. 快適に利用できるSingle Page Application ○ 直感的にデッキ編集が行える UI設計 ○ 高速に仮説検証が行える改善基盤 2. 業界一のカード検索・デッキ検索機能 (自社調べ) ○ 数百万デッキに対して 2~300msで複合検索可能な検索バックエンド ○ ECサイトならではの価格データ連携

Slide 8

Slide 8 text

Deck Maker コアとなる技術 1. 快適に利用できるSingle Page Application ○ 直感的にデッキ編集が行える UI設計 ○ 高速に仮説検証が行える改善基盤 2. 業界一のカード検索・デッキ検索機能 (自社調べ) ○ 数百万デッキに対して 2~300msで複合検索可能な検索バックエンド ○ ECサイトならではの価格データ連携

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

What’s good? ● Vue.js向けのフルスタックフレームワーク ○ Dynamic Routing, Server API, Build Optimization, Middleware, etc… ○ 充実したModule ecosystem ■ 弊社ではSentry, Google Analytics等のModuleを利用 ○ Webアプリケーション開発に必要なことを「大体なんでも」やってくれる ○ Pluginをうまく活用すればDIもサクッとできてテスタビリティが高い

Slide 11

Slide 11 text

Deck Maker コアとなる技術 1. 快適に利用できるSingle Page Application ○ 直感的にデッキ編集が行える UI設計 ○ 高速に仮説検証が行える改善基盤 2. 業界一のカード検索・デッキ検索機能 (自社調べ) ○ 数百万デッキに対して 2~300msで複合検索可能な検索バックエンド ○ ECサイトならではの価格データ連携

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

What’s good? ● ドキュメント格納型の検索エンジン。とにかく速い & 高機能 ● RESTFul APIによりクライアント(Browser)から複雑なクエリを組み立てて検索を実行 できるためデッキメーカーと相性が良い ● 実際のクエリ利用例 ○ レベル7魔法使い族効果モンスターで破壊以外の除去効果を持つカード ○ フェアリー・ライフとフェアリー・ Re:ライフを含む水・自然文明のみかつ 5,000円以 下のデッキ ○ 等々

Slide 14

Slide 14 text

Architecture

Slide 15

Slide 15 text

Deck Maker (Nuxt.js) Firestore Firebase Functions カーナベルECサイト Elasticsearch 1. カード・デッキ検索 2. デッキを保存 3. 関数をトリガー 4. 価格データを取得 5. 正規化したデッキ データをWrite ※ 実際のアーキテクチャとは異なります

Slide 16

Slide 16 text

ECサイトリニューアル(WIP)

Slide 17

Slide 17 text

ECサイトリニューアル(WIP) ● 現行EC老朽化と事業拡大に対応するべく式年遷宮中 ● Microservices分割(とはいえ細かく分けすぎないように ) ○ 販売サイト ○ 買取サイト ○ トレカデータサービス ○ お客様情報管理サービス ○ 社員管理サービス ○ 認証基盤 ○ etc… ● 「難しいことを難しくやらない」という姿勢で適切に先端技術を取り込んでいく

Slide 18

Slide 18 text

Browser Frontend (Next.js) Contour (Envoy) Auth Gateway Argo CD・ Workflows Cilium OTel Collector Microservice A Microservice B Microservice C

Slide 19

Slide 19 text

Kubernetes ● Kubernetes自体は4年ほど前から利用中。 GKE(画像AI学習基盤) & EKS(新EC) ● Middlewareをサクッと導入しやすいのが便利 ○ Helm chart repositoryを用意してMiddleware versionsをgit管理 ● 何かあった時にソースコードレベルで原因調査を行える安心感 ≠ ブラックボックス ● Terraform Moduleを適切に設計すれば multi-cluster構成も導入しやすい ○ Middlewareの破壊的変更などを検証する際は clusterごと壊したり作り直したくなる ことが結構ある ○ multi-clusterであればcluster-levelの変更をユーザー影響なく検証できる ○ 現在はmain-cluster, experimental-clusterのdual構成で常時稼働中。一方に変 更を加えて動作確認が完了した段階で ALBの向き先を切り替える運用 ○ 徹底的にstatelessなclusterになるように意識している

Slide 20

Slide 20 text

Knative ● Cloud Runのようなzero-scalingやeventingをEKSでも行いたくて導入 ● EKSはNodeの稼働時間に応じて課金されるため、業務時間外の Staging Cluster費用が 勿体無い → Knativeならリクエストを送るまで Pod数を0にできて経済的 ● まだまだ導入企業は少ないものの、小さい開発組織とは相性が良さそう ● 余談: カーナベルは日本の会社として唯一 Knative Adoptersにリスティングされてる

Slide 21

Slide 21 text

Cilium ● eBPFで動作するネットワーク制御ツール ● Observability ○ カーネルレベルでCluster内の通信を追えるため、ネットワーク流量や経路の監視が 行いやすい ○ HubbleというIstioでいうKiali的なService Map可視化ツールもある ● Security ○ Namespace単位でNetwork Policyを定義でき、Cluster外へのアクセスを制御で きる ○ アクセス可能な外部サービス (決済プロバイダー, Firebase, Aurora, etc…)をホワ イトリストで定義し、万が一サーバーに侵入されても攻撃者のサーバーへデータを持 ち逃げされないよう制御

Slide 22

Slide 22 text

Q&A

Slide 23

Slide 23 text

Hands-on Web Components(Lit)を利用したモダンなUI開発体験

Slide 24

Slide 24 text

もう少しだけ座学にお付き合いください

Slide 25

Slide 25 text

そもそもWeb Componentsって何? ● 再利用可能なコンポーネントを独自の HTML Tags(Custom Elements)として定義するWebの標 準仕様 ● 2011年頃に初めて提唱され、Google主導のPolymer Projectなどを経て2016年頃から順次ブラ ウザ実装が進む。2020年以降は全てのモダンブラウザで利用可能 ● React / Vue / Angular / Svelteといったフレームワークに依存しない = ブラウザネイティブのラン タイムで動作。ポータビリティの高いコンポーネント 定義が可能に ● 実際は... ○ Web標準として汎用性重視の仕様となったため重厚な書き味に ... ○ LitやSvelte等で定義したコードをcompileして利用するケースが大半

Slide 26

Slide 26 text

Web Componentsを構成する要素 ● Custom Elements ○ 開発者が独自のHTML Tagsを定義し、挙動をカスタマイズできる ● Shadow DOM ○ Document APIやCSSの適用対象を特定のスコープ (Shadow DOM)に限定することが可 能になる。各コンポーネントの CSS定義が他の定義と干渉しない! ● HTML Template ○ というフラグメント要素(ページ読み込み時に描画されない DOM要素)の活用 ● ES Modules

Slide 27

Slide 27 text

Web Components in real-life

Slide 28

Slide 28 text

youtube.com

Slide 29

Slide 29 text

Material Web

Slide 30

Slide 30 text

Adobe Spectrum

Slide 31

Slide 31 text

How to use them?

Slide 32

Slide 32 text

CodeSandbox Link

Slide 33

Slide 33 text

Writing Web Components

Slide 34

Slide 34 text

CodeSandbox Link

Slide 35

Slide 35 text

CodeSandbox Link 1. HTMLElementを拡張してカ スタムクラスを定義

Slide 36

Slide 36 text

CodeSandbox Link 1. HTMLElementを拡張してカ スタムクラスを定義 2. shadowRootにHTMLを描 画。getAttributeで親要素から 渡された値を参照

Slide 37

Slide 37 text

CodeSandbox Link 1. HTMLElementを拡張してカ スタムクラスを定義 2. shadowRootにHTMLを描 画。getAttributeで親要素から 渡された値を参照 3. customElements.defineで カスタムタグを定義

Slide 38

Slide 38 text

CodeSandbox Link 1. HTMLElementを拡張してカ スタムクラスを定義 2. shadowRootにHTMLを描 画。getAttributeで親要素から 渡された値を参照 3. customElements.defineで カスタムタグを定義 4. 定義したカスタムタグを呼び 出し。Attributeで任意の値を指 定

Slide 39

Slide 39 text

Web Components Data-flow Parent Component Child Component Attributeを経由して値を伝達 CustomEventをdispatchEvent()で発火

Slide 40

Slide 40 text

Timer Componentを作ろう

Slide 41

Slide 41 text

Timer Componentを作ろう

Slide 42

Slide 42 text

Timer Componentを作ろう ① ● src/timer.ts を作成 ● index.htmlの

Slide 43

Slide 43 text

Timer Componentを作ろう ② ● HTMLElementを拡張して TimerComponentを定義 ● Shadow DOMやattribute監視の設定を 諸々追加 ● connectedCallbackに初期化処理を記 述 ● customElements.defineを呼んで というカスタムタ グを定義

Slide 44

Slide 44 text

Timer Componentを作ろう ②

Slide 45

Slide 45 text

Timer Componentを作ろう ③ ● timer private propertyを定義 ● startTimer() ○ setIntervalで1000ms = 1sご とにHTMLを書き換える ○ タイマーが終了したら timer-finishedイベントを発火 ● stopTimer() ○ タイマーを初期化する

Slide 46

Slide 46 text

Timer Componentを作ろう ④ ● Event Listenerの設定 ● connectedCallback() ○ 読み込み時の初期化処理 ● disconnectedCallback() ○ オフロード時のリセット処理 ○ メモリリークの防止 ● アロー関数におけるthisのスコープに注 意

Slide 47

Slide 47 text

Timer Componentを作ろう ⑤ ● 呼び出し元でtimer-finishedのイベン トリスナーを定義

Slide 48

Slide 48 text

Timer Componentを作ろう Check full code in Gist

Slide 49

Slide 49 text

正直、仕事で使うには辛い・・・ ● 現代のUIコンポーネント開発に必須な機能 ○ Reactに代表される宣言的テンプレート定義 ○ Vueに代表されるリアクティブなデータバインディング

Slide 50

Slide 50 text

正直、仕事で使うには辛い・・・ ● 現代のUIコンポーネント開発に必須な機能 ○ Reactに代表される宣言的テンプレート定義 ○ Vueに代表されるリアクティブなデータバインディング ● どちらも自前で実装する必要がある 🔥

Slide 51

Slide 51 text

正直、仕事で使うには辛い・・・ ● 現代のUIコンポーネント開発に必須な機能 ○ Reactに代表される宣言的テンプレート定義 ○ Vueに代表されるリアクティブなデータバインディング ● どちらも自前で実装する必要がある 🔥 ● 手続き的なDOM管理 ○ element.innerHTMLで一括更新を行うためパフォーマンスも悪い ● リアクティビティの担保とデータバインディング ○ class propertyにtemplateが依存した状態でpropertyが更新された場合、明示的に DOM の更新や参照部分の再計算を行う必要がある → 依存ツリーやbatch update queueの自 前管理が必要

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

Lit: Simple. Fast. Web Components. ● Googleの開発者主導のプロジェクト。 Polymer Projectの後継 ● Web Componentsをより宣言的かつ生産的に開発するためのフレームワーク ● コンパイル後はネイティブの Web Componentsに変換されるため再利用性が高い ● Decorator(ref: TC39)を利用してreactive propertyを定義し、更新時にはbatch処理により影響 部分のみをatomicに更新してくれる = ハイパフォーマンス ● その他の便利な機能 ○ html関数や css関数とtagged template literalを組み合わせて安全なtemplateを記述 可能 = sanitize & virtual-node-treeの構築 ○ Pre-definedなdirectiveを活用して記述量を削減できる ○ TypeScript support

Slide 54

Slide 54 text

Lit Sandbox Link

Slide 55

Slide 55 text

LitでTimer Componentを作ろう litをインストール

Slide 56

Slide 56 text

LitでTimer Componentを作ろう tsconfig.jsonにオプションを追加

Slide 57

Slide 57 text

LitでTimer Componentを作ろう ① ● src/lit-timer.ts を作成 ● index.htmlの

Slide 58

Slide 58 text

LitでTimer Componentを作ろう ② ● LitElementを拡張する ● @customElement decoratorでラップ ● 親から受け取るdurationに@property decoratorを付与 ● render() ○ reactive propertyの変更に応じ て自動で実行される描画処理 ○ html関数にtagged template literalを渡して宣言的に定義

Slide 59

Slide 59 text

LitでTimer Componentを作ろう ③ ● timer private propertyを定義 ● startTimer() ○ setIntervalでduration propertyを更新。HTMLの書き換え は行わない ○ タイマーが終了したら timer-finishedイベントを発火 ● stopTimer() ○ タイマーを初期化する

Slide 60

Slide 60 text

LitでTimer Componentを作ろう ④ ● Event Listenerの設定 ● @click ○ onClickの糖衣構文 ○ template内で直接 addEventListener相当の処理を定義 可能 = 宣言的定義 ○ removeEventListenerも不要

Slide 61

Slide 61 text

LitでTimer Componentを作ろう ⑤ ● 呼び出し元でtimer-finishedのイベン トリスナーを定義

Slide 62

Slide 62 text

Lit Timer Componentを作ろう Check full code in Gist

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

ここまでのまとめ

Slide 65

Slide 65 text

前半の部 まとめ ● Web Componentsを利用することで汎用的なカスタムタグを利用することができる ● Litを利用することでより宣言的かつ生産的なコンポーネント開発が行える ○ reactive property ○ declarative render function ○ etc… ● 後半の部ではLitを利用してDeck Makerのデッキ埋め込み機能を作っていきます

Slide 66

Slide 66 text

休憩時間 (~13:30)

Slide 67

Slide 67 text

後半の部: デッキ埋め込み機能を作ってみよう

Slide 68

Slide 68 text

完成イメージ Storybook Link

Slide 69

Slide 69 text

作業の流れ GitHub Repository

Slide 70

Slide 70 text

お題①: DMDeckInfo ● デッキの基本情報を表示するコンポーネントを作る (完成系) ● デザインは雰囲気で。paddingや細かいfont-sizeは特に指定しません ● property: ○ deckData ← APIから取得するデッキデータ。今回は mock値で代用 ● 表示する情報: ○ サムネイル ○ レギュレーション ○ 更新日 ○ デッキ名 ○ ユーザー名 ○ 閲覧数 レギュレーションの表示ルール ● none → 殿堂ゼロ ● advance → アドバンス ● 2block → 2ブロック ● party → デュエパーティー ● Original → オリジナル

Slide 71

Slide 71 text

お題②: DMTabs ● デッキの表示領域を切り替えるコンポーネントを作る (完成系) ● property: ○ deckData ← APIから取得するデッキデータ。今回は mock値で代用 ○ currentTab ← 現在表示中のタブ ● dispatchEvent: ○ change ← タブが選択されたら発火 ■ new CustomEvent(‘change’, { detail: targetTab }) ● その他仕様: ○ currentTabはactive classを付与 ○ メイン/GR/超次元はラベルの横に枚数を表示する ○ deckData.dorumagedonがtrueの時のみドルマゲドンのタブを表示する ○ deckData.zeronがtrueの時のみ零龍のタブを表示する

Slide 72

Slide 72 text

お題③: DMDeck ● 実際にサイトから呼び出されるデッキ取得 & 表示コンポーネント(完成系) ● property: ○ dmDeckId ← デュエマデッキID ● state: ○ currentTab ← 現在表示中のタブ ○ 参考: Lit - Internal reactive state ● methods ○ changeTab ← DMTabsコンポーネントのchange eventをトリガーに実行。受け取った値 でstateを更新する ● おまけ ○ Storybook上でdmDeckIdを変更た際にデッキの再取得 & 再描画を行うには? ○ 参考: Lit - Reactive update cycle

Slide 73

Slide 73 text

自サイトへの埋め込み方法

Slide 74

Slide 74 text

自サイトへの埋め込み方法

Slide 75

Slide 75 text

自サイトへの埋め込み方法 ● vite buildが実行されるとdist/assets/index-.jsが生成される ● 上記のindex.jsをサイトに読みこんで を呼ぶだけ ● --assetsInlineLimitで指定のbyte数以下の画像ファイルはインライン化できる

Slide 76

Slide 76 text

外部サイトへの埋め込み方法

Slide 77

Slide 77 text

外部サイトへの埋め込み方法 ● 外部のサービスプロバイダ (note, アメーバブログ, はてなブログ, etc…)がの仕様 を把握するのは流石にムリ ● デッキメーカーのnote埋め込み機能はどのように実装されているのか?

Slide 78

Slide 78 text

oEmbed ● oembed.comで定義されている汎用的なコンテンツ埋め込み仕様 ● 2008年ごろに提案され、Wordpressをはじめとする複数企業によってメンテナンス中 ● 埋め込み可能なコンテンツ : ○ photo ○ video ○ link ○ rich ← htmlの埋め込みが可能。Deck Maker(ガチまとめ)はこれを利用している

Slide 79

Slide 79 text

oEmbedのフロー 1. 埋め込みURLへアクセス

Slide 80

Slide 80 text

oEmbedのフロー

Slide 81

Slide 81 text

oEmbedのフロー 1. 埋め込みURLへアクセス 2. embed endpointをheadで伝達

Slide 82

Slide 82 text

oEmbedのフロー

Slide 83

Slide 83 text

oEmbedのフロー 1. 埋め込みURLへアクセス 2. embed endpointをheadで伝達 3. embed endpointへアクセス 4. Rich TypeのJSONを返却

Slide 84

Slide 84 text

oEmbedのフロー embed.json

Slide 85

Slide 85 text

oEmbedのフロー 1. 埋め込みURLへアクセス 2. embed endpointをheadで伝達 3. embed endpointへアクセス 4. Rich TypeのJSONを返却 5. JSONに指定されたiframeを描画 6. iframe srcで指定されたHTMLを返却

Slide 86

Slide 86 text

oEmbedのフロー iframe src link

Slide 87

Slide 87 text

本日はここまで

Slide 88

Slide 88 text

長時間お付き合いいただき ありがとうございました!