@shinyaz

Next.js ブログに関連記事セクションを追加する

約2分
目次

課題

このブログの記事ページはソーシャルシェアコンポーネントで終わっており、その先に何もなかった。カテゴリやタグで記事を分類しているのに、関連コンテンツへの導線がない。多くの読者が1記事だけ読んで離脱している状態だった。

関連記事を自動的に表示するセクションを追加したい。ただし、過度に複雑にはしたくなかった。

シンプルなスコアリングで十分な理由

記事が数十件のブログで、高度なレコメンドシステムは過剰だ。機械学習、エンべディング、全文類似度検索 — いずれもこの規模ではオーバーエンジニアリングになる。

代わりに、既にあるメタデータ — カテゴリとタグ — を使ったスコアリングを採用した。

  • カテゴリ一致: 重み 2(カテゴリはより広い分類なので、一致の意味が大きい)
  • タグ一致: 重み 1
  • スコア 0: 完全に除外(無関係な記事は表示しない)
  • 同点時: 新しい記事を優先

なぜカテゴリの重みを高くしたか。「programming」のような共通カテゴリは、両記事が同じ大きなドメインにいることを示す。「nextjs」のような共通タグはより具体的だが、人気のタグは多くの記事で共有されるため弁別力が低い。実際に試してみて 2:1 の比率がしっくりきたが、記事数が増えれば調整の余地はある。

スコアリング関数

核となるロジックは src/lib/posts.ts の1関数だ:

src/lib/posts.ts
export function getRelatedPosts(post: Post, limit = 3): Post[] {
  const candidates = getPublishedPosts(post.locale as Locale).filter(
    (p) => p.permalink !== post.permalink
  );
 
  const scored = candidates.map((p) => {
    let score = 0;
    for (const cat of p.categories) {
      if (post.categories.includes(cat)) score += 2;
    }
    for (const tag of p.tags) {
      if (post.tags.includes(tag)) score += 1;
    }
    return { post: p, score };
  });
 
  return scored
    .filter((s) => s.score > 0)
    .sort(
      (a, b) =>
        b.score - a.score ||
        new Date(b.post.date).getTime() - new Date(a.post.date).getTime()
    )
    .slice(0, limit)
    .map((s) => s.post);
}

設計上のポイント:

  • 同ロケール限定: getPublishedPosts(post.locale) で候補を現在の言語に限定。日本語の読者に英語のレコメンドは不要だ。
  • permalink での自己除外: シンプルで確実。
  • スコア 0 のフィルタ: 現在の記事と何も共有していなければ表示しない。関連記事がゼロになっても構わない。

組み込み

RelatedPosts コンポーネントは既存の PostCard を再利用し、ブログ一覧ページとの視覚的一貫性を保つ。サーバーコンポーネントとして実装し、配列が空なら null を返す — マッチがなければセクション自体が消える。

意図的なセマンティクスの判断として、コンポーネントはページレイアウト上 <article> の外に配置した。関連記事は記事のコンテンツではなく、ナビゲーションだからだ。

まとめ

  • 小規模ならメタデータスコアリングで十分 — カテゴリ(重み 2)+ タグ(重み 1)で、50記事未満のブログなら驚くほど良い結果が出る。ML は本当に必要になってからでいい。
  • バイリンガルサイトでは同ロケールフィルタが必須 — これがないと、日本語の読者に英語記事をレコメンドしてしまう。
  • 悪いレコメンドより表示しない方がいい — スコア 0 のフィルタで無関係な記事を排除する。空のセクションの方が、的外れな提案よりマシだ。

共有する

田原 慎也

田原 慎也

ソリューションアーキテクト @ AWS

AWS ソリューションアーキテクトとして金融業界のお客様を中心に技術支援をしており、クラウドアーキテクチャや AI/ML に関する学びをこのサイトで発信しています。このサイトの内容は個人の見解であり、所属企業の公式な意見や見解を代表するものではありません。

関連記事