• #architecture
  • #performance
  • #lazy-loading
  • #nuxt
未分類

財務データ全量表示の遅延読み込み設計

概要

proportional-animation-qqq.vueページの「財務データ一覧」セクションに、SQLiteに格納された全メトリクスを表示する機能を追加した際の設計について記録する。

背景と課題

現状のアーキテクチャ

data/koyfin.db (SQLite)
    ↓ [node scripts/generate-financial-data.mjs]
app/composables/financial-data.ts (3.4MB)
    ↓ [import]
app/pages/financial-quiz/proportional-animation-qqq.vue
  • チャート表示に必要なメトリクスのみを TypeScript ファイルに事前生成
  • 初期ロード時にすべてのデータが読み込まれる
  • 問題: DBには約100種類のメトリクスがあるが、チャートでは約30種類のみ使用

要件

  • 「財務データ一覧」で全メトリクスを表示したい
  • しかし、全メトリクスを TypeScript に含めると初期ロードが重くなる(推定 30MB+)

解決策: 遅延読み込み (Lazy Loading)

アーキテクチャ

data/koyfin.db (SQLite)
    ↓ [node scripts/generate-raw-metrics.mjs]
public/data/raw/{TICKER}.json (50-60KB/企業)
    ↓ [fetch() on demand]
app/components/financial-quiz/FinancialDataTable.vue

ポイント:

  1. チャート用データ: 従来通り TypeScript で即時読み込み
  2. 全メトリクス: JSONとして public/data/raw/ に配置し、タブクリック時に fetch

データフロー

┌─────────────────────────────────────────────────────────────┐
│ Initial Load                                                │
├─────────────────────────────────────────────────────────────┤
│  financial-data.ts (3.4MB)                                 │
│    └─ チャート用メトリクス ~30種類                           │
│       → BS/PL/CF図、収益チャート、バリュエーション等         │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│ On "全データ" Tab Click                                     │
├─────────────────────────────────────────────────────────────┤
│  fetch(`/data/raw/${ticker}.json`)                         │
│    └─ 全メトリクス ~100種類 (50-60KB)                       │
│       → セクション別テーブル表示                             │
└─────────────────────────────────────────────────────────────┘

実装詳細

1. データ生成スクリプト

ファイル: scripts/generate-raw-metrics.mjs

// 使用方法
cd apps/web
node scripts/generate-raw-metrics.mjs

// 出力
public/data/raw/{TICKER}.json  // 101企業分

出力JSONの構造:

{
  "ticker": "NVDA",
  "periods": ["2016", "2017", ..., "LTM"],
  "sections": [
    {
      "id": "highlights",
      "label": "ハイライト",
      "metrics": [
        {
          "name": "Diluted EPS",
          "values": {
            "2016": "0.03",
            "2017": "0.06",
            ...
          }
        }
      ]
    },
    {
      "id": "income_statement",
      "label": "損益計算書",
      "metrics": [...]
    }
    // ... 他のセクション
  ]
}

セクション一覧:

ID日本語名
highlightsハイライト
income_statement損益計算書
balance_sheet貸借対照表
cash_flowキャッシュフロー計算書
profitability収益性指標
multiplesマルチプル(株価倍率)
enterprise_value企業価値
solvency安全性指標
roicROIC関連

2. コンポーネント実装

ファイル: components/financial-quiz/FinancialDataTable.vue

<script setup lang="ts">
// タブ状態
const activeTab = ref<'summary' | 'raw'>('summary')

// rawデータの状態
const rawData = ref<RawData | null>(null)
const isLoading = ref(false)
const loadedTicker = ref<string | null>(null)

// 「全データ」タブクリック時に読み込み
async function loadRawData() {
  activeTab.value = 'raw'

  // キャッシュチェック: 同じtickerなら再読み込みしない
  if (loadedTicker.value === props.company.ticker && rawData.value) {
    return
  }

  isLoading.value = true
  try {
    const response = await fetch(`/data/raw/${props.company.ticker}.json`)
    rawData.value = await response.json()
    loadedTicker.value = props.company.ticker
  } catch (e) {
    loadError.value = `データの読み込みに失敗しました`
  } finally {
    isLoading.value = false
  }
}

// 企業切り替え時: rawタブなら自動再読み込み
watch(() => props.company.ticker, () => {
  if (activeTab.value === 'raw') {
    loadRawData()
  }
})
</script>

ファイルサイズ比較

ファイルサイズ読み込みタイミング
financial-data.ts3.4MB初期ロード
raw/{TICKER}.json50-60KBタブクリック時
全rawファイル合計5.4MB-

効果: 初期ロードサイズを増やさずに全メトリクス表示を実現

設計のメリット

  1. 初期ロード維持: チャート表示に必要なデータのみ初期読み込み
  2. オンデマンド読み込み: 全データが必要な場合のみ追加fetch
  3. キャッシュ効率: 同一企業の再読み込みを防止
  4. CDN対応: public/ 配下の静的JSONはCDNキャッシュ可能
  5. デプロイ簡素化: JSONは静的ファイルとしてビルドに含まれる

Cloudflare Pages の容量制限

無料枠での制限と現在の使用状況:

制限項目上限現在の使用状況余裕
1ファイルあたり25 MB50-60 KB十分
ファイル数20,000 ファイル101 ファイル十分
総デプロイサイズ1 GB約 5.4 MB十分
月間デプロイ回数500 回--
帯域幅無制限--

参考: Cloudflare Pages Limits

将来的なスケーリング

  • 1000企業に増えても約 60MB(1GB上限には余裕)
  • ファイル数も 20,000 の上限に対して余裕
  • 25MBを超える大きなファイルが必要な場合は Cloudflare R2 を検討

注意事項

データ更新時の手順

SQLiteデータが更新された場合:

cd apps/web

# 1. チャート用データを再生成
node scripts/generate-financial-data.mjs

# 2. 全メトリクスJSONを再生成
node scripts/generate-raw-metrics.mjs

# 3. ビルド&デプロイ
pnpm build

スクリプトの制限事項

  • generate-raw-metrics.mjs は年次データ (Annual) と LTM のみ出力
  • 四半期データは含まれない(必要であれば別途対応)

関連ファイル

  • scripts/generate-raw-metrics.mjs - JSON生成スクリプト
  • components/financial-quiz/FinancialDataTable.vue - 表示コンポーネント
  • pages/financial-quiz/proportional-animation-qqq.vue - 親ページ
  • composables/financial-data.ts - チャート用データ