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」という名前で、端的にまとめた記事を発表しています。これらの呼び方は現場では使われませんが、考え方はとても参考になるので興味のある方はぜひ記事を以下よりご覧ください。