
AI Assisted
この記事は筆者の実装経験をもとに実装コードをベースで執筆し、AIによる校閲・推敲を経て公開しています。
自身のブログメディア(本サイト)を運用するにあたり、SEO対策として内部構造の最適化を行いました。 結果、PageSpeed InsightsのSEOスコアで100点を安定して叩き出し、検索流入の基盤を固めることができました。
今回は、その中でも特にエンジニアが意識すべき**「構造化マークアップ(JSON-LD)」**の実装について解説します。 フレームワークに関わらず、Webサイトをシステム運用する上での「実装のお手本」として参考にしてください。
SEO(Search Engine Optimization)というと、「キーワードをたくさん埋め込むこと」や「被リンクを増やすこと」だと思われがちです。しかし、本質はそこではありません。
SEOの本質は、Googleの検索エンジン(クローラー)に対して、Webサイトの内容を「正しく」「わかりやすく」伝えることです。
人間は、ブラウザに表示された綺麗なデザインや文字の大きさを見て、「これは記事のタイトルだ」「これは重要な画像だ」と直感的に理解できます。 しかし、Googleのロボットはあくまで「HTMLコード」を見て判断しています。デザインされた見た目は見えていません。
この「人間が見ている情報」と「ロボットが見ているコード」の認識のズレをなくす作業こそが、テクニカルSEOの第一歩です。
その認識のズレを埋めるための「共通言語」が構造化データです。
HTMLの中に、Schema.orgという規格に則ったJSON-LD形式のデータを埋め込むことで、Googleに対して以下のように具体的に説明できるようになります。
これを正しく実装することで、Googleはページの内容を深く理解し、検索結果に画像付きで表示したり(リッチリザルト)、正しい評価を与えたりしてくれるようになります。
ブログやメディアサイトで実装すべき主要なスキーマ(データの定義)は以下の4つです。
| スキーマ | 用途 | 配置場所 |
|---|---|---|
| Organization | サイト運営者情報 | TOPページ |
| WebSite | サイト全体の情報・検索機能 | TOPページ |
| NewsArticle | 記事そのものの情報 | 記事詳細ページ |
| BreadcrumbList | サイト内の階層構造 | 全ページ |
ここからは、具体的な実装コード(React/Next.jsなどのコンポーネント指向を想定)を見ていきます。
まずは、どのページでもJSON-LDを流し込める「受け皿」となるコンポーネントを用意します。
Next.jsではScriptコンポーネントを使うことで、適切なタイミングでの読み込みを制御できます。
// StructuredData.tsx
import Script from 'next/script'
type Props = {
// 単体または複数のデータを受け取れるように配列対応
data: object | object[]
}
export default function StructuredData({ data }: Props) {
const jsonLd = Array.isArray(data) ? data : [data]
return (
<>
{jsonLd.map((item, index) => (
<Script
key={index}
id={`structured-data-${index}`}
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(item) }}
strategy="beforeInteractive"
/>
))}
</>
)
}
サイト運営者(組織)の情報を定義します。 TOPページに配置することで、Googleに「このサイトは誰が運営しているのか」を明確に伝えられます。
export function OrganizationStructuredData() {
const baseUrl = 'https://nero15.dev'
const structuredData = {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'nero15.dev',
url: baseUrl,
logo: `${baseUrl}/logo.png`,
description: 'インテルミラノの最新ニュースと開発・AI関連情報を発信するブログ',
sameAs: [
'https://x.com/nero15_dev'
],
contactPoint: {
'@type': 'ContactPoint',
contactType: 'Customer Service',
email: 'contact@nero15.dev',
},
}
return <StructuredData data={structuredData} />
}
ポイント:
sameAsにSNSアカウントを記載することで、Google Knowledge Panelへの表示に繋がる可能性がありますcontactPointで問い合わせ先を明示し、信頼性を向上させますサイト全体の情報と、サイト内検索機能を定義します。 これにより、Google検索結果にサイト内検索ボックス(Sitelinks Search Box)が表示される可能性があります。
export function WebSiteStructuredData() {
const baseUrl = 'https://nero15.dev'
const structuredData = {
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'nero15.dev',
url: baseUrl,
description: 'インテルミラノの最新ニュースと開発・AI関連情報を発信するブログ',
inLanguage: 'ja-JP',
potentialAction: {
'@type': 'SearchAction',
target: {
'@type': 'EntryPoint',
urlTemplate: `${baseUrl}/posts?search={search_term_string}`,
},
'query-input': 'required name=search_term_string',
},
}
return <StructuredData data={structuredData} />
}
ポイント:
potentialActionでサイト内検索のURLテンプレートを定義しますinLanguageで主要言語を明示することで、適切な地域へのインデックスに寄与します記事詳細ページにおいて、DBやCMSから取得したデータを以下のフォーマットにマッピングします。
export function NewsArticleStructuredData({ post }: { post: Post }) {
const baseUrl = 'https://nero15.dev'
const structuredData = {
'@context': 'https://schema.org',
'@type': 'NewsArticle', // ブログの場合は 'BlogPosting' でも可
headline: post.title, // 記事タイトル
description: post.excerpt || post.body.substring(0, 160),
image: [post.heroImage || `${baseUrl}/og-image.png`],
datePublished: new Date(post.publishedAt).toISOString(),
dateModified: new Date(post.updatedAt).toISOString(),
author: {
'@type': 'Person',
name: 'nero15',
url: `${baseUrl}/developer`,
},
publisher: {
'@type': 'Organization',
name: 'nero15.dev',
logo: {
'@type': 'ImageObject',
url: `${baseUrl}/logo.png`,
},
},
mainEntityOfPage: {
'@type': 'WebPage',
'@id': `${baseUrl}/posts/${post.id}`,
},
articleSection: post.category?.name || 'ニュース',
keywords: post.tags?.join(', ') || '',
inLanguage: 'ja-JP',
// 翻訳記事の場合、元ソースを明示
...(post.sourceUrl && {
isBasedOn: post.sourceUrl,
citation: post.sourceUrl,
}),
}
return <StructuredData data={structuredData} />
}
ポイント:
publisher(運営者情報)や author(著者情報)を明記することで、近年Googleが重視するE-E-A-T(経験・専門性・権威性・信頼性)の評価に繋がりますarticleSectionでカテゴリを、keywordsでタグを明示しますisBasedOnとcitationで元ソースを記載し、透明性を確保しますサイトの階層構造を定義します。検索結果のURL欄に nero15.dev > カテゴリ > 記事 のように表示され、クリック率(CTR)の向上に寄与します。
export function BreadcrumbListStructuredData({
items
}: {
items: Array<{ name: string; url?: string }>
}) {
const baseUrl = 'https://nero15.dev'
const structuredData = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: items.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.name,
// 現在のページ(最終階層)には item (URL) を含めないのがベストプラクティス
...(item.url && {
item: item.url.startsWith('http') ? item.url : `${baseUrl}${item.url}`
}),
})),
}
return <StructuredData data={structuredData} />
}
使用例:
// 記事詳細ページでの使用
<BreadcrumbListStructuredData
items={[
{ name: 'ホーム', url: '/' },
{ name: 'セリエA', url: '/category/serie-a' },
{ name: '記事タイトル' }, // 最終階層はURLなし
]}
/>
コードを実装したら、必ずGoogle公式のツールでテストを行います。 自己満足で終わらせず、Googleに正しく伝わっているかを確認・検証するところまでが実装です。
| 確認項目 | 状態 |
|---|---|
| TOPページに Organization を配置 | |
| TOPページに WebSite を配置 | |
| 記事ページに NewsArticle を配置 | |
| 全ページに BreadcrumbList を配置 | |
| 画像URLが絶対パス(https://)になっている | |
| datePublished/dateModified が ISO 8601 形式 | |
| リッチリザルトテストでエラーなし |
SEOには「コンテンツの質」という正解のない領域と、「技術的な実装」という正解がある領域があります。 今回紹介した構造化データは後者です。
システム側でこのテンプレートさえ正しく用意しておけば、あとはどれだけ記事を量産しても、自動的にGoogleへ「正しい情報」として翻訳され、伝わっていきます。 Webメディアを開発・運用する際は、ぜひこの実装を標準装備してください。
スポーツ×ITの会社でバックエンドエンジニア兼マネージャーとして勤務。インテル関連の情報を中心に、AI・IT技術やサイト運用ノウハウも発信しています。
最終更新: 2025年12月30日
© 2025 nero15.dev. All rights reserved.