はじめに
ディレクトリのベストプラクティスを知らずに、適当に開発してきたため、今一度ディレクトリ構成を見直すことにしました。
今までの記事
環境構築編
https://qiita.com/naoyuki2/items/e974c630c6cbd3c55254
【01】Next.js14でTodoアプリ作成(CREATE編)
https://qiita.com/naoyuki2/items/7474d448de7769905d82
【02】Next.js14でTodoアプリ作成(SELECT編)
https://qiita.com/naoyuki2/items/2d8ad09f767f818e8860
【03】Next.js14でTodoアプリ作成(INSERT編)
https://qiita.com/naoyuki2/items/27d550ab3766a1869aaf
【04】Next.js14でTodoアプリ作成(DELETE編)
https://qiita.com/naoyuki2/items/8fcb8ae5583a1aef8a2d
【05】Next.js14でTodoアプリ作成(UPDATE編)
今回のゴール
色々な記事を読んだ結果私は以下のようなディレクトリ構成にしました。
.
├── components/
│ ├── elements/
│ │ ├── Button.tsx
│ │ └── Input.tsx
│ └── layouts/
│ ├── Header.tsx
│ └── Footer.tsx
├── features/
│ └── todo/
│ ├── api/
│ │ ├── getTodo.ts
│ │ ├── postTodo.ts
│ │ ├── deleteTodo.ts
│ │ ├── patchTodo.ts
│ │ └── Index.ts
│ └── components/
│ ├── TodoInput/
│ │ └── TodoInput.tsx
│ ├── TodoList/
│ │ └── TodoList.tsx
│ ├── TodoItem/
│ │ └── TodoItem.tsx
│ └── Index.ts
└── src/app/
├── api/
│ ├── lib/
│ │ └── prisma.ts
│ └── todo/
│ ├── [id]/
│ │ └── route.ts
│ └── route.ts
├── favicon.ico
├── global.css
├── layout.tsx
└── page.tsx
1.componentsディレクトリ
├── components/
│ ├── elements/
│ │ ├── Button.tsx
│ │ └── Input.tsx
│ └── layouts/
│ ├── Header.tsx
│ └── Footer.tsx
ルートディレクトリ直下に作成した/components
では、アプリケーション全体で使用されるコンポーネントを配置しています。
このレイヤーでは、副作用を持たずに親から受け取ったpropsのみを表示することに特化しています。
状態の受け取りに対して、条件分岐やクラスの付与などを行うことは問題ありません。
/components/elements
├── elements/
│ ├── Button.tsx
│ └── Input.tsx
ここでは、ページ全体で使用する小さい部品、いわゆるButton
やInput
などを格納するディレクトリです。
/components/layouts
└── layouts/
├── Header.tsx
└── Footer.tsx
ここでは、ページ全体で共通するレイアウト、いわゆるHeader
やFooter
を格納するディレクトリです。
2.featuresディレクトリ
├── features/
│ └── todo/
│ ├── api/
│ │ ├── getTodo.ts
│ │ ├── postTodo.ts
│ │ ├── deleteTodo.ts
│ │ ├── patchTodo.ts
│ │ └── Index.ts
│ └── components/
│ ├── TodoInput/
│ │ └── TodoInput.tsx
│ ├── TodoList/
│ │ └── TodoList.tsx
│ ├── TodoItem/
│ │ └── TodoItem.tsx
│ └── Index.ts
features
は特定の機能をもっているコンポーネントをまとめるためのディレクトリです。
例に挙げている、TodoInputなどは、アプリケーション全体で再利用できるとは言えず、/components
にいれず、features
を用意しています。
features
は日本語で特徴
という意味なので、特徴あるコンポーネントを格納することから名付けけられたのではないでしょうか。
/features/todo/api
api/
│── getTodo.ts
│── postTodo.ts
│── deleteTodo.ts
│── patchTodo.ts
│── Index.ts
ここでは、todo
に関するAPI
をたたくための関数を格納しています。
前回の記事のpage.tsx
を見てもらえればわかると思いますが、API
をたたくだけでもかなり記述量が増えてしまっていたためこのようにしました。
Index.ts
については後程解説します。
/features/todo/components
└── components/
├── TodoInput/
│ └── TodoInput.tsx
├── TodoList/
│ └── TodoList.tsx
├── TodoItem/
│ └── TodoItem.tsx
└── Index.ts
ここでは、todo
に関するコンポーネント
を格納しています。
コンポーネントごとにフォルダを用意しているのは、その中に.styled.tsx
や.test.tsx
やstories.tsx
などを入れる可能性があるからだそうです。
私はテストもストーリーブックも分かりません。
ここでもIndex.ts
がでてきます。次で解説します。
Index.ts
おまちかねIndex.ts
の解説です。
意図としては、下記の記事のお言葉をお借りして説明させていただきます。
※products
はtodo
で置き換えてください。
● このファイルは、products関連のコンポーネントを一括でインポートするためのエントリーポイントとなります。
● コンポーネントのインポートパスを短くすることで、コードの可読性を向上させます。
● 他のファイルからこのエントリーポイントを通じてproductsコンポーネントにアクセスすることができます。
● 新しいproductsコンポーネントが追加された場合でも、このファイルの変更のみで済みます。
コード例を紹介すると以下の通りです。
import TodoInput from './TodoInput/TodoInput'
import TodoList from './TodoList/TodoList'
import TodoItem from './TodoItem/TodoItem'
export { TodoInput, TodoList, TodoItem }
import * as Todo from '@features/todo/components/Index'
export default function Home() {
return (
<>
<Todo.TodoInput />
<Todo.TodoList />
</>
)
}
Index.ts
でまとめてエクスポートしています。
そして、使用するページで* as Todo
とすることで、Todo.TodoInput
という風に使用します。
import
するデータ量は増えてしまいますが、かなりわかりやすくなりますし、import
文は一つで済みます。
API
をたたく関数もIndex.ts
を使用してimport
しています。
3.src/appディレクトリ
└── src/app/
├── api/
│ ├── lib/
│ │ └── prisma.ts
│ └── todo/
│ ├── [id]/
│ │ └── route.ts
│ └── route.ts
├── favicon.ico
├── global.css
├── layout.tsx
└── page.tsx
私はプロジェクトを作る際にsrc
フォルダの有無を有りにしたので、src
フォルダがありますが、別にいらないような気がします。
ここはそんなに解説することはないですね。
おわりに
このディレクトリ構成が一番!なんてものはないと思いますが、私は個人開発ではこのディレクトリ構成で開発を進めようと思います。
次回以降はまたロジックを実装していきます。
参考文献