src はプロジェクトのメインのコード置き場である
srcディレクトリは自分たちで書くコードを置く場所です。ほとんどの場合はsrcに主要なコードが格納されていますが、srcを作成せずにプロジェクトルートにcomponentsやpages、その他のディレクトリを配置する場合もあります。
my-app/
└── src/
├── components/ # 再利用可能なUIコンポーネント
│ ├── common/ # 共通コンポーネント(例: Button、Inputなど)
│ └── layout/ # レイアウト関連のコンポーネント(例: ヘッダー、フッター)
├── features/ # 機能ごとのディレクトリ
│ └── featureName/
│ └── (後述) # このディレクトリは後述します
├── pages/ # ページコンポーネント(ルーティングに対応)
├── (後述のため省略)
└── (後述のため省略)
ディレクトリの種類が多いため、重要なものと相対的に重要度が下がるもののメリハリをつけて解説します。
ルーティングに対応したコンポーネントを配置するpagesディレクトリ
pagesには、ルーティングに対応したコンポーネントを配置します。ここに配置するコンポーネントは各ページで実行される重要なコンポーネントであるため、他と区別してページコンポーネントと呼ばれることがあります。
Next.jsやRemixといったフレームワークはファイルベースルーティングを採用しています。ファイルベースルーティングとは、そのディレクトリがURLのパスとなり、そこに配置したコンポーネントがページ用のコンポーネントとして描画される仕組みです。
例えば、src/pages/foo/bar/index.tsxというファイルがあれば、/foo/barというパスにアクセスしたとき、このindex.tsxが描画されるというものです。この場合、パスとディレクトリが対応しているため、目当てのコンポーネントが探しやすいです。
ファイルベースルーティングに対応していない場合、どのURLでどのコンポーネントが描画されるかというルーティングの設定のコードを別で記述します。その場合はroutesディレクトリを調べると良いでしょう。
なお、pagesディレクトリのコンポーネントは、次のcomponentsやfeaturesといった別のディレクトリのコンポーネントに依存します。その逆はありません。レイヤードアーキテクチャのように、依存の方向が一方向であることを念頭に置けば、ディレクトリ構成への理解が深まるでしょう。
共通コンポーネントのcomponentsディレクトリとプロダクトに即したfeaturesディレクトリ
componentsは、ButtonやText, Input, Cardといったどこからでも使える基礎的な共通コンポーネントを配置します。HeaderやFooterといったページ間で共通のレイアウト用のコンポーネントもここに配置されることがあります。
一方、featuresディレクトリはプロダクトに特化したコンポーネントを配置する場所です。これらは共通コンポーネントにプロダクト特有のコンテキストを入れ込んだコンポーネントであるため、コンポーネントの名前にアプリケーションの特徴が表れることがあります。
例えば、ブログアプリケーションを構築する場合、記事を表示するためのArticleコンポーネントや一覧表示のためのArticleList、記事を投稿するためのArticleFormといったコンポーネントをここに配置します。
もしfeaturesディレクトリが見当たらない場合は、単にcomponentsがfeaturesの代わりになることがあります。特にプロダクト独自のUIライブラリがある場合や、基礎的なコンポーネントの役割をRadix UIやshadcn/ui、Material UIといった外部ライブラリに任せる場合、共通コンポーネントがsrc外にあるためcomponentsディレクトリを作成する必要がないからです。
名前だけで考えるとややこしいため、コンポーネントには3種類あるのだと考えるとよいでしょう。1つ目は基盤となる共通コンポーネント、2つ目はそれを活用したプロダクト用コンポーネント[2]、3つ目は共通コンポーネントとプロダクト用コンポーネントを組み合わせて構築したページ用のコンポーネントです。
featuresディレクトリの中にはコンポーネントに関連するファイルのみが配置される
では、featuresディレクトリの中身を見てみましょう。以下では先ほど例示したArticleコンポーネントを想定しています。
src/features/article ├── __tests__ │ ├─ article.test.tsx # コンポーネントのテスト │ └─ article.hooks.test.ts # hooksのユニットテスト ├── index.ts # コンポーネントをexportするファイル ├── article.tsx # 主要なコンポーネント ├── article.stories.ts # Storybook用のコンポーネント ├── article.hooks.ts # コンポーネントの中から呼ばれるhooks └── style.module.css # (CSS Modulesを採用している場合)コンポーネント用のCSS
featuresディレクトリ配下のファイル構成の特徴は、そのコンポーネントに関係するファイルしか配置しないことです。各ファイルを大まかに説明します。
__tests__はコンポーネントのテストとReact Hooksのテストを配置しています。コンポーネントに関係するユニットテスト、コンポーネントテスト、ビジュアルリグレッションテスト(VRT)が配置されることもあります。
index.tsはコンポーネントをexportするためのファイルです。article.stories.tsはコンポーネントのカタログであるStorybook用のファイルです(Storybookの説明は割愛)。article.tsxはこのディレクトリの主役であるコンポーネントです。
article.hooks.tsにはArticleコンポーネントでしか使わないReact Hooksを置きます。単にhooks.tsとだけ書く場合もあります。style.module.cssはCSS Modulesを採用している場合、Articleコンポーネントでのみ利用するCSSを配置します。なお、ユーティリティクラスを使ったTailwind CSS を採用している場合、コンポーネントにクラス名を付与するだけでスタイリングができるためstyle.module.cssは不要です。
グローバルなロジックはsrcディレクトリ配下に配置する
my-app/
└── src/
├── (省略)
├── hooks/ # グローバルに使用するカスタムフック
├── libs/ # 外部ライブラリを呼び出すコード(DBやUAの判定処理など)
├── routes/ # ルーティング設定
├── services/ # APIクライアントなど
├── store/ # (もしあれば)状態管理(Reduxなど)
├── styles/ # グローバルスタイルやテーマ
├── types/ # グローバルな型定義
└── utils/ # グローバルなユーティリティ関数。commonと名付けられられることも
srcディレクトリ配下にはコンポーネントの他にグローバルなロジックやTypeScript、CSSに関するファイルを配置します。上から順にサッと見ていきましょう。
hooksディレクトリにはどのコンポーネントからでも使えるグローバルなReact Hooksを配置します。libsはLibrariesの略で、例えばUser Agentの判定ライブラリやGoogle Analytics関連のコード、サーバーで動くコードがある場合データベースへの接続をするライブラリをラップするコードが置かれます。
routesはURLのパスとページコンポーネントを対応させるルーティングの設定です。なお、フレームワークのRemixはapp/routesにファイルベースルーティングを設定しており、この説明の例外となるためご注意ください。
servicesはAPIリクエストを送るために作られるAPIクライアントのラッパーです。GETリクエストを送るだけの薄いラッパーや、POSTリクエストを送るためのデータ整形ロジックを書いたコードが書かれます。servicesという名前から役割が連想しづらいため、他の名前が採用されることもあります。
storesはReduxといったグローバルな状態管理をするStoreを採用している場合に作られるディレクトリです。stylesはfeatures配下のコンポーネント用のCSS Modulesのスタイルとは違って、全体で共通のスタイルが置かれます。typesも同様にプロジェクト全体で使う型を置きます。utilsはどこでも使える共通のロジック置き場です。単にcommonと名付けられることもあります。utilsはバックエンドでも採用されることが多いためわかりやすいはずです。なお、他にtestsディレクトリが設けられ、そこでE2Eテストが置かれていることもあります。
これでsrc内のディレクトリ構成を一通り眺めることができました。お疲れ様でした。
最後に
本記事の冒頭で「ディレクトリ構成を学ぶとフロントエンドの関心事がわかる」と書きました。それはつまり以下の通りです。
まず、コンポーネントは3層あることがcomponents, features, pagesディレクトリに表現されています。次にロジックに関して、Reactコンポーネントでしか使えないロジックはhooks、どこでも使えるJavaScript/TypeScriptのロジックはutils、外部ライブラリを使ったコードはlibs、APIをコールする処理はservicesに置かれます。
また、クライアントサイドでのルーティングの設定はroutes、グローバルな状態管理、CSS、TypeScriptの型ファイルはそれぞれstores, styles, typesに配置します。
つまり、ディレクトリ構成はモダンなフロントエンドのアプリケーションが行っている仕事を分類し、表現したものなのです。この点を理解すると「ちょっとこの画面のここの改修をお願い」と言われたあなたも、どこをどう辿ればいいのか道筋が見えてくるのではないでしょうか。
さて、全4回に渡る連載も本記事で最終回です。バックエンドエンジニアのみなさまが、実際にフロントエンドのコードを書く一助になったでしょうか。もしそうではなくても、フロントエンドエンジニアの考え方に対する理解が深まり、少しでもコミュニケーションが円滑になれば幸いです。
[2]デザイナーのBrad Frost氏は1つ目と2つ目のコンポーネントをそれぞれ「Design System Components」と「Recipes」という名前で、端的にまとめた記事を発表しています。これらの呼び方は現場では使われませんが、考え方はとても参考になるので興味のある方はぜひ記事を以下よりご覧ください。
