• #nuxt
  • #cloudflare
  • #architecture
未分類

Nuxt 3のapp/datapublicディレクトリの使い分け

特にCloudflare Pagesにデプロイする場合のNuxt 3プロジェクトで、データファイルをどこに配置すべきかについて整理する。

結論(TL;DR)

配置場所用途ビルド時の扱い
app/data/TypeScript/JavaScriptでインポートするデータバンドルに含まれる
public/fetch/APIで取得する静的ファイルそのまま配信される

現在のapp/data/japanese-quiz/は正しい配置。 TypeScriptファイルとしてインポートしているため、app/data/に置くのが適切。

詳細な比較

app/data/ディレクトリ

apps/web/app/data/
└── japanese-quiz/
    ├── index.ts
    ├── modification-order.ts
    ├── punctuation.ts
    └── particles.ts

特徴:

  • ビルド時にバンドルされる(JavaScriptにコンパイルされる)
  • TypeScriptの型チェックが効く
  • import { data } from '~/data/...' でインポート
  • Tree-shakingにより未使用コードは除外される
  • SSR/SSGでそのまま使える

適しているケース:

  • TypeScriptの型定義を活用したい
  • コード内で直接インポートして使う
  • データ量が比較的小さい(数KB〜数十KB)
  • ビルド時に確定するデータ

例:クイズの問題データ

// app/data/japanese-quiz/modification-order.ts
export const modificationOrderQuestions: QuizQuestion[] = [
  {
    id: 'mod-001',
    category: 'modification-order',
    question: '次の文で修飾語の順序が適切なものを選べ',
    // ...
  }
]

public/ディレクトリ

apps/web/public/
├── data/
│   ├── financial-data-20251202.json
│   └── raw/
│       ├── AAPL.json
│       └── ...
├── images/
└── audio/

特徴:

  • そのまま静的ファイルとして配信される
  • URLで直接アクセス可能(/data/file.json
  • ビルドプロセスを経由しない
  • fetch('/data/file.json') で取得
  • Cloudflare Pagesのエッジキャッシュが効く

適しているケース:

  • ランタイムで動的にfetchしたい
  • データ量が大きい(数百KB〜数MB)
  • 画像、音声、PDFなどのバイナリファイル
  • 外部ツールから生成・更新されるファイル
  • クライアントサイドでのみ使用するデータ

例:財務データJSON

// コンポーネント内
const { data } = await useFetch('/data/financial-data-20251202.json')

Cloudflare Pagesでの動作

ビルド出力の構造

.output/
├── public/           # 静的ファイル(CDNから配信)
│   ├── data/         # public/からコピー
│   ├── images/
│   └── _nuxt/        # バンドルされたJS/CSS
└── server/           # サーバーサイドコード
    └── chunks/       # app/data/はここに含まれる

パフォーマンスの違い

項目app/data/public/
初回ロードJSバンドルに含まれる別リクエスト
キャッシュJSと一緒にキャッシュ個別にCDNキャッシュ
更新時再ビルド必要ファイル置換のみ可
SSRサーバーで直接利用fetchが必要

判断フローチャート

データを配置したい
    │
    ├─ TypeScriptで型チェックしたい?
    │   └─ Yes → app/data/
    │
    ├─ import文で直接インポートする?
    │   └─ Yes → app/data/
    │
    ├─ ファイルサイズが大きい(100KB以上)?
    │   └─ Yes → public/
    │
    ├─ ランタイムで動的に取得する?
    │   └─ Yes → public/
    │
    ├─ 外部ツールで生成・更新される?
    │   └─ Yes → public/
    │
    └─ バイナリファイル(画像/音声/PDF)?
        └─ Yes → public/

このプロジェクトでの実例

app/data/に置いているもの

パス理由
app/data/japanese-quiz/*.tsTypeScriptでインポート、型チェック活用

public/に置いているもの

パス理由
public/data/raw/*.json財務データ(大量、fetchで取得)
public/images/画像ファイル
public/audio/音声ファイル
public/excel/Excelファイル

まとめ

  • app/data/: コードの一部として扱いたいデータ(TypeScript推奨)
  • public/: 静的ファイルとして配信したいデータ(JSON、画像、音声など)

TypeScriptファイルとしてインポートしており型安全性も確保されている現在のapp/data/japanese-quiz/は、この配置で正しい。public/に移動する必要はない。