# Kuroco Frontend Integration

> KurocoとVite / React / Nuxt.js / Next.jsなどフロントエンドフレームワークの統合パターン（SPA・SSG・SSRの実装）。SPA / SSGでのコンテンツ表示（SSR・ISRが必要な場合の公開先切り替えを含む）、ログイン・会員登録・認証状態管理（Cookie認証・動的アクセストークン）、サードパーティCookie問題の回避、XSS対策、公開先の決定（既定はKurocoFront、Vercel・Codex Sites指定時の確認事項）、KurocoFrontへのデプロイ（kuroco_front.json、GitHub連携、Admin MCP直接デプロイ）をカバー。フロントエンドからのKuroco連携、認証状態の管理、静的生成・動的ルート、デプロイ、公開先の選択の質問で使用。

- Skill: `diverta/kuroco-frontend-integration` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add diverta/kuroco-frontend-integration`
- Raw SKILL.md: https://api.skillmd.com/api/skills/diverta/kuroco-frontend-integration/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: diverta (https://skillmd.com/u/diverta)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/diverta/kuroco-frontend-integration

---


# Kuroco フロントエンド統合パターン

Kuroco HeadlessCMSとVite/Nuxt.js/Next.jsなどのフロントエンドフレームワークの統合パターン、および公開先の決定とデプロイ。

**ドキュメント参照**: `/kuroco-docs` スキルを使用してKuroco公式ドキュメントを検索・参照できます。

**チュートリアル**: フロントエンドのデプロイ手順やサンプルサイトの構築方法は [Kurocoサンプルサイトチュートリアル](https://kuroco.app/ja/docs/tutorials/kuroco-sample-site/) を参照してください。

## 目次

- [公開先の決定](#公開先の決定)
- [サポートフレームワーク](#サポートフレームワーク)
- [環境設定](#環境設定)
- [API設定の前提条件](#api設定の前提条件)
- [認証実装](#認証実装)
- [Nuxt.js統合](#nuxtjs統合) → 詳細は [references/nuxt.md](references/nuxt.md)
- [Next.js統合](#nextjs統合) → 詳細は [references/nextjs.md](references/nextjs.md)
- [KurocoFront統合](#kurocofront統合) → 詳細は [references/kuroco-front.md](references/kuroco-front.md)

## 公開先の決定

**既定はKurocoFront。** ユーザーからの指定がない限り、質問せずKurocoFrontで進める。

| 状況 | 動作 |
|---|---|
| **SSR / ISR が必要** | KurocoFrontは静的コンテンツホスティング（CDN）で**サーバー実行ができない**ため動かない。Vercel等を提案する。ただし提案の前に下の「SSR/ISRが本当に必要か」で切り分ける |
| 新規構築（指定なし） | KurocoFrontで進める。公開先を選ばせる質問はしない |
| ユーザーがVercel / Netlify / Codex Sites / v0 / Lovable などを指定 | その公開先で進める。実装前に [references/other-hosting.md](references/other-hosting.md) の確認軸を埋める。Codex Sitesの場合は下の実行環境の制約を先に伝える |
| Codexで実行中の新規構築 | KurocoFrontを既定としつつ、Codex Sitesという選択肢があることを一度だけ提示してよい。指定が返らなければKurocoFrontで進める |
| ChatGPTの通常チャットで実行中 | Codex Sitesは提示しない（下の実行環境の制約）。KurocoFrontで進める |
| 既存サイトの変更 | 現在の公開先を維持する。明示的な移行依頼があったときだけ選び直す |

### 実行環境の制約（Codex Sites）

**Codex Sites（`chatgpt.site`）は ChatGPT の通常チャットからは操作できない。**
サイトの作成・更新は **Work モード（ChatGPTのワークスペース）または Codex** での実行が前提になる。

通常チャットで Kuroco の構築を進めていて Codex Sites を使いたい場合は、この制約を伝えたうえで
**Work モードか Codex で作業することを推奨**する。切り替えないなら KurocoFront で進める。

### SSR/ISRが本当に必要かの切り分け

「SSR/ISRが要る」という要望の動機はたいていSEOかOGPで、その多くはSSGで足りる。
安易にVercelへ出すと運用先が分散するため、**動機で判定する**。

| 動機 | 結論 |
|---|---|
| 検索流入・各ページのOGP | **SSG（KurocoFront）** で足りる。SPAは各ページ個別のOGPを持てないのでSSGにする |
| ページ数が多くSSGのビルドが現実的に回らない／更新頻度が高くビルド待ちが許容できない | **SSR / ISR → Vercel等**。KurocoFrontでは実現できない |
| 会員制・認証必須の画面 | **SPA（KurocoFront）**。SSR/ISRにしても利点が出ない（下記） |
| 公開ページ（OGP要）と会員ページが混在 | 公開側SSG＋会員側SPAの混在構成。公開先はKurocoFrontのままでよい |

**会員制サイトをSSR/ISRにしない理由**: サーバー側fetchにはブラウザのセッションCookieが乗らないため、
認証付きコンテンツはどのみちクライアント側fetchで取ることになる。SSRにしてもサーバーレンダリングの
利点（初期表示のHTMLに中身が入る・OGP）が認証領域では得られず、構成だけが複雑になる。
会員制は**SPAに寄せて認証をクライアント側に閉じる**方が分離が効く。

フレームワーク・認証・CORS・XSS対策の実装パターンは公開先によらず共通（以下のセクション）。
公開先ごとに違うのは設定と公開手順だけなので、選んだ側のreferenceだけを読む。

- KurocoFront: [references/kuroco-front.md](references/kuroco-front.md)
- それ以外: [references/other-hosting.md](references/other-hosting.md)

## サポートフレームワーク

| フレームワーク | バージョン | 推奨ユースケース | KurocoFrontでの配信 |
|--------------|-----------|----------------|------------------|
| Vite + Vue 3 | Vue 3系 | シンプルなSPA（SEO不要のアプリ・ツール類）。最小構成で認証が素直 | `vite build` → `dist/` をそのまま |
| Nuxt.js 3.x | Vue 3系 | SEOが必要なコンテンツサイト（SSGで静的HTML生成、推奨） | `nuxt generate`（SSG）。`nuxt build` のSSRは不可 |
| Nuxt.js 2.x | Vue 2系 | 既存プロジェクト | `nuxt generate`（SSG） |
| Next.js 13+ | React (App Router) | 新規Reactプロジェクト | `output: 'export'` の静的エクスポートのみ（下記の制約） |
| Next.js (Pages) | React (Pages Router) | 既存Reactプロジェクト | `output: 'export'` の静的エクスポートのみ |

**KurocoFrontは静的コンテンツホスティング（CDN）なので、サーバー実行を伴う構成は動かない。**
SSR / ISR が必要な場合は[公開先の決定](#公開先の決定)へ戻る。

### Next.js を KurocoFront で配信する場合の制約

`next.config.js` に `output: 'export'` を設定し、`out/` を配信する。
Next.js 16 時点で静的エクスポートでは以下が**使えない**（[公式ドキュメント](https://nextjs.org/docs/app/guides/static-exports)）。

| 使えない機能 | Kuroco構成での対処 |
|---|---|
| `cookies()` | 認証はクライアント側で行う。Cookie認証も動的アクセストークンもブラウザから直接Kuroco APIを呼ぶ |
| Server Actions | フォーム送信はクライアントからKuroco APIへ直接POSTする |
| `next.config` の Rewrites / Redirects / Headers | `kuroco_front.json` の `rewrites` / `redirects` で設定する（[references/kuroco-front.md](references/kuroco-front.md)） |
| ISR、Draft Mode、Intercepting Routes、Proxy | 代替なし。必要ならVercel等に公開先を変える |
| Request に依存する Route Handler | 静的化するなら `export const dynamic = 'force-static'`（GETのみ）。動的な読み取りが要るならKuroco API側で処理する |
| `dynamicParams: true` / `generateStaticParams()` なしの動的ルート | ビルド時に `generateStaticParams()` で全パスを列挙する。列挙できない量ならSPAに寄せる |
| `next/image` のデフォルトloader | カスタムloaderを指定するか、Kuroco Filesの画像URLを直接使う |

## 環境設定

### 環境変数

```bash
# .env.local
NUXT_PUBLIC_API_BASE=https://example.g.kuroco.app
NEXT_PUBLIC_API_BASE=https://example.g.kuroco.app
API_ID=1
```

### プロジェクト構成

**Nuxt.js:**
```
pages/
├── news/
│   ├── index.vue      # 一覧
│   └── [slug].vue     # 詳細 (Nuxt3)
├── login.vue
└── profile.vue
composables/
├── useAuth.ts
└── useApi.ts
```

**Next.js (App Router):**
```
app/
├── news/
│   ├── page.tsx       # 一覧
│   └── [slug]/page.tsx
├── login/page.tsx
└── profile/page.tsx
lib/
├── auth.ts
└── api.ts
```

## API設定の前提条件

### 1. セキュリティ設定（Cookie認証）

1. 管理画面 → API → セキュリティ → **Cookie**を選択
2. フロントエンドとAPIドメインをサブドメイン違いに設定
   - 例: `www.example.com` と `api.example.com`

### 2. CORS設定

管理画面: [API] → [セキュリティ] → [CORS設定]

```
CORS_ALLOW_ORIGINS:
  - http://localhost:3000
  - https://your-frontend-domain.com

CORS_ALLOW_CREDENTIALS: true

CORS_ALLOW_METHODS:
  - GET
  - POST
```

## 認証実装

### ログイン

```typescript
interface LoginResponse {
  grant_token: string
  status: number
  member_id: number
}

async function login(email: string, password: string): Promise<LoginResponse> {
  const response = await fetch(
    'https://example.g.kuroco.app/rcms-api/1/login',
    {
      method: 'POST',
      credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password })
    }
  )

  if (!response.ok) {
    const error = await response.json()
    throw new Error(error.errors?.[0]?.message || 'ログインに失敗しました')
  }

  return response.json()
}
```

### ログアウト

```typescript
async function logout(): Promise<void> {
  await fetch('https://example.g.kuroco.app/rcms-api/1/logout', {
    method: 'POST',
    credentials: 'include'
  })
}
```

### ログイン状態の確認

```typescript
async function checkAuth(): Promise<ProfileResponse | null> {
  try {
    const response = await fetch(
      'https://example.g.kuroco.app/rcms-api/1/profile',
      { credentials: 'include' }
    )
    if (!response.ok) return null
    return response.json()
  } catch {
    return null
  }
}
```

### 会員登録

```typescript
async function signup(memberData: SignupData): Promise<void> {
  const response = await fetch(
    'https://example.g.kuroco.app/rcms-api/1/member/insert',
    {
      method: 'POST',
      credentials: 'include',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(memberData)
    }
  )

  if (!response.ok) {
    const error = await response.json()
    throw new Error(error.errors?.[0]?.message || '登録に失敗しました')
  }
}
```

## Nuxt.js統合

**詳細な実装例**: [references/nuxt.md](references/nuxt.md) を参照

クイックスタート（Nuxt 3）:

```typescript
// composables/useKurocoApi.ts
export function useKurocoApi() {
  const config = useRuntimeConfig()

  async function get<T>(endpoint: string, params?: Record<string, any>): Promise<T> {
    const query = params ? `?${new URLSearchParams(params)}` : ''
    return await $fetch<T>(
      `${config.public.apiBase}/rcms-api/${config.public.apiId}/${endpoint}${query}`,
      { credentials: 'include' }
    )
  }

  return { get }
}
```

## Next.js統合

**詳細な実装例**: [references/nextjs.md](references/nextjs.md) を参照

クイックスタート（App Router）:

```typescript
// lib/api.ts
export async function apiGet<T>(endpoint: string): Promise<T> {
  const response = await fetch(
    `${process.env.NEXT_PUBLIC_API_BASE}/rcms-api/1/${endpoint}`,
    { credentials: 'include', cache: 'no-store' }
  )
  if (!response.ok) throw new Error(`API Error: ${response.status}`)
  return response.json()
}
```

## KurocoFront統合

KurocoFrontはKurocoが提供するフロントエンドホスティングサービス（静的ホスティング + CDN）。

**SPA（History APIでのクライアントルーティング）を配信するなら、`kuroco_front.json` の
`rewrites` に `{"source": ".*", "destination": "/index.html"}` が必須**（無いとリロード・URL直打ち・共有リンクが404になる）。
`source` を絞ると壊れる理由・`error_page` との関係を含めた推奨設定は
[references/kuroco-front.md「SPA配信」](references/kuroco-front.md#spa配信履歴apiのクライアントルーティング)にまとめてある。

`kuroco_front.json` の設定（rewrites / redirects / Basic認証 / IPアドレス制限）、
**非公開デフォルト（ユーザーが公開を明示するまで robots.txt の `Disallow: /` ＋ Basic認証/IP制限をかけてデプロイする既定）**、GitHub連携デプロイ、
Admin MCPからの直接デプロイ（`files-create_temp_upload_url` → `kuroco_front-deploy` → `kuroco_front-history`）の
手順と制約は [references/kuroco-front.md](references/kuroco-front.md) を参照。

## 注意事項

### サードパーティCookie問題（SPAのCookie認証）

フロントエンドとAPIが別ドメイン（例: `www.example.com` と `{site_key}.g.kuroco.app`）の場合、
SafariのITP等によりクロスサイトCookieがブロックされ、Cookie認証が動作しません。回避策は2つ:

**回避策1: 同一親ドメインに揃える（Cookie認証を続ける場合）**

APIに独自ドメイン（例: `api.example.com`）を設定し、フロント（`www.example.com`）と親ドメインを揃えるとCookieがファーストパーティ扱いになります。
設定: 管理画面 [独自ドメイン/TLS証明書] でAPIドメインを登録 → [アカウント設定] でAPIベースURLを更新。

> **Safariの7日間Cookie上限に注意**: 親ドメインを揃えても、SafariはCNAMEクローキング対策により
> この構成のCookie有効期限を7日に制限します。毎日使うツールでは実質問題ありませんが、
> ログイン頻度が低いサイトではSafariユーザーが7日で再ログインになります。

**回避策2: 動的アクセストークン認証に切り替える（SPAで確実な方法）**

アクセストークンはCookieではなく `X-RCMS-API-ACCESS-TOKEN` リクエストヘッダーで送るため、
ITP・サードパーティCookie制限の対象外です。長期ログイン保持が必要なSPA・クロスドメイン構成ではこちらを推奨。

1. APIセキュリティを「動的アクセストークン」に設定し、`token` エンドポイント（`Login::token`）を作成
2. ログイン: `login` → `grant_token` 取得 → `token` にPOSTして `access_token`（+ `refresh_token`）取得
3. 以降のリクエストに `X-RCMS-API-ACCESS-TOKEN: {access_token}` ヘッダーを付与
4. 期限切れ時は `refresh_token` を `token` エンドポイントに送って再発行（`access_token_lifespan` / `refresh_token_lifespan` で期間設定）

> トークンをlocalStorageに保存する場合はXSS対策（後述のHTMLサニタイズ等）を徹底すること。

**開発時（localhost）**: 開発サーバーのプロキシ（Viteの `server.proxy` 等）でAPIを同一オリジンに見せると、ブラウザのCookie制限を受けずに開発できます。

### HTMLサニタイズ

`v-html` や `dangerouslySetInnerHTML` を使用する際はXSSに注意:

```typescript
import DOMPurify from 'dompurify'
const sanitizedHtml = DOMPurify.sanitize(htmlContent)
```

## 関連スキル

- `/kuroco-api-content` - API設計・認証パターン、コンテンツCRUD操作
- `/kuroco-admin-mcp` - Admin MCP経由の管理操作

## 関連ドキュメント

- `../kuroco-docs/docs/tutorials-frontend-1.md`（`integrate-kuroco-with-nuxt`） - Nuxt.js統合
- `../kuroco-docs/docs/tutorials-auth-member-2.md`（`integrate-login`） - ログイン実装
- `../kuroco-docs/docs/tutorials-auth-member-4.md`（`signup`） - 会員登録
- `../kuroco-docs/docs/tutorials-misc.md`（`beginners-guide`） - ビギナーズガイド
- `../kuroco-docs/docs/tutorials-frontend-1.md`（`corporate-sample-site-to-ssg`） - SSG対応
- [Kurocoサンプルサイトチュートリアル](https://kuroco.app/ja/docs/tutorials/kuroco-sample-site/) - サンプルサイトの構築・デプロイ手順

