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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide






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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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


    );
    };

    View full-size slide

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




    送信

    );
    };

    View full-size slide

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




    送信

    );
    };

    View full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide