はじめに
今回は、実際にNext.jsでweb制作をした時に遭遇したケースを共有していきます!
今からご説明する方法は以下の場面を想定した際に、使用できると考えてます。
- ポートフォリオ:制作実績一覧
- コーポレートサイト:事例紹介、導入企業一覧
- 採用サイト:社員紹介、インタビュー記事
- サービスサイト:料金プラン、機能一覧
この記事では、Next.jsのweb制作で実績一覧を実装する際にJSON + fsモジュールを用いた方法を紹介していきます!
例として簡易的に社員紹介の一覧画面を作る場面を想定してます。
この記事ではJSONファイルからfsモジュールでデータを取得するところまでを解説します。
この記事で学べること
- fsモジュールとは?
- fsモジュールを使ったJSONファイルの読み込み方
- DB・API不要のJSON管理方法
fsモジュールとは?
簡単に言うと、ファイルの読み書きやフォルダの操作ができるNode.jsの標準機能です。今回はこれを使ってJSONファイルを読み込みます。
The node:fs module enables interacting with the file system in a way modeled on standard POSIX functions.
— Node.js公式ドキュメントより
他のNode.jsのfsモジュールの使い方を知りたい方は以下の記事がおすすめです!
前提条件
- Next.js 15以上(App Router使用)
- TypeScript
- Node.js環境
プロジェクト構成
src/
├── app/
│ └── members/
│ └── page.tsx # メンバー一覧ページ
├── components/
│ └── MemberCard/
│ ├── MemberCard.tsx # メンバーカード
│ └── MemberCard.module.css
├── data/
│ └── members/
│ ├── tanaka.json
│ ├── sato.json
│ └── momo.json # 個別JSONファイル
├── lib/
│ └── getMembers.ts # データ取得関数
└── types/
└── member.ts # 型定義
1. データ構造の設計
型定義を作成
まず、メンバー情報の型を定義します。
export interface Member {
id: string;
name: string;
role: string;
bio: string;
}
個別JSONファイルで管理
各メンバーを個別のJSONファイルとして管理します。
{
"id": "tanaka",
"name": "田中太郎",
"role": "エンジニア",
"bio": "バックエンド開発を担当しています。Go言語とNext.jsが得意です。"
}
{
"id": "sato",
"name": "佐藤花子",
"role": "デザイナー",
"bio": "UI/UXデザインを担当。Figmaを使ったデザインが得意です。"
}
{
"id": "momo",
"name": "桃太郎",
"role": "エンジニア",
"bio": "AIに全て任せます!"
}
ポイント
- 1メンバー = 1JSONファイルでメンテナンスしやすい
- 全てのJSONファイルで同じプロパティを持たせる
2. データ取得関数の実装
Next.jsでJSONからデータを取得して表示するときにfsモジュールを使います。
サーバーコンポーネントで実装するのがセオリーです。この方法を使うことでビルド時にJSONを埋め込むことができ、クライアントからのリクエストを0回にできるためパフォーマンスが向上します。
import fs from 'fs';
import path from 'path';
import { Member } from '@/types/member';
export function getMembers(): Member[] {
// 1. membersフォルダの絶対パスを取得
const membersDir = path.join(process.cwd(), 'src/data/members');
// 2. フォルダ内の全ファイル名を配列で取得
// *ビルド時にローカルファイルを読み込むだけなので、ここでは同期処理のreadFileSyncを使っています。
const files = fs.readdirSync(membersDir);
// → ['tanaka.json', 'sato.json', 'momo.json']
// 3. JSONファイルを処理して配列に変換
const members = files
// .jsonで終わるファイルだけをフィルタリング
.filter(file => file.endsWith('.json'))
// → ['tanaka.json', 'sato.json', 'momo.json']
// 各JSONファイルを読み込んでオブジェクトに変換
.map(file => {
// ファイルの絶対パスを作成
const filePath = path.join(membersDir, file);
// ファイル内容をUTF-8文字列として読み込み
const content = fs.readFileSync(filePath, 'utf-8');
// JSON文字列→JavaScriptオブジェクトに変換
// as Member で型を指定(JSON.parseの戻り値はany型のため)
return JSON.parse(content) as Member;
});
// デバッグ方法
// console.log(members);
return members;
}
上記の関数を実行すると、以下のような配列が返ってきます。
(ブラウザのコンソールで確認した例)
[
0: { id: 'tanaka', name: '田中太郎', role: 'エンジニア', bio: '...' },
1: { id: 'sato', name: '佐藤花子', role: 'デザイナー', bio: '...' },
2: { id: 'momo', name: '桃太郎', role: 'エンジニア', bio: '...' }
]
as Member(型アサーション)について
JSON.parse() の戻り値は any 型なので、TypeScriptに「これは Member 型だよ」と教えてあげる必要があります。これを型アサーションと呼びます。
注意:型アサーションの落とし穴
as Member は実行時チェックを行いません。JSONの構造が型定義と異なっていてもエラーになりません。
今回はシンプルさを優先して型アサーションを使っていますが、データの信頼性が重要な場面では型ガードやZodなどのバリデーションライブラリを検討してください。
おわりに
この方法を使えば、APIサーバーやデータベースを用意しなくても、簡単にデータ一覧を実装できます。
特に以下のようなケースで有効と考えてます。
- 更新頻度が低いデータ(社員紹介、実績一覧など)
- 小〜中規模のサイト
- コストを抑えたい場合
ぜひ試してみてください!