Slide 1

Slide 1 text

控えめな App Router と持続可能な開発 2024.01.17 @ahomu PWA Night vol.59 〜フロントエンド設計の振り返り〜

Slide 2

Slide 2 text

@ahomu - Ayumu Sato 株式会社 over fl ow 副業・転職サービス O ff ers 開発、VPoE 前の職場: 緑のメガベンでテックリード、マネージャー、⼈事など 前々職場: Web システムの受託会社で零細フルスタック開発

Slide 3

Slide 3 text

みなさん App Router 楽しんでますか

Slide 4

Slide 4 text

使うパーツを選べば控えめにも使えそう、という話 メンタルモデルやプラクティスの更新は時間かかりますからね

Slide 5

Slide 5 text

Agenda 話の流れ、スライドは⽂字多めですmm 1.開発コンテキスト 2.設計と選定 3.控えめな App Rouer 4.持続可能な開発

Slide 6

Slide 6 text

։ൃίϯςΩετ

Slide 7

Slide 7 text

Context ここ数年のスタートアップでよく⾒るやつ • 中⻑期の QCD とユーザー体験の改善を狙ったリプレースプロジェクト • Ruby on Rails モノリスからフロントエンド部分を剥がして置き換える • ログインユーザーと⾮ログインユーザーが混在して利⽤する画⾯ • 今後はログインユーザー向けアプリケーション等の置き換え予定もある • Ruby on Rails が提供する GraphQL とその裏側にある既存資産の流⽤が前提

Slide 8

Slide 8 text

Team とことん Rails Way で⾏くことも検討したのですが諸事情が...(・x・) • 既存の技術スタックは Ruby on Rails + Vue.js + jQuery • Ruby と RoR 上等、JavaScript と Vue 程々といったメンバーが多め • 別プロダクトでは先⾏して現代の技術スタックを⽀持 • O ff ers MGR (SaaS) : Next.js Pages Router を利⽤した SPA • オウンドメディア群 : monorepo 管理で Astro ベース量産(共通化 & コピペ) • Next.js に触れること⾃体は意気軒昂 • Vue.js の責ではないが既存コードの腐敗が厳しく前⽅に脱出したい空気

Slide 9

Slide 9 text

ઃܭͱબఆ

Slide 10

Slide 10 text

Overview 局所影響のあるログイン判定、ユーザー依存情報の取得はクライアントサイドで⾮同期にやる User ① HTML リクエスト ② ユーザー⾮依存情報 (gql) ③ HTML レスポンス ④ ユーザー依存情報 (gql) CloudFront

Slide 11

Slide 11 text

Migration plan 1ページずつ順番にゆっくりやっています ECS Lamba CloudFront ELB RDS ※1 実際は1度インターネットに出ています (略図) ※2 ELB の表現⼒を超えているので nginx 先⽣にお願いしています ※1 User ※2 特定のパスへのリクエスのみ nginx で Lambda 上の Next.js 宛てにリバースプロキシ リプレースの適⽤範囲を段階的に拡げていく

Slide 12

Slide 12 text

Web framework 最近はどれも良くできてますなぁ • Next.js、Qwik City、Remix、SvelteKit、Vite ベースの⼿作りを⽐較検討 • うーん・・・ • 社内エンジニアへの要求知識の増分 (特に React 以外) • フレームワークの基本構成 (スターター類) の⾒通しの良さ、簡潔さ • ビルド後の成果物ファイルサイズの多寡 • クライアントサイドバンドルのファイルサイズを絞り込む機構の有無

Slide 13

Slide 13 text

うん、⻑いもの (Next.js) に巻かれよう! 今回の要件はともかく他のアプリもあるので投資 投資

Slide 14

Slide 14 text

Repository 無難にやってます • サービス内の複数アプリを管理する monorepo 構造 • Turborepo のスターターを踏襲 • apps/* • アプリ置き場、今後増えるが⼀応 Next.js に限らない • packages/* • Design System 実装ほか共通ライブラリ、設定など

Slide 15

Slide 15 text

• src • __generated__ • app • components • features • providers • utils • acme-feature • components • hooks • providers • utils • AcmeComponent • AcmeComponent.module.css • AcmeComponent.stories.tsx • AcmeComponent.test.tsx • AcmeComponent.tsx • index.tsx app/**/src features/** components/* アプリ内共通層 関⼼別の分類層 個々のコンポーネント App Router graphql-codegen components gql Fragment hooks App Router と⼼中し切っていない構造... 200⾏超えたら⼦コンポーネントに分割していく感覚

Slide 16

Slide 16 text

詳しくは zenn 記事のほうを参照してください :) • Next.js に限らないレイヤリング • "上から順に辿って分かる"が⼤切 • App Router とズブズブなら... • app 以下にページ固有要素を集約 • 共通パーツは _shared に分類・整理 Directories in app

Slide 17

Slide 17 text

Others その他いろいろ • Styling • Mocking • Testing • UI Inventory • GraphQL • State + Fetch CSS Modules Resolver 先⾏でやりくりにつき省略、テストのみ msw 使⽤ vitest + testing-library + msw + happy-dom シンプルに Storybook、アドオンは最⼩限 GraphQL Code Generator + client-preset TanStack Query + graphql-request + React.cache

Slide 18

Slide 18 text

߇͑Ίͳ "QQ3PVUFS

Slide 19

Slide 19 text

“おれのかんがえたさいきょう” は絶対詰む ∕(^o^)\

Slide 20

Slide 20 text

とりあえず普通の Web アプリ作りたいんだよなぁ

Slide 21

Slide 21 text

Normally 普通というかトラディショナルというか… • サーバーサイド (オリジン) が HTML を返す • CDN (エッジ) が HTML をキャッシュする • ブラウザが HTML を受け取る • クライアントサイドの JavaScript バンドルが UI を動かす

Slide 22

Slide 22 text

hmm... オリジンがもりもり仕事するログイン前提の SaaS だと App Router + RSC 全⼒設計たのしそう • いくつかの機能は要求するメンタルモデルの変化が⼤きい (特に古い RoR ⽐) • Caching features • next/link の推奨 → SPA の半強制 (これは Pages Router もだったか?) • RSC + Suspense • RSC Payload • Server Actions 等の境界 etc...

Slide 23

Slide 23 text

よっしゃ、ほどほどに使おう 必要になるまで App Router の全⼒は持ち越し、Keep It Simple, Stupid

Slide 24

Slide 24 text

詳しくは zenn 記事のほうを参照してください :) • 各種の内部 Cache 無効化 • soft navigation を禁⽌ • page.tsx ≒ getServerSideProps • RSC + Suspense は不使⽤ • middleware で Cache-Control Details

Slide 25

Slide 25 text

App Router らしくないが従来知識でも⾒れば分かる msw や storybook との互換性問題やテスト周りの障害も回避!

Slide 26

Slide 26 text

We ♥ Next.js Remix に浮気したいとか⾔わない...よ? • App Router の全⼒でなくてもフレームワークとしての基盤は有⽤ • レイアウト、ルーティング、最適化、ビルド周り⼀式、公式プラグイン etc • チームでは Next.js と React と TypeScript の基本から固めたい • 便利なサブセットは必要になったら都度チームとして取り⼊れる

Slide 27

Slide 27 text

࣋ଓՄೳͳ։ൃ

Slide 28

Slide 28 text

⚠ ここから思想が偏ります

Slide 29

Slide 29 text

https://w3c.github.io/sustyweb/

Slide 30

Slide 30 text

3.17 Manage Dependencies Appropriately "only using libraries where necessary" 本当に必要なものだけを管理しましょう、って • Environmental • 開発者のマシンは、必要のないパッケージのインストールやレンダリングにエネルギーを浪費す る必要がない。 • Security • サードパーティのコードには、バグやセキュリティ上の問題が含まれている可能性があります。 パッケージを常に最新の状態に保ち、サードパーティ・ライブラリの使⽤数を減らすことで、セ キュリティ上の⽋陥が発⽣する可能性を減らすことができます。 • Performance • クライアントサイドのJavaScriptを減らすと、通常ウェブサイトが速くなります。

Slide 31

Slide 31 text

依存の肥⼤化は開発の持続可能性を損ねる

Slide 32

Slide 32 text

Bloated dependence Next.js などの⼤物が引き連れてくる依存ツリーがそもそも多い/⼤きいという話はある • 依存パッケージのアップデート • アップデートするためのアップデート • アップデートするためのアップデートのためのアップデート • バージョン整合性パズルがしばしば開催される • クライアントサイドバンドルが⼤きくなる (devDependencies は限りでない) • サプライチェーン毒まんじゅうの当たり判定が増える

Slide 33

Slide 33 text

プリミティブなパーツをシンプルに使うことを優先 過度に禁欲的にはならずとも常に⾃問していくスタイル

Slide 34

Slide 34 text

Prefer primitives 異論は認める、素朴に書くほうが GPT 等の AI によるコーディング⽀援を受けやすいとかはある • classnames パッケージを⼊れずに [].join で済ます • tailwind や CSS in JS もいらない、Next.js の CSS Modules で事⾜りる • IntersectionObserver や matchMedia のラッパーも不要 • Storybook はあくまで UI インベントリ、余計なことしない • アクセシブルな UI 各種 → react-aria 最⾼!! • 凝った Carousel UI、凝った Combobox 各種 → ぐぬぬ...

Slide 35

Slide 35 text

package.json 必要最⼩限の依存を努めている (storybook や uni fi ed 関連は外出しされています :P ) "dependencies": { "@bugsnag/js": "7.22.2", "@next/bundle-analyzer": "14.0.2", "@next/third-parties": "14.0.2", “@my-prj/md2html": "workspace:*", “@my-prj/ui": "workspace:*", "@opentelemetry/api": "1.7.0", "@opentelemetry/sdk-trace-base": "1.19.0", "@tanstack/react-query": "5.0.0", "@tanstack/react-query-devtools": "5.0.1", "encoding": "0.1.13", "graphql-request": "6.1.0", "keen-tracking": "5.0.1", "next": "14.0.2", "react": "18.2.0", "react-dom": "18.2.0", "valibot": "0.20.1" }, "devDependencies": { "@graphql-codegen/cli": "5.0.0", "@graphql-codegen/client-preset": "4.1.0", "@my-prj/eslint-config-offers": "workspace:*", “@my-prj/prettier”: "workspace:*", "@my-prj/stylelint-config-offers": "workspace:*", "@my-prj/test-utils": "workspace:*", "@my-prj/tsconfig": "workspace:*", "@vitejs/plugin-react": "4.2.0", "eslint": "8.47.0", "happy-dom": "12.10.3", "postcss-nesting": "12.0.1", "prettier": "3.0.3", "schema-dts": "1.1.2", "stylelint": "15.11.0", "typescript": "5.2.2", "vitest": "1.0.1" }, @types/ … は割愛しています

Slide 36

Slide 36 text

In my opinion... 偏った思想の中⼼です • Next.js のクライアントサイドバンドルだけでも既に⼤きい • 「あったら便利」 程度では⻭⽌めがきかなくなる • 糖⾐構⽂程度の開発者体験のためにユーザー体験を毀損してはならない • 依存関係を増やせば増やすほど苦労するのは未来の誰か • 主要ライブラリはそれぞれの理想を求めてダイナミックに進化する • 各依存先の理想と理想の折り合いに疲弊するのがフロントエンドの近代史

Slide 37

Slide 37 text

⻄表島にいたヤギです • App Router は控えめにも使える • 使い切らない導⼊戦略も • 依存は⼤事だよ〜 • よ〜く考えよ〜 • 本当に必要か常に⾃問したい まとめ

Slide 38

Slide 38 text

ご清聴ありがとうございました :)