Astroのサイトに構造化マークアップ(JSON-LD)を導入する

検索エンジンにサイトの情報を正確に伝え、検索結果での見栄えを良くするために、構造化マークアップを追加します。

ここでは、JSON-LD形式を使って、サイト共通の情報とページ固有の情報を設定する手順を解説します。

**前提:**
* サイト共通のレイアウトコンポーネント（例: `src/layouts/BaseLayout.astro`）を使用している。
* `astro.config.mjs` にサイトの公開URLが `site` オプションとして設定されている。

## 構造化マークアップを導入する手順

### 1.サイト共通の構造化データを追加する (`BaseLayout.astro`)

まず、サイト全体に関する基本的な情報（サイト名、URLなど）を定義する構造化データを、共通レイアウトファイルに追加します。

`src/layouts/BaseLayout.astro` ファイルを開き、`<head>` タグの中に `<script type="application/ld+json">` タグを追加します。

```astro
---
// src/layouts/BaseLayout.astro
// ... (Props定義やOGP用のURL生成ロジックなど、前の手順で追加したもの) ...

// サイト共通の構造化データを作成
const websiteSchema = {
  "@context": "https://schema.org",
  "@type": "WebSite",
  "name": siteName, // フロントマターで定義したサイト名変数
  "url": siteUrl ? siteUrl.toString() : "/", // Astro.site から取得
  // 必要に応じて "description" なども追加
};
---
<html lang="ja">
<head>
  {/* ... (charset, viewport, OGP/Twitterメタタグなど) ... */}
  <title>{title}</title>

  {/* ↓ サイト共通の構造化データを追加 */}
  <script type="application/ld+json" set:html={JSON.stringify(websiteSchema)} />

  {/* ページ固有の構造化データは後でここに追加 */}

</head>
<body>
  <slot />
</body>
</html>
```
フロントマター部分で、`WebSite` タイプに必要な情報をJavaScriptオブジェクト (`websiteSchema`) として定義します。`Astro.site` を使ってサイトURLを設定します。

`<head>` 内に `<script type="application/ld+json">` タグを追加し、`set:html={JSON.stringify(websiteSchema)}` を使ってオブジェクトをJSON文字列に変換して埋め込みます。`JSON.stringify()` を使うことで、JSON形式のエラーを防ぎやすくなります。

### 2.ページ固有の構造化データを追加する（動的設定）

次に、ブログ記事ページなどで、その記事固有の情報（記事タイプ、タイトル、公開日など）を含む構造化データを追加します。

**レイアウト側での準備 (`BaseLayout.astro` または記事用レイアウト):**
フロントマターで、Propsとして受け取ったページ情報を使って、ページ固有のJSON-LDデータオブジェクト（例: `BlogPosting`）を生成するロジックを追加します。OGP設定で使ったProps (`title`, `description`, `pubDate`, `ogImage` など) を流用できます。

```typescript
---
// src/layouts/BaseLayout.astro (続き)
// ... (Props定義, websiteSchema 定義など) ...

// ページ固有の構造化データを生成 (例: ブログ記事の場合)
let pageSchema = null;
if (ogType === 'article' && Astro.props.pubDate) { // ogTypeが'article'で公開日がある場合など
  pageSchema = {
    "@context": "https://schema.org",
    "@type": "BlogPosting", // または Article など
    "headline": title,
    "description": description, // OGPと同じ説明文を使う場合
    "image": absoluteOgImageUrl, // OGPと同じ画像URLを使う場合
    "url": pageUrl, // 現在ページの絶対URL
    "datePublished": new Date(Astro.props.pubDate).toISOString(), // 日付をISO形式に
    // 必要に応じて "author", "publisher" なども追加
    "author": { // 例
      "@type": "Person", // または Organization
      "name": "あなたの名前または組織名"
    },
    "publisher": { // 例
      "@type": "Organization",
      "name": siteName,
      "logo": {
        "@type": "ImageObject",
        "url": siteUrl ? new URL('/logo-for-schema.png', siteUrl).toString() : '/logo-for-schema.png' // ロゴ画像URL (public/ に配置)
      }
    }
  };
}
---
```
* `ogType` プロパティなどを使って、ブログ記事ページかどうかを判定し、`BlogPosting` (または `Article`) のスキーマデータを生成します。
* 日付は `toISOString()` で標準形式にするのが一般的です。
* `author` や `publisher` など、必要に応じて情報を追加します。ロゴ画像などは `public/` に配置し、絶対URLで指定します。

**レイアウトの `<head>` 内に追加:**

ステップ1で追加した `<script>` タグの **後** に、もう一つ `<script type="application/ld+json">` タグを追加し、ページ固有のスキーマ (`pageSchema`) が存在する場合のみ出力するようにします。

```html
<head>
  {/* ... (charset, viewport, OGP/Twitterメタタグなど) ... */}
  <title>{title}</title>

  {/* サイト共通の構造化データ */}
  <script type="application/ld+json" set:html={JSON.stringify(websiteSchema)} />

  {/* ↓ ページ固有の構造化データを追加 (pageSchemaが存在する場合のみ) */}
  {pageSchema && (
    <script type="application/ld+json" set:html={JSON.stringify(pageSchema)} />
  )}

</head>
```
これで、記事ページなど `pageSchema` が生成されるページでのみ、2つ目の `<script>` タグが出力されます。

* **ページ側でのデータ指定:**
    * **`.astro` ページ:** 前のOGP設定と同様に、レイアウトに渡すPropsに必要な情報を追加します（例: `<BaseLayout ... pubDate={new Date()} ogType="article">`）。
    * **Markdown/MDXページ:** フロントマターに必要な情報（`title`, `description`, `pubDate`, `ogImage`, `ogType: 'article'` など）を記述します。

### 3.開発サーバーでの確認と注意点

* 開発サーバーを起動 (`npm run dev` など) します。
* トップページや記事ページなど、複数のページにアクセスし、ブラウザの開発者ツールで `<head>` 内を確認します。
    * トップページなどでは `WebSite` の `<script>` タグのみが出力されているはずです。
    * 記事ページでは `WebSite` と `BlogPosting` (または `Article`) の **両方の `<script>` タグ** が出力されているはずです。
    * 各 `<script>` タグの中身（JSON）が正しく生成されているか確認します。

Googleの **[リッチリザルト テスト](https://search.google.com/test/rich-results?hl=ja)** や **[スキーマ マークアップ検証ツール](https://validator.schema.org/)** を使うと、生成された構造化データがGoogleに正しく認識されるか、エラーがないかを確認できます（サイト公開後や、ngrokなどで一時的に公開してテストします）。

**注意点:**
* JSONの構文（カンマ、引用符など）が正しいか。`JSON.stringify()` を使えば大抵は大丈夫です。
* 各スキーマタイプ（`WebSite`, `BlogPosting` など）で推奨されるプロパティが設定されているか（Schema.org や Google のドキュメント参照）。
* URLは **絶対URL** になっているか。
* `astro.config.mjs` の `site` 設定が正しいか。

## まとめ
これで、サイト全体とページ固有の両方の情報を検索エンジンに伝えられるようになります。検証ツールを使って