Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

控えめな App Router と持続可能な開発 - PWA Night vol.59

Ayumu Sato
January 17, 2024

控えめな App Router と持続可能な開発 - PWA Night vol.59

Ayumu Sato

January 17, 2024
Tweet

More Decks by Ayumu Sato

Other Decks in Programming

Transcript

  1. @ahomu - Ayumu Sato 株式会社 over fl ow 副業・転職サービス O

    ff ers 開発、VPoE 前の職場: 緑のメガベンでテックリード、マネージャー、⼈事など 前々職場: Web システムの受託会社で零細フルスタック開発
  2. Context ここ数年のスタートアップでよく⾒るやつ • 中⻑期の QCD とユーザー体験の改善を狙ったリプレースプロジェクト • Ruby on Rails

    モノリスからフロントエンド部分を剥がして置き換える • ログインユーザーと⾮ログインユーザーが混在して利⽤する画⾯ • 今後はログインユーザー向けアプリケーション等の置き換え予定もある • Ruby on Rails が提供する GraphQL とその裏側にある既存資産の流⽤が前提
  3. 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 の責ではないが既存コードの腐敗が厳しく前⽅に脱出したい空気
  4. Migration plan 1ページずつ順番にゆっくりやっています ECS Lamba CloudFront ELB RDS ※1 実際は1度インターネットに出ています (略図)

    ※2 ELB の表現⼒を超えているので nginx 先⽣にお願いしています ※1 User ※2 特定のパスへのリクエスのみ nginx で Lambda 上の Next.js 宛てにリバースプロキシ リプレースの適⽤範囲を段階的に拡げていく
  5. Web framework 最近はどれも良くできてますなぁ • Next.js、Qwik City、Remix、SvelteKit、Vite ベースの⼿作りを⽐較検討 • うーん・・・ •

    社内エンジニアへの要求知識の増分 (特に React 以外) • フレームワークの基本構成 (スターター類) の⾒通しの良さ、簡潔さ • ビルド後の成果物ファイルサイズの多寡 • クライアントサイドバンドルのファイルサイズを絞り込む機構の有無
  6. Repository 無難にやってます • サービス内の複数アプリを管理する monorepo 構造 • Turborepo のスターターを踏襲 •

    apps/* • アプリ置き場、今後増えるが⼀応 Next.js に限らない • packages/* • Design System 実装ほか共通ライブラリ、設定など
  7. • 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 <ListItem> hooks App Router と⼼中し切っていない構造... 200⾏超えたら⼦コンポーネントに分割していく感覚
  8. 詳しくは zenn 記事のほうを参照してください :) • Next.js に限らないレイヤリング • "上から順に辿って分かる"が⼤切 •

    App Router とズブズブなら... • app 以下にページ固有要素を集約 • 共通パーツは _shared に分類・整理 Directories in app
  9. 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
  10. Normally 普通というかトラディショナルというか… • サーバーサイド (オリジン) が HTML を返す • CDN

    (エッジ) が HTML をキャッシュする • ブラウザが HTML を受け取る • クライアントサイドの JavaScript バンドルが UI を動かす
  11. hmm... オリジンがもりもり仕事するログイン前提の SaaS だと App Router + RSC 全⼒設計たのしそう •

    いくつかの機能は要求するメンタルモデルの変化が⼤きい (特に古い RoR ⽐) • Caching features • next/link の推奨 → SPA の半強制 (これは Pages Router もだったか?) • RSC + Suspense • RSC Payload • Server Actions 等の境界 etc...
  12. 詳しくは zenn 記事のほうを参照してください :) • 各種の内部 Cache 無効化 • <Link>

    soft navigation を禁⽌ • page.tsx ≒ getServerSideProps • RSC + Suspense は不使⽤ • middleware で Cache-Control Details
  13. We ♥ Next.js Remix に浮気したいとか⾔わない...よ? • App Router の全⼒でなくてもフレームワークとしての基盤は有⽤ •

    レイアウト、ルーティング、最適化、ビルド周り⼀式、公式プラグイン etc • チームでは Next.js と React と TypeScript の基本から固めたい • 便利なサブセットは必要になったら都度チームとして取り⼊れる
  14. 3.17 Manage Dependencies Appropriately "only using libraries where necessary" 本当に必要なものだけを管理しましょう、って

    • Environmental • 開発者のマシンは、必要のないパッケージのインストールやレンダリングにエネルギーを浪費す る必要がない。 • Security • サードパーティのコードには、バグやセキュリティ上の問題が含まれている可能性があります。 パッケージを常に最新の状態に保ち、サードパーティ・ライブラリの使⽤数を減らすことで、セ キュリティ上の⽋陥が発⽣する可能性を減らすことができます。 • Performance • クライアントサイドのJavaScriptを減らすと、通常ウェブサイトが速くなります。
  15. Bloated dependence Next.js などの⼤物が引き連れてくる依存ツリーがそもそも多い/⼤きいという話はある • 依存パッケージのアップデート • アップデートするためのアップデート • アップデートするためのアップデートのためのアップデート

    • バージョン整合性パズルがしばしば開催される • クライアントサイドバンドルが⼤きくなる (devDependencies は限りでない) • サプライチェーン毒まんじゅうの当たり判定が増える
  16. Prefer primitives 異論は認める、素朴に書くほうが GPT 等の AI によるコーディング⽀援を受けやすいとかはある • classnames パッケージを⼊れずに

    [].join で済ます • tailwind や CSS in JS もいらない、Next.js の CSS Modules で事⾜りる • IntersectionObserver や matchMedia のラッパーも不要 • Storybook はあくまで UI インベントリ、余計なことしない • アクセシブルな UI 各種 → react-aria 最⾼!! • 凝った Carousel UI、凝った Combobox 各種 → ぐぬぬ...
  17. 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/ … は割愛しています
  18. In my opinion... 偏った思想の中⼼です • Next.js のクライアントサイドバンドルだけでも既に⼤きい • 「あったら便利」 程度では⻭⽌めがきかなくなる

    • 糖⾐構⽂程度の開発者体験のためにユーザー体験を毀損してはならない • 依存関係を増やせば増やすほど苦労するのは未来の誰か • 主要ライブラリはそれぞれの理想を求めてダイナミックに進化する • 各依存先の理想と理想の折り合いに疲弊するのがフロントエンドの近代史