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

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

Takepepe
November 27, 2021

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

Takepepe

November 27, 2021
Tweet

More Decks by Takepepe

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  6. フロントエンド技術スタック
    ■ Next.js
    ■ CSS Modules
    ■ SWR
    ■ OpenAPI Generator
    ■ MSW
    ■ Storybook
    ■ reg-suit
    ■ jest
    ■ testing-library
    ■ Cypress

    View Slide

  7. Next.js 選定基準
    ■ ページ分割による最適化など、パフォーマンス面でメリットを得やすい
    ■ 数あるアプローチの中から、要件に適合したものを選べる
    ■ SSR / ISR / SSG など、組み合わせが自在(AMP の実績も)
    社内ナレッジも蓄積されてきている

    View Slide

  8. アーキテクチャ構成
    ■ Node.js プロセスは稼働しない
    ■ Static Generation による full CSR
    ■ Web Server は Nginx
    API Server
    Next.js
    SSG + CSR
    Nginx

    View Slide

  9. API 検討
    ■ BFF として機能する API サーバー
    ■ BFF 開発はバックエンドチームが担当
    ■ FE 観点から、適した API を提案
    ■ Component 境界 = API 境界 = タスク境界
    画面単位(Component単位)がタスク境界としても適切な粒度だった

    View Slide

  10. プロジェクト課題整理
    【1】リリース済み内容をリグレッションさせないこと
    【2】React 開発に慣れたメンバーがアサインできるとは限らないこと
    【3】フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
    【4】10人並列でも開発可能な体制をつくること

    View Slide

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

    View Slide

  12. リリース済み内容に沿った、マークアップの再現について。
    再現するプロジェクトの CSS は BEM で書かれていました。
    (全体的に綺麗に設計されている良い設計)
    BEM の Block は Component 単位として扱うことができるため、
    CSS 定義から遡り、Component へ書き換えを進めました。
    React Component への書き換え
    【課題1】リリース済み内容をリグレッションさせないこと

    View Slide






  13. 書き換え前のマークアップ
    .breadcrumb {
    display: flex;
    &__item {
    padding-left: 24px;
    }
    &--mb0 {
    margin-bottom: 0;
    }
    }
    【課題1】リリース済み内容をリグレッションさせないこと

    View Slide

  14. 書き換え後の React Component
    // Block はアッパーキャメル変換し、そのまま Component名称とする
    function Breadcrumb(props) {
    return
    }
    // Element は Block 接頭辞をもった Component名称とする
    function BreadcrumbItem(props) {
    return
    }
    function MyComponent() {
    return (





    )
    }
    .breadcrumb {
    display: flex;
    }
    .item {
    padding-left: 24px;
    &[data-mb0] {
    margin-bottom: 0;
    }
    }
    【課題1】リリース済み内容をリグレッションさせないこと

    View Slide

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

    View Slide

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

    View Slide

  17. CSS 定義の削除
    .alert-text {
    color: $col-accent;
    }
    .annotation {
    font-size: 1rem;
    }
    .nodata {
    display: block;
    text-align: center;
    }
    最後まで残った、
    使われているか不明な Global 定義。
    【課題1】リリース済み内容をリグレッションさせないこと

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. 【課題2】
    React 開発に慣れたメンバーが
    アサインできるとは限らないこと

    View Slide

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

    View Slide

  25. 初学者でも参画しやすいコードベースを目指して
    まだ React は「誰でも書ける」まで至っていません。
    初学者であっても、参画しやすいコードベースを目指しました。
    ■ 見様見真似でも、ある程度実装できる
    ■ 学習しながらでも、開発に参画できる
    ■ つまづきやすいポイント・そうではないポイントが分離されている
    コードベースの理解しやすさは、学習曲線に影響する
    【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  34. ■ 多くの画面で処理を共通化できた
    ■ 見様見真似でも Component 作成が可能に
    ■ 画面毎の考察ポイントは、画面固有のロジック実装のみ
    Custom Hooks でコードベースを標準化
    【課題2】 React 開発に慣れたメンバーが アサインできるとは限らないこと
    Custom Hooks でコードベースを標準化することができた

    View Slide

  35. 【課題3】
    フロントエンドエンジニアが先行して
    OpenAPI を定義する必要がある

    View Slide

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

    View Slide

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

    View Slide

  38. OpenAPI でバックエンドとフロントエンドの齟齬をなくす
    ■ BFF として機能する API サーバー
    ■ Component を起点として API を考える
    ■ フロントエンドエンジニアが OpenAPI を書く
    スキーマ駆動開発
    OpenAPI 定義に慣れている開発メンバーは少数。定義のしやすさが課題に
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    View Slide

  39. OpenAPI 定義への取り組み
    ー User 編集モーダルを作る例 ー
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    View Slide

  40. API スキーマの考察
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
    Component を組み上げ、API に求めるデータを考える
    export type ServerProps = {
    };
    export const ModalEditUser: React.VFC = (props) => {
    return (


    );
    };

    View Slide

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




    送信

    );
    };

    View Slide

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




    送信

    );
    };

    View Slide

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

    View Slide

  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",
    }));
    }),
    ];

    View Slide

  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 }));
    }),
    ];

    View Slide

  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>("/path/to/api", (req, res, ctx) => {
    return res(ctx.json({ statusCode: 200 }));
    }),
    ];

    View Slide

  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>("/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: ["不正なリクエストです"] })
    );
    }),
    ];

    View Slide

  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>("/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: ["不正なリクエストです"] })
    );
    }),
    ];

    View Slide

  49. Stoplight Studio を使う
    Open API 定義にあたり、
    チーム内標準ツールとして
    Stoplight Studio を使用。
    MSW のレスポンスから、
    サンプル JSON を取得。
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    View Slide

  50. Stoplight Studio を使う
    Stoplight Studio には、
    JSON から OpenAPI 定義を
    Generateできる機能がある。
    この機能を使い、
    OpenAPI 定義の工数を削減。
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    View Slide

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

    View Slide

  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 #
    #########################################

    View Slide

  53. スキーマ駆動開発
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
    fetch client を、 Component にバインド
    export const ModalEditUserBase: React.VFC = (props) => {
    return (




    送信

    );
    };
    export const ModalEditUser: React.VFC = () => {
    const { data, error } = useSWR(
    "/path/to/api",
    get("path/to/api")
    );
    if (error) return ;
    if (!data) return <>loading...>;
    return ;
    };

    View Slide

  54. スキーマ駆動開発
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
    export const ModalEditUserBase: React.VFC = (props) => {
    return (




    送信

    );
    };
    export const ModalEditUser: React.VFC = () => {
    const { data, error } = useSWR(
    "/path/to/api",
    organismsApi.organismsModalEditUserGet()
    );
    if (error) return ;
    if (!data) return <>loading...>;
    return ;
    };
    fetch client を、 Component にバインド

    View Slide

  55. スキーマ駆動開発
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
    この段階で期待値と、OpenAPI 定義に齟齬があれば気付く(型推論あわせ)
    export const ModalEditUserBase: React.VFC = (props) => {
    return (




    送信

    );
    };
    export const ModalEditUser: React.VFC = () => {
    const { data, error } = useSWR(
    "/path/to/api",
    organismsApi.organismsModalEditUserGet()
    );
    if (error) return ;
    if (!data) return <>loading...>;
    return ;
    };

    View Slide

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

    View Slide

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

    View Slide

  58. 【課題4】
    10人並列でも開発可能な体制を
    つくること

    View Slide

  59. 課題「123」の取り組みでコードベースの標準化が整う
    ✅ 課題1:マークアップから Component への確実なマッピング
    ✅ 課題2:React Component の一律な実装
    ✅ 課題3:OpenAPI 定義検討のフロー
    増員とチームビルディング
    【課題4】10人並列でも開発可能な体制をつくること

    View Slide

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

    View Slide

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

    View Slide

  62. 定型処理の自動化
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    View Slide

  63. hygen による定型出力
    大人数での並列開発は、lint・formatter だけでは
    防げないガイドライン違反が起こりえます。
    また、管理上 OpenAPI と Component の
    命名規則・書式を一律にする必要性がありました。
    そこで活用したのが「hygen」です。
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある

    View Slide

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

    View Slide

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

    View Slide

  66. $ npm run new:swrfc
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
    > [email protected] 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 による定型出力

    View Slide

  67. $ npm run new:swrfc
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
    > [email protected] 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 による定型出力

    View Slide

  68. $ npm run new:swrfc
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
    > [email protected] 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 による定型出力

    View Slide

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

    View Slide

  70. ModalEditUser.stories.tsx
    【課題3】 フロントエンドエンジニアが先行して OpenAPI を定義する必要がある
    const defaultArgs: Props = { ...data };
    const Template: Story = (args) => (

    );
    export const Index: Story = Template.bind({});
    Index.args = {};
    export default {
    title: "organisms/ModalEditUser",
    } as Meta;
    Storybook の雛形ファイルも同時に生成
    hygen による定型出力

    View Slide

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

    View Slide

  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

    View Slide

  73. コード標準化・定型作業の自動化にあわせ「タスク標準化」にも着手
    ■ 「定型的な工程・非定型的な工程」を分離
    ■ 「レビュー依頼先・レビュー観点」を分離
    増員とチームビルディング
    【課題4】10人並列でも開発可能な体制をつくること

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  79. 設計と開発フローは不可分であり、
    「継続的チームビルディングの基礎となる」
    という事を実感できた案件でした

    View Slide

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

    View Slide