Next.js(Pages Router)のディレクトリ構成を見直した話
はじめに
株式会社Rehab for JAPAN のまっちゃんです。
私は昨年12月に入社し、通所介護の記録を管理するデイリーというアプリケーションのフロントエンドの開発を行っています。
今回は、デイリーチームにアサインして行ったフロントエンドのディレクトリ構成の見直しについて紹介いたします。
ディレクトリ構成は、プロジェクトの規模やプロジェクトの遍歴によって変わるものと考えております。これから紹介するものが最適なものとは限りませんし、他のプロジェクトにそのまま適用できるものでも有りませんが、参考にしていただければ幸いです。
なぜディレクトリ構成を見直したのか
テストコードを書きやすくするため
きっかけは、フロントエンドの技術負債を解消するための一貫として始めた技術負債のブレストでした。プロダクトの開発が落ち着きつつあったタイミングがあり、技術負債の解消に取り組むことになりました。どこから手を付けるのが効果的か考えた結果、メンバー全員がテストコードの追加をしたいとの意見が出たためテストコードを追加していくことになりました。Unitテストも充足している部分とそうでない部分が分かれており、またUIコンポーネントが巨大化してきており、既存のソースコードにテストを追加するだけでも難しくなってきました。そのため、改めてファイル構成を見直し、テストコードを追加しやすい環境を整えることにしました。
変更したディレクトリ構成
ディレクトリ構成と言っても前述の通り目的は、テストコードを追加しやすい環境を整えることでした。そのため、以下のことを意識し、ディレクトリ構成を変更しました。
変更時に意識したこと
-
単体テストを記載するため、責務が多き過ぎてUTが記載できないファイルを分割できること
- 分割する際にある程度悩まずに開発者の共通意識を持って分割できること
-
機能追加と同時並行で行えること
- 大きく構成を変えてプロダクト開発が行えない状態を作らないこと
- 既存のファイル分割が目的のため、ファイル名を変えたりディレクトリを変えたりせず、基本的にはファイルを追加する方向で変更
-
Unitテストを書きやすくするために責務で分割すること
- UI / 状態管理 / ロジック が混在しないようにすること
変更後
src
├── _pages
│ ├── error
│ └── [PageName]
│ ├── index.tsx # エントリポイント(UIのみ)
│ ├── useHook.ts # 同ディレクトリのHook関数
│ ├── useHook.test.ts # 同ディレクトリのHook関数
│ ├── type.ts # 同ページで使用する型情報
│ ├── constants.tsx # 同ページで使用する定数
│ ├── utils # 同ページで使用するロジック(画面やステートに依存しないもの) ※新設
│ │ │ 1ファイルだとデカくなる可能性あるためディレクトリ
│ │ ├── [LogicFileName].test.ts
│ │ └── [LogicFileName].ts
│ └── [ComponentName]
│ └── [PageName配下と同様]
│
├── components # 共通コンポーネント
│ └── [ComponentName]
│ ├── [ComponentName].stories.tsx
│ ├── [ComponentName].test.tsx
│ ├── [ComponentName].tsx
│ └── index.ts
│
├── constants # 定数ディレクトリ
│ └── [定数種別名].ts
│
├── containers # 複数ページにまたがるが
│ └── [ContainerName]
│ ├── index.tsx # エントリポイント(UIのみ)
│ ├── useHook.ts # 同ディレクトリのHook関数
│ ├── useHook.test.ts # 同ディレクトリのHook関数
│ ├── type.ts # 同ページで使用する型情報
│ ├── constants.tsx # 同ページで使用する定数
│ └── utils # 同ページで使用するロジック(画面やステートに依存しないもの) ※新設
│ │ 1ファイルだとデカくなる可能性あるためディレクトリ
│ ├── [LogicFileName].test.ts
│ └── [LogicFileName].ts
│
├── contexts # グローバルでReactContextディレクトリ
│ └── [ContextsName]
│ ├── index.tx # エントリポイント
│ ├── context.tx # コンテキスト管理
│ └── hooks # 状態管理用ディレクトリ
│ └── [StateName].ts
│
│── utils # グローバルで使用する便利系関数ディレクトリ
│ ├── [UtilName]
│ │ ├── index.test.ts
│ │ └── index.ts1
│ └── index.ts
│
├── pages # ページコンポーネントディレクトリ
// etc...
_pages/, pages ディレクトリ
ページディレクトリ配下にはページごとに使用する、Hook関数、型情報、定数、ロジックを配置します。変更前はページにもよりますが、UIページにロジックとUIが混在しており、肥大化していました。UIやHookを使用しないロジックは、utilsディレクトリに配置することで、UIやHookに依存しないロジックを分離し、単体テストを記載しやすいようにしました。
components/ ディレクトリ
共通コンポーネントを配置するディレクトリです。ドメインに依存しないコンポーネント(ボタンやテキストエリアなど)を配置します。変更前と変わりません。
constants/ ディレクトリ
定数を配置するディレクトリです。変更前と変わりません。
containers/ ディレクトリ
ページコンポーネントとは異なり、複数ページにまたがるロジックを配置するディレクトリです。ページコンポーネントと同様に、Hook関数、型情報、定数、ロジックを配置します。こちらもUIやHookを使用しないロジックは、utilsディレクトリに配置することで、UIやHookに依存しないロジックを分離し、単体テストを記載しやすいようにしました。
contexts/ ディレクトリ
グローバルで使用するReactContextを配置するディレクトリです。
utils/ ディレクトリ
グローバルで使用する便利系関数を配置するディレクトリです。変更前と変わりません。
ディレクトリ変更の効果
ディレクトリ構成を変更したことで、以下の効果がありました。
-
プロダクト開発を行いながら、テストコードを追加が行えた
当初の目的の通り、大幅な構成の変更をしなかった結果、ファイル分割とテストコードの追加を行いつつ、プロダクト開発を並行して行うことができました。 -
開発する際に、ファイルを作成するしないを悩む回数が減った
FEは開発初期からアサインされているメンバーがいません。それ故に開発当初の思想が浸透していない状態で開発を行っていました。ディレクトリ構成がどうあるべきかを明文化することで、目指すべき方向性が明確になり、ソースコードの追加や修正時に悩む回数が減りました。
まとめ
現場のリアルなディレクトリ構成の話でしたが、少しでも参考になれば幸いです。
Discussion