Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Next.js でリアーキテクトした話 / story-of-re-architect-with-nextjs

5d9cd19df0e91caac118b793b4f803d5?s=47 Takepepe
November 27, 2021

Next.js でリアーキテクトした話 / story-of-re-architect-with-nextjs

5d9cd19df0e91caac118b793b4f803d5?s=128

Takepepe

November 27, 2021
Tweet

More Decks by Takepepe

Other Decks in Technology

Transcript

  1. Next.js でリアーキテクトした話 JSConf JP 2021 @Takepepe

  2. 吉井 健文 / Takepepe 株式会社リクルート 横断エンジニアリング部 APソリューショングループ フロントエンドエンジニア・アーキテクト

  3. リクルートでの働き方 横断組織の一員として、 社内事業ソフトウェア開発・技術支援をしています。 主に、得意なフロントエンド開発で携わっています。

  4. これまでの働き方 【新規立ち上げプロジェクト】 ▪ 最適な技術スタックをメンバーと選定し、フルスクラッチ 【リアーキテクトプロジェクト】 ▪ 既存 MPA(Template-Engine)を FE /

    BE に分離するプロジェクト
  5. これまでの働き方 【新規立ち上げプロジェクト】 ▪ 最適な技術スタックをメンバーと選定し、フルスクラッチ 【リアーキテクトプロジェクト】 ▪ 既存 MPA(Template-Engine)を FE /

    BE に分離するプロジェクト 「開発体制構築・開発フロー構築」をした話
  6. フロントエンド技術スタック ▪ Next.js ▪ CSS Modules ▪ SWR ▪ OpenAPI

    Generator ▪ MSW ▪ Storybook ▪ reg-suit ▪ jest ▪ testing-library ▪ Cypress
  7. Next.js 選定基準 ▪ ページ分割による最適化など、パフォーマンス面でメリットを得やすい ▪ 数あるアプローチの中から、要件に適合したものを選べる ▪ SSR / ISR

    / SSG など、組み合わせが自在(AMP の実績も) 社内ナレッジも蓄積されてきている
  8. アーキテクチャ構成 ▪ Node.js プロセスは稼働しない ▪ Static Generation による full CSR

    ▪ Web Server は Nginx API Server Next.js SSG + CSR Nginx
  9. API 検討 ▪ BFF として機能する API サーバー ▪ BFF 開発はバックエンドチームが担当

    ▪ FE 観点から、適した API を提案 ▪ Component 境界 = API 境界 = タスク境界 画面単位(Component単位)がタスク境界としても適切な粒度だった
  10. プロジェクト課題整理 【1】リリース済み内容をリグレッションさせないこと 【2】React 開発に慣れたメンバーがアサインできるとは限らないこと 【3】フロントエンドエンジニアが先行して OpenAPI を定義する必要がある 【4】10人並列でも開発可能な体制をつくること

  11. 【課題1】 リリース済み内容を リグレッションさせないこと

  12. リリース済み内容に沿った、マークアップの再現について。 再現するプロジェクトの CSS は BEM で書かれていました。 (全体的に綺麗に設計されている良い設計) BEM の Block

    は Component 単位として扱うことができるため、 CSS 定義から遡り、Component へ書き換えを進めました。 React Component への書き換え 【課題1】リリース済み内容をリグレッションさせないこと
  13. <ul class="breadcrumb"> <li class="breadcrumb__item"></li> <li class="breadcrumb__item"></li> <li class="breadcrumb__item breadcrumb--mb0"></li> </ul>

    書き換え前のマークアップ .breadcrumb { display: flex; &__item { padding-left: 24px; } &--mb0 { margin-bottom: 0; } } 【課題1】リリース済み内容をリグレッションさせないこと
  14. 書き換え後の React Component // Block はアッパーキャメル変換し、そのまま Component名称とする function Breadcrumb(props) {

    return <ul {...props} className={styles.breadcrumb} /> } // Element は Block 接頭辞をもった Component名称とする function BreadcrumbItem(props) { return <li {...props} className={styles.item} /> } function MyComponent() { return ( <Breadcrumb> <BreadcrumbItem></BreadcrumbItem> <BreadcrumbItem></BreadcrumbItem> <BreadcrumbItem data-mb0></BreadcrumbItem> </Breadcrumb> ) } .breadcrumb { display: flex; } .item { padding-left: 24px; &[data-mb0] { margin-bottom: 0; } } 【課題1】リリース済み内容をリグレッションさせないこと
  15. 書き換えのマッピングコスト 元の CSS 設計が整備されていたこともあり、 スムーズに書き換えを行うことができました。 ▪ CSS Modules に BEM

    定義を移植 ▪ 命名は変更せず、Case 変換し Component 名称とした ▪ Modifier を data 属性に変換した ▪ class 名から Component (Atom) の特定が容易 【課題1】リリース済み内容をリグレッションさせないこと
  16. 元の CSS 設計が整備されていたこともあり、 スムーズに書き換えを行うことができました。 ▪ CSS Modules に BEM 定義を移植

    ▪ 命名は変更せず、Case 変換し Component 名称とした ▪ Modifier を data 属性に変換した ▪ class 名から Component (Atom) の特定が容易 書き換えのマッピングコスト 探しやすく、マッピングコストが最小限。再現性の高い書き換えに 【課題1】リリース済み内容をリグレッションさせないこと
  17. CSS 定義の削除 .alert-text { color: $col-accent; } .annotation { font-size:

    1rem; } .nodata { display: block; text-align: center; } 最後まで残った、 使われているか不明な Global 定義。 【課題1】リリース済み内容をリグレッションさせないこと
  18. CSS 定義の削除 最後まで残った、 使われているか不明な Global 定義。 この定義削除にあたり、 Visual Regression Testing

    を活用。 【課題1】リリース済み内容をリグレッションさせないこと .alert-text { color: $col-accent; } .annotation { font-size: 1rem; } .nodata { display: block; text-align: center; }
  19. ▪ 書き換えのタイミングで、Storybook をコミットしていた ▪ リグレッションに備え、reg-suit を導入していた ▪ CSS に対し、積極的なリファクタリングが行えた 書き換えのマッピングコスト

    【課題1】リリース済み内容をリグレッションさせないこと
  20. ▪ 書き換えのタイミングで、Storybook をコミットしていた ▪ リグレッションに備え、reg-suit を導入していた ▪ CSS に対し、積極的なリファクタリングが行えた 書き換えのマッピングコスト

    Storybook は開発軸だけでなく、テスト戦略としても必須 【課題1】リリース済み内容をリグレッションさせないこと
  21. ▪ 現在、CSS ソリューションはかつてないほど乱立 ▪ 異なる CSS ソリューション間で移植しやすい定義かどうか ▪ 特定の CSS

    ソリューションでしか実現できない内容ではないか 次世代のリアーキテクトに向けて 【課題1】リリース済み内容をリグレッションさせないこと
  22. ▪ 現在、CSS ソリューションはかつてないほど乱立 ▪ 異なる CSS ソリューション間で移植しやすい定義かどうか ▪ 特定の CSS

    ソリューションでしか実現できない内容ではないか 次世代のリアーキテクトに向けて CSSソリューションは、次世代のマッピングコストも配慮して 【課題1】リリース済み内容をリグレッションさせないこと
  23. 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと

  24. まだ React は「誰でも書ける」まで至っていません。 初学者であっても、参画しやすいコードベースを目指しました。 ▪ 見様見真似でも、ある程度実装できる ▪ 学習しながらでも、開発に参画できる ▪ つまづきやすいポイント・そうではないポイントが分離されている

    初学者でも参画しやすいコードベースを目指して 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと
  25. 初学者でも参画しやすいコードベースを目指して まだ React は「誰でも書ける」まで至っていません。 初学者であっても、参画しやすいコードベースを目指しました。 ▪ 見様見真似でも、ある程度実装できる ▪ 学習しながらでも、開発に参画できる ▪

    つまづきやすいポイント・そうではないポイントが分離されている コードベースの理解しやすさは、学習曲線に影響する 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと
  26. ▪ 【易】Component 構築 → マークアップの延長線 ▪ 【易】型定義で縛って、Props を渡す ▪ 【難】込み入った

    hooks の記述 ▪ 【難】React Context Provider の設計 React 初学者でも習得しやすい点 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと
  27. ▪ 【易】Component 構築 → マークアップの延長線 ▪ 【易】型定義で縛って、Props を渡す ▪ 【難】込み入った

    hooks の記述 ▪ 【難】React Context Provider の設計 React 初学者が習得し辛い点 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと
  28. ▪ 【易】Component 構築 → マークアップの延長線 ▪ 【易】型定義で縛って、Props を渡す ▪ 【難】込み入った

    hooks の記述 ▪ 【難】React Context Provider の設計 React 初学者が習得し辛い点 Custom Hooks の整備に着手 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと
  29. Custom Hooks でコードベースを標準化 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと export function useMyComponent()

    { const { openModal } = useModal(); const handleClick = () => { openModal({ contentsNode: <div></div> }); }; } モーダルを表示する Custom Hooks
  30. Custom Hooks でコードベースを標準化 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと export function useMyComponent()

    { const { openAlertDialog } = useAlertDialog(); const handleClick = () => { openAlertDialog({ text: "エラーが発生しました ", buttonLabel: "OK", }); } } ダイアログを表示する Custom Hooks
  31. Custom Hooks でコードベースを標準化 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと export function useMyComponent()

    { const { showNotification } = useNotification(); const handleClick = () => { showNotification("削除しました"); } } ノティフィケーションを表示する Custom Hooks
  32. Custom Hooks でコードベースを標準化 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと export function useMyComponent()

    { const { errorMessages, handleErrorWithErrorMessages, } = useHandleErrorWithErrorMessage(); const { execFetch } = useFetcher(async () => { try { ... } catch (err) { handleErrorWithErrorMessages(err); } }); } エラーハンドリングを集約した Custom Hooks
  33. Custom Hooks でコードベースを標準化 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと export function useMyComponent()

    { const { validationErrors, handleValidationErrorsWithAlertDialog, } = useHandleValidationErrorsWithAlertDialog(); const { execFetch } = useFetcher(async () => { try { ... } catch (err) { handleValidationErrorsWithAlertDialog(err); } }); } エラーハンドリング・Component 表示を兼ねた Custom Hooks
  34. ▪ 多くの画面で処理を共通化できた ▪ 見様見真似でも Component 作成が可能に ▪ 画面毎の考察ポイントは、画面固有のロジック実装のみ Custom Hooks

    でコードベースを標準化 【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと Custom Hooks でコードベースを標準化することができた
  35. 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

  36. OpenAPI でバックエンドとフロントエンドの齟齬をなくす ▪ BFF として機能する API サーバー ▪ Component を起点として

    API を考える ▪ フロントエンドエンジニアが OpenAPI を書く スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
  37. OpenAPI でバックエンドとフロントエンドの齟齬をなくす ▪ BFF として機能する API サーバー ▪ Component を起点として

    API を考える ▪ フロントエンドエンジニアが OpenAPI を書く スキーマ駆動開発 API 定義が予め決まっている通常の開発フローとは「逆」 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
  38. OpenAPI でバックエンドとフロントエンドの齟齬をなくす ▪ BFF として機能する API サーバー ▪ Component を起点として

    API を考える ▪ フロントエンドエンジニアが OpenAPI を書く スキーマ駆動開発 OpenAPI 定義に慣れている開発メンバーは少数。定義のしやすさが課題に 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
  39. OpenAPI 定義への取り組み ー User 編集モーダルを作る例 ー 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

  40. API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある Component を組み上げ、API に求めるデータを考える export

    type ServerProps = { }; export const ModalEditUser: React.VFC<ServerProps> = (props) => { return ( <form> </form> ); };
  41. API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある export type ServerProps =

    { id: string; firstName?: string; lastName?: string; }; Component を組み上げ、API に求めるデータを考える export const ModalEditUser: React.VFC<ServerProps> = (props) => { return ( <form> <input type="hidden" name="id" defaultValue={props.id} /> <input type="text" name="firstName" defaultValue={props.firstName} /> <input type="text" name="lastName" defaultValue={props.lastName} /> <button>送信</button> </form> ); };
  42. API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある Component を組み上げ、API に求めるデータを考える export

    type ServerProps = { id: string; firstName?: string; lastName?: string; }; export const ModalEditUser: React.VFC<ServerProps> = (props) => { return ( <form> <input type="hidden" name="id" defaultValue={props.id} /> <input type="text" name="firstName" defaultValue={props.firstName} /> <input type="text" name="lastName" defaultValue={props.lastName} /> <button>送信</button> </form> ); };
  43. API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある MSW でモックを書き、Component が成立するか検討 export

    const handlers = [ rest.get("/path/to/api", (_, res, ctx) => { return res( ctx.json({})); }), ];
  44. API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある MSW でモックを書き、Component が成立するか検討 export

    const handlers = [ rest.get("/path/to/api", (_, res, ctx) => { return res( ctx.json({ id: "xxxx", firstName: "taro", lastName: "yamada", })); }), ];
  45. API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある 更新系のメソッドも、モックが記述可能 export const handlers

    = [ rest.get("/path/to/api", (_, res, ctx) => { return res( ctx.json({ id: "xxxx", firstName: "taro", lastName: "yamada", })); }), ]; export const handlers = [ rest.put("/path/to/api", (req, res, ctx) => { return res(ctx.json({ statusCode: 200 })); }), ];
  46. API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある 要件定義とすりあわせ、考慮漏れの洗い出し export const handlers

    = [ rest.get("/path/to/api", (_, res, ctx) => { return res( ctx.json({ id: "xxxx", firstName: "taro", lastName: "yamada", })); }), ]; export const handlers = [ rest.put<Partial<{ id: string }>>("/path/to/api", (req, res, ctx) => { return res(ctx.json({ statusCode: 200 })); }), ];
  47. API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある 要件定義とすりあわせ、考慮漏れの洗い出し export const handlers

    = [ rest.get("/path/to/api", (_, res, ctx) => { return res( ctx.json({ id: "xxxx", firstName: "taro", lastName: "yamada", })); }), ]; export const handlers = [ rest.put<Partial<{ id: string }>>("/path/to/api", (req, res, ctx) => { if (req.body.id) { return res(ctx.json({ statusCode: 200 })); } return res( ctx.status(400), ctx.json({ statusCode: 400, errorMessages: ["不正なリクエストです"] }) ); }), ];
  48. API スキーマの考察 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある API スキーマ考察の軸となった export const

    handlers = [ rest.get("/path/to/api", (_, res, ctx) => { return res( ctx.json({ id: "xxxx", firstName: "taro", lastName: "yamada", })); }), ]; export const handlers = [ rest.put<Partial<{ id: string }>>("/path/to/api", (req, res, ctx) => { if (req.body.id) { return res(ctx.json({ statusCode: 200 })); } return res( ctx.status(400), ctx.json({ statusCode: 400, errorMessages: ["不正なリクエストです"] }) ); }), ];
  49. Stoplight Studio を使う Open API 定義にあたり、 チーム内標準ツールとして Stoplight Studio を使用。

    MSW のレスポンスから、 サンプル JSON を取得。 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
  50. Stoplight Studio を使う Stoplight Studio には、 JSON から OpenAPI 定義を

    Generateできる機能がある。 この機能を使い、 OpenAPI 定義の工数を削減。 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
  51. Stoplight Studio を使う 不足している定義を追加し、 description に詳細を記載。 レビューコミュニケーションに 役立てた。 【課題3】 フロントエンドエンジニアが先行して

    OpenAPI を定義する必要がある
  52. スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある $ npm run openapi:gen 書き上がった

    OpenAPI から、 fetch client を生成 ######################################### # Thanks for using OpenAPI Generator. # # Please consider donation to help us maintain this project 🙏 # # https://opencollective.com/openapi_generator/donate # #########################################
  53. スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある fetch client を、 Component にバインド

    export const ModalEditUserBase: React.VFC<ServerProps> = (props) => { return ( <form> <input type="hidden" name="id" defaultValue={props.id} /> <input type="text" name="firstName" defaultValue={props.firstName} /> <input type="text" name="lastName" defaultValue={props.lastName} /> <button>送信</button> </form> ); }; export const ModalEditUser: React.VFC = () => { const { data, error } = useSWR( "/path/to/api", get<ServerProps>("path/to/api") ); if (error) return <ErrorFallback error={error} />; if (!data) return <>loading...</>; return <ModalEditUserBase {...data} />; };
  54. スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある export const ModalEditUserBase: React.VFC<ServerProps> =

    (props) => { return ( <form> <input type="hidden" name="id" defaultValue={props.id} /> <input type="text" name="firstName" defaultValue={props.firstName} /> <input type="text" name="lastName" defaultValue={props.lastName} /> <button>送信</button> </form> ); }; export const ModalEditUser: React.VFC = () => { const { data, error } = useSWR( "/path/to/api", organismsApi.organismsModalEditUserGet() ); if (error) return <ErrorFallback error={error} />; if (!data) return <>loading...</>; return <ModalEditUserBase {...data} />; }; fetch client を、 Component にバインド
  55. スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある この段階で期待値と、OpenAPI 定義に齟齬があれば気付く(型推論あわせ) export const ModalEditUserBase:

    React.VFC<ServerProps> = (props) => { return ( <form> <input type="hidden" name="id" defaultValue={props.id} /> <input type="text" name="firstName" defaultValue={props.firstName} /> <input type="text" name="lastName" defaultValue={props.lastName} /> <button>送信</button> </form> ); }; export const ModalEditUser: React.VFC = () => { const { data, error } = useSWR( "/path/to/api", organismsApi.organismsModalEditUserGet() ); if (error) return <ErrorFallback error={error} />; if (!data) return <>loading...</>; return <ModalEditUserBase {...data} />; };
  56. スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある ▪ 確度の高い状態で、バックエンド担当にレビュー依頼 ▪ レビューで受けた指摘のもと、OpenAPI 定義を修正

    ▪ 修正内容を伝搬させるため、fetch client を再生成 ▪ 影響箇所を TypeScript のコンパイルエラーで検知
  57. スキーマ駆動開発 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある ▪ 確度の高い状態で、バックエンド担当にレビュー依頼 ▪ レビューで受けた指摘のもと、OpenAPI 定義を修正

    ▪ 修正内容を伝搬させるため、fetch client を再生成 ▪ 影響箇所を TypeScript のコンパイルエラーで検知 OpenAPI と Component の疎通齟齬が TypeScript で担保された
  58. 【課題4】 10人並列でも開発可能な体制を つくること

  59. 課題「123」の取り組みでコードベースの標準化が整う ✅ 課題1:マークアップから Component への確実なマッピング ✅ 課題2:React Component の一律な実装 ✅

    課題3:OpenAPI 定義検討のフロー 増員とチームビルディング 【課題4】10人並列でも開発可能な体制をつくること
  60. 量産フェーズに入り、大人数での並列開発が開始 ▪ 2人 → 5人 → 10人 と、1ヶ月間隔でメンバーが倍増 ▪ 新規メンバー受入を兼ねながら進行

    ▪ 並行して開発可能な体制を整える 増員とチームビルディング 【課題4】10人並列でも開発可能な体制をつくること
  61. 量産フェーズに入り、大人数での並列開発が開始 ▪ 2人 → 5人 → 10人 と、1ヶ月間隔でメンバーが倍増 ▪ 新規メンバー受入を兼ねながら進行

    ▪ 並行して開発可能な体制を整える 増員とチームビルディング スムーズな増員計画を念頭に 【課題4】10人並列でも開発可能な体制をつくること
  62. 定型処理の自動化 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

  63. hygen による定型出力 大人数での並列開発は、lint・formatter だけでは 防げないガイドライン違反が起こりえます。 また、管理上 OpenAPI と Component の

    命名規則・書式を一律にする必要性がありました。 そこで活用したのが「hygen」です。 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
  64. $ npm run new:swrfc 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある > fe@0.1.0

    new:swrfc > hygen new swrfc ✔ What is the name of component? · npm scripts に hygen のエントリーポイントを登録 hygen による定型出力
  65. $ npm run new:swrfc 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある > fe@0.1.0

    new:swrfc > hygen new swrfc ✔ What is the name of component? · ModalEditUser CLI に Component 名称を入力すると… hygen による定型出力
  66. $ npm run new:swrfc 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある > fe@0.1.0

    new:swrfc > hygen new swrfc ✔ What is the name of component? · ModalEditUser Loaded templates: .hygen added: src/components/organisms/ModalEditUser/apis.mock.ts added: src/components/organisms/ModalEditUser/ModalEditUser.stories.tsx added: src/components/organisms/ModalEditUser/ModalEditUser.test.tsx added: src/components/organisms/ModalEditUser/ModalEditUser.tsx added: src/components/organisms/ModalEditUser/index.tsx added: /Users/Me/openapi/src/paths/organisms/modal_edit_user.yaml added: src/components/organisms/ModalEditUser/styles.module.scss Component 命名に沿ったファイル群が出力される hygen による定型出力
  67. $ npm run new:swrfc 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある > fe@0.1.0

    new:swrfc > hygen new swrfc ✔ What is the name of component? · ModalEditUser Loaded templates: .hygen added: src/components/organisms/ModalEditUser/apis.mock.ts added: src/components/organisms/ModalEditUser/ModalEditUser.stories.tsx added: src/components/organisms/ModalEditUser/ModalEditUser.test.tsx added: src/components/organisms/ModalEditUser/ModalEditUser.tsx added: src/components/organisms/ModalEditUser/index.tsx added: /Users/Me/openapi/src/paths/organisms/modal_edit_user.yaml added: src/components/organisms/ModalEditUser/styles.module.scss React Component に必要なファイル群と… hygen による定型出力
  68. $ npm run new:swrfc 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある > fe@0.1.0

    new:swrfc > hygen new swrfc ✔ What is the name of component? · ModalEditUser Loaded templates: .hygen added: src/components/organisms/ModalEditUser/apis.mock.ts added: src/components/organisms/ModalEditUser/ModalEditUser.stories.tsx added: src/components/organisms/ModalEditUser/ModalEditUser.test.tsx added: src/components/organisms/ModalEditUser/ModalEditUser.tsx added: src/components/organisms/ModalEditUser/index.tsx added: /Users/Me/openapi/src/paths/organisms/modal_edit_user.yaml added: src/components/organisms/ModalEditUser/styles.module.scss OpenAPI 定義の雛形・MSW ハンドラーも同時に自動生成 hygen による定型出力
  69. ModalEditUser.tsx 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある export const ModalEditUser: React.VFC =

    () => { const { data, error } = useSWR(apiPath(), () => get<ServerProps>(apiPath())); if (error) return <ErrorFallback error={error} />; if (!data) return <>loading...</>; return <ModalEditUserBase {...data} />; }; ファイル命名ミスがなく、コンポーネント書式も一律に hygen による定型出力
  70. ModalEditUser.stories.tsx 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある const defaultArgs: Props = {

    ...data }; const Template: Story<Props> = (args) => ( <ModalEditUserBase {...defaultArgs} {...args} /> ); export const Index: Story<Props> = Template.bind({}); Index.args = {}; export default { title: "organisms/ModalEditUser", } as Meta; Storybook の雛形ファイルも同時に生成 hygen による定型出力
  71. 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある describe("organisms/ModalEditUser", () => { describe("異常系レスポンスの表示", ()

    => { describe("エラーレスポンスが返ってきた時", () => { test.skip("エラーが表示される", async () => {}); }); }); describe("正常系レスポンスの表示", () => { describe("正常レスポンスが返ってきた時", () => { test.skip("コンテンツが表示される", async () => {}); }); }); }); ModalEditUser.test.tsx テストの雛形ファイルも同時に生成 hygen による定型出力
  72. 【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある modal_edit_user.yaml post: summary: XXX::画面名称::POST description: 画面概要

    operationId: OrganismsModalEditUserPost tags: - Organisms responses: '201': description: Created content: application/json: schema: $ref: ../../components/common_201.yaml '400': description: Bad Request content: application/json: schema: $ref: ../../components/common_400.yaml '403': description: Forbidden content: application/json: schema: $ref: ../../components/common_403.yaml Component に紐づいた Open API の雛形ファイルも同時に生成 hygen による定型出力 openapi: 3.0.0 info: title: XXX::画面名称 version: '1.0' servers: - url: http://localhost:8080 description: development paths: /organisms/modal_edit_user: get: summary: XXX::画面名称::GET description: 画面概要 operationId: OrganismsModalEditUserGet tags: - Organisms responses: '200': description: OK content: application/json: schema: $ref: ../../components/common_200.yaml
  73. コード標準化・定型作業の自動化にあわせ「タスク標準化」にも着手 ▪ 「定型的な工程・非定型的な工程」を分離 ▪ 「レビュー依頼先・レビュー観点」を分離 増員とチームビルディング 【課題4】10人並列でも開発可能な体制をつくること

  74. 増員とチームビルディング 大人数での開発が本格化する前に、ばらつきが出ない「標準化」を徹底した 【課題4】10人並列でも開発可能な体制をつくること コード標準化・定型作業の自動化にあわせ「タスク標準化」にも着手 ▪ 「定型的な工程・非定型的な工程」を分離 ▪ 「レビュー依頼先・レビュー観点」を分離

  75. 分離された5段階タスク 【課題4】10人並列でも開発可能な体制をつくること 1画面あたり、5段階に分離されたタスク Scaffold 生成 Component 構築 API 考察 接続

    OpenAPI 定義 テスト拡充 1画面
  76. 分離された5段階タスク 【課題4】10人並列でも開発可能な体制をつくること PR 単位も一律になるため、差分・レビュー観点が一律に Scaffold 生成 Component 構築 API 考察

    接続 OpenAPI 定義 テスト拡充 1画面
  77. 新規メンバー受け入れと、複数人並列開発を実現した要因 ▪ 参画ハードルが低く、標準化されたコードベース ▪ タスク境界が分離されているため、閉じた影響範囲で作業が完遂できる ▪ 完了した類似タスク範例があるため、作業内容の認知負荷が低い API とComponentが結合している設計 メンバーからも「開発しやすい」と評価

    【課題4】10人並列でも開発可能な体制をつくること
  78. 新規メンバー受け入れと、複数人並列開発を実現した要因 ▪ 参画ハードルが低く、標準化されたコードベース ▪ タスク境界が分離されているため、閉じた影響範囲で作業が完遂できる ▪ 完了した類似タスク範例があるため、作業内容の認知負荷が低い API とComponentが結合している設計 【成果】マイルストンから6週間前倒して開発完了

    【課題4】10人並列でも開発可能な体制をつくること
  79. 設計と開発フローは不可分であり、 「継続的チームビルディングの基礎となる」 という事を実感できた案件でした

  80. ー ご清聴ありがとうございました ー