Next.js(App Router)でポートフォリオを作るときのフォルダ構成
ポートフォリオは「見せる作品」と「書き足す記事」と「隠したいページ」が同居するサイトです。私が自分のサイトをNext.jsのApp Routerで作ったときのフォルダの分け方と、運用が楽になった判断を書きます。
Next.jsのApp Routerでポートフォリオを作るとき、私が最初に決めるのは 「コード」「データ」「文章」を置く場所を分けること です。ページの見た目より先にこの3つの置き場所を決めておくと、作品の追加や記事の更新がコードに触らずに済むようになり、公開後の運用がかなり軽くなりました。このサイト自体がその構成で動いているので、実物をもとに書きます。
全体像:src/app はURL、それ以外は役割で分ける
App Routerでは、app フォルダの中のフォルダ構成がそのままURLになります。私はプロジェクト直下を散らかさないように、Next.jsが対応している src フォルダ方式を使っています(src Folder)。
| 置き場所 | 中身 | 更新する頻度 |
|---|---|---|
src/app/ |
URLと1対1のページ(/work、/art、/articles など) |
低い |
src/components/ |
ヘッダー、作品カード、問い合わせフォームなどの部品 | 低い |
src/data/ |
作品一覧や経歴などの構造化データ(TypeScriptのファイル) | 中くらい |
content/articles/ |
記事のMarkdown | 高い |
src/lib/ |
Markdownを読む処理、認証の判定など | 低い |
ポイントは右端の列です。更新頻度が高いものほど、コードから遠い場所に置く。 記事はMarkdownを1枚置くだけ、作品はデータファイルに1件足すだけ、という状態にしておくと、デザインやロジックを壊す心配をせずに更新できます。
記事は「置くだけで反映」にする
記事一覧・詳細ページ・サイトマップは、すべて content/articles/ のMarkdownを読み込む1つの関数から作っています。詳細ページは /articles/[slug] という動的ルートにして、generateStaticParams でビルド時に全記事のページを生成しています。公式ドキュメントでも、この関数は動的ルートと組み合わせて、リクエスト時ではなくビルド時に静的生成するためのものと説明されています(generateStaticParams)。
サイトマップも app/sitemap.ts という特別なファイルに書けば、Next.jsが sitemap.xml として配信してくれます(sitemap.xml)。記事の一覧取得を同じ関数にしておけば、記事を足すたびにサイトマップを手で直す作業がなくなります。OGP画像も opengraph-image.tsx というファイル規約で生成できるので(opengraph-image)、SNSで共有されたときの見え方もコード側で揃えられます。
見せたくないページは proxy.ts でまとめて守る
ポートフォリオには、守秘義務があって一般公開できない実績もあります。私のサイトでは /work 以下をパスワードで保護し、/art は公開にしています。
この判定は、ページごとに書かずに proxy.ts 1か所にまとめました。Next.js 16では、従来の middleware.ts が proxy.ts に置き換わり、ファイル名と関数名を変えるだけでロジックはそのままでよい、と公式ブログで案内されています(Next.js 16)。保護するパスを matcher に列挙しておくと、「どのURLが非公開か」がこのファイルを見るだけでわかります。ディレクターとして引き継ぎを考えても、ここが1か所にあることは大きいと感じています。
なお、同じ公式ブログでは、ページが受け取る params を非同期で扱う(await params と書く)形への変更も挙げられています。古い記事のコードをそのまま写すとここでつまずきやすいので、バージョンを確認してから参考にするのがおすすめです。
装飾は部品の中に閉じ込める
ポートフォリオは表現の場でもあるので、WebGLやスクロール演出を入れたくなります。私はこうした装飾を components の中の独立した部品にして、ページ側からは「置くだけ」にしています。演出を外したくなったときに1行消せば済み、記事ページのように読ませたい場所では最初から読み込まない、という判断もしやすくなります。
明日から試せる一手
いま作っている(または作りたい)ポートフォリオについて、「月に1回以上更新するもの」を紙に書き出してみてください。作品、記事、経歴、お知らせなど。そのそれぞれに「どのファイルを1つ触れば更新できるか」を書き添えられれば、構成はほぼ決まっています。書けないものがあれば、そこがデータやMarkdownに切り出す候補です。