PR

【Next.jsブログ構築】免責事項・アフィリエイト・お問い合わせページを作成してPR表示の仕組みを実装する方法

Next.jsブログに免責事項とアフィリエイトページを追加しPRバッジを実装している様子を表したイラスト VPS・RentalServer
この記事は約29分で読めます。
記事内に広告が含まれています。
スポンサーリンク

これまでの Next.js ブログサイト構築シリーズをまとめたページは以下の通りです。

これまで Next.js ブログのフッターに
「免責事項」「アフィリエイトについて」「お問い合わせ」のリンクを設置していましたが、
リンク先のページは未作成のままでした。

クリックするたびに 404 エラーが表示される状態は、
訪問者への印象が悪いだけでなく、将来のアフィリエイト契約の審査にも支障が出ます。

今回の第21回では、この3つの固定ページを新規作成します。

あわせて、将来のアフィリエイト掲載に向けた法律対応として、
記事に「PR」バッジを表示する仕組みも同時に実装しました。
アフィリエイトを始めるなら、広告表示の仕組みは先に用意しておく必要があります。
その理由についても詳しく解説します。

この記事でわかること:

  • Next.js で免責事項・アフィリエイト・お問い合わせページを作成する方法
  • 景品表示法に対応した「PR」バッジ表示の仕組みの作り方
  • Markdown のフロントマターに独自フィールドを追加して活用する方法
  • Google フォームを使ったお問い合わせページの実装方法

記事の前提条件

この記事は、以下の環境と進捗を前提として解説しています。

  • Next.js のバージョン:Next.js 15 系(React 19)の App Router を使用
  • 動作方式:VPS 上でサーバーモード(PM2)で動作
  • 対象読者:シリーズ第20回までを完了し、サイドバーと人気記事ランキングの実装が完了している方

なぜアフィリエイト未契約でもページを先に作るのか

「まだアフィリエイトの契約もしていないのに、アフィリエイトページを作る必要があるの?」

これは自然な疑問だと思います。結論から言うと、契約前に作っておくべき理由が2つあります。

理由①:審査の必須条件になっている

Google AdSense や Amazon アソシエイツ、A8.net などの ASP(アフィリエイトサービスプロバイダー)は、サイトの審査を実施しています。
その審査項目の中に「免責事項・プライバシーポリシーの記載」が含まれているケースがほとんどです。

当ブログはメインブログ(lifework-blog.com)では各 ASP との契約済みですが、サブドメイン(next.lifework-blog.com)は別ドメインとして扱われるため、別途申請が必要です。
申請のタイミングでこれらのページが揃っていないと、審査を通過できません。

理由②:法律上の義務がある

2023年10月、景品表示法が改正され、ステルスマーケティング規制が施行されました。
これにより、アフィリエイトリンクや広告を含む記事には「広告である」という表示が義務付けられました。

広告を掲載した瞬間から表示義務が発生するため、「まず掲載して、後で対応する」ではなく、先に仕組みを整えておく必要があります。

ブログサイトでアフィリエイトを利用しようとすると、ASP が求める対応に加えて、法律まわりの対応も必要になります。
今後、記事が増えて、読者が増えれば、ASP との契約もしたいと考え、早いうちに対応しようと考えたのが、今回の作業のきっかけです。

今回作成・変更するファイル

操作ファイル内容
新規作成app/disclaimer/page.tsx免責事項ページ
新規作成app/affiliate/page.tsxアフィリエイトについてページ
新規作成app/contact/page.tsxお問い合わせページ
変更lib/posts.tspr フィールドの型定義・読み込みを追加
変更app/page.tsx記事カードに PR バッジを追加
変更app/page/[pageNum]/page.tsx同上(2ページ目以降)
変更app/posts/[id]/page.tsx個別記事に PR バッジと広告バナーを追加

STEP 1:lib/posts.ts に pr フィールドを追加する

まず、Markdown のフロントマターに pr: true と書いた記事を「広告記事」として識別できる仕組みを作ります。

Next.js ブログでは lib/posts.ts というファイルが Markdown を読み込む役割を担っています。
ここに pr フィールドを追加します。

Cursor のターミナルで以下のコマンドを実行してファイルを開いてください。

nano ~/example-blog/lib/posts.ts

nano は VPS 上のテキストエディタです。
ファイルを編集した後は Ctrl + X を押し、保存確認に Y、ファイル名確認に Enter を押して保存します。
この操作はこれ以降の nano を使うすべての手順で共通です。

変更①:型定義に pr: boolean を追加する

PostData という型定義を探してください。
以下のように ogImage の次の行に pr: boolean を追加します。

export type PostData = {
  id: string;
  title: string;
  date: string;
  category: string;
  tags: string[];
  status: string;
  description: string;
  ogImage: string;
  pr: boolean;   // ← 追加
};

型定義とは何か

TypeScript では、変数やオブジェクトに入るデータの「形(型)」を事前に宣言しておくことができます。
型定義を書くことで、「この変数には文字列が入る」「この変数には真偽値(true/false)が入る」ということを TypeScript が把握し、間違った使い方をした場合にエラーで教えてくれます。

boolean は真偽値の型で、true(はい)か false(いいえ)のどちらかが入ります。
PR バッジを表示するかどうかのフラグとして使います。

変更②:getSortedPostsData() 関数の return に pr を追加する

同じファイルの中で getSortedPostsData() 関数を探してください。
return { で始まるブロックの末尾(ogImage の次)に以下を追加します。

return {
  id,
  title: (matterResult.data.title as string) ?? '',
  date: dateString,
  category: (matterResult.data.category as string) ?? '',
  tags,
  status: (matterResult.data.status as string) ?? 'done',
  description: (matterResult.data.description as string) ?? '',
  ogImage: (matterResult.data.ogImage as string) ?? '',
  pr: matterResult.data.pr === true,   // ← 追加
};

matterResult.data.pr === true という書き方のポイント

pr: matterResult.data.pr === true は「フロントマターの prtrue と完全に一致する場合のみ true、それ以外はすべて false」という意味です。

この書き方をする理由は安全性のためです。
フロントマターに pr が書かれていない記事(既存の全記事)は undefined になりますが、undefined === truefalse なので安全に false として扱われます。

変更③:getPostData() 関数にも同じ変更を行う

同じファイルの中に getPostData() という別の関数があります。
この関数は個別記事ページで使われる関数で、getSortedPostsData() とは別物です。
両方に追加しないと個別記事ページで postData.pr が使えません。

content: matterResult.content という行を含む return { ブロックを探し、末尾に追加してください。

return {
  id,
  content: matterResult.content,
  title: matterResult.data.title as string,
  date: dateString,
  description: (matterResult.data.description as string) ?? '',
  ogImage: (matterResult.data.ogImage as string) ?? '',
  status: (matterResult.data.status as string) ?? 'done',
  category: (matterResult.data.category as string) ?? '',
  tags: Array.isArray(matterResult.data.tags) ? matterResult.data.tags : [],
  pr: matterResult.data.pr === true,   // ← 追加
};

STEP 2:免責事項ページを作成する

Next.js の App Router では、app/ ディレクトリ内にフォルダを作り、その中に page.tsx を置くだけで新しいページが生成されます。
今回は app/disclaimer/ というフォルダを作り、その中に page.tsx を作成します。

これにより /disclaimer という URL でページにアクセスできるようになります。

まずフォルダを作成します。

mkdir -p ~/example-blog/app/disclaimer

mkdir は「フォルダを作成するコマンド」、-p オプションは「途中のフォルダが存在しない場合も含めて作成する」という意味です。

次にファイルを作成します。
以下のコマンドをそのまま貼り付けて実行してください。

以降のコードにおいても同様ですが、コード内のサイト名や URL などは、ご自身のサイト名や URL に変更して使用してください。

cat > ~/example-blog/app/disclaimer/page.tsx << 'EOF'
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: '免責事項 | あなたのブログ名',
  description: 'あなたのブログ名 の免責事項・プライバシーポリシーについて説明しています。',
};

export default function DisclaimerPage() {
  return (
    <div className="max-w-3xl mx-auto px-4 py-12">
      <h1 className="text-2xl font-bold text-[#e2e4ea] mb-8 pb-3 border-b-2 border-[#0f6e56]">
        免責事項
      </h1>
      <div className="space-y-10 text-[#c0c4d0] leading-relaxed">
        <section>
          <h2 className="text-lg font-bold text-[#e2e4ea] mb-3">情報の正確性について</h2>
          <p>当サイト(あなたのブログ名)に掲載している情報は、公開時点での内容です。
          技術情報・設定手順・コマンド等は、OS・ソフトウェアのバージョンや環境によって結果が異なる場合があります。
          掲載内容の正確性・最新性・完全性については保証できません。
          記事の内容を実際の作業に使用する際は、必ずご自身の環境でご確認ください。</p>
        </section>
        <section>
          <h2 className="text-lg font-bold text-[#e2e4ea] mb-3">損害について</h2>
          <p>当サイトの情報を利用したことにより生じたいかなる損害・トラブルについても、
          当サイトは一切の責任を負いません。作業の実施はご自身の責任においておこなってください。</p>
        </section>
        <section>
          <h2 className="text-lg font-bold text-[#e2e4ea] mb-3">外部リンクについて</h2>
          <p>当サイトからリンクしている外部サイトの内容・安全性については関知しておりません。
          外部サイトの利用については、各サイトの利用規約をご確認ください。</p>
        </section>
        <section>
          <h2 className="text-lg font-bold text-[#e2e4ea] mb-3">アフィリエイト広告について</h2>
          <p>
            当サイトは、将来的にアフィリエイト広告(Google AdSense・Amazon アソシエイツ等)を掲載する予定です。
            広告を含む記事には「PR」の表示をおこないます。詳細は
            <a href="/affiliate" className="text-[#4dd4b0] hover:underline mx-1">アフィリエイトについて</a>
            をご覧ください。
          </p>
        </section>
        <section>
          <h2 className="text-lg font-bold text-[#e2e4ea] mb-3">著作権について</h2>
          <p>当サイトに掲載しているテキスト・画像・コード等の著作権は、当サイト運営者に帰属します。
          無断転載・複製・改変はご遠慮ください。
          ただし、記事内のコードについては自由に利用していただいて構いません。</p>
        </section>
        <section>
          <h2 className="text-lg font-bold text-[#e2e4ea] mb-3">プライバシーポリシー</h2>
          <h3 className="text-base font-semibold text-[#d0d4e0] mb-2 mt-4">アクセス解析ツールについて</h3>
          <p>当サイトでは、Google が提供するアクセス解析ツール「Google アナリティクス(GA4)」を使用しています。
          このツールはデータ収集のために Cookie を使用しています。
          収集されるデータは匿名であり、個人を特定するものではありません。</p>
          <p className="mt-2">Google アナリティクスのデータ収集を無効化したい場合は、
          Google アナリティクス オプトアウトアドオン(
          <a href="https://tools.google.com/dlpage/gaoptout" target="_blank" rel="noopener noreferrer"
            className="text-[#4dd4b0] hover:underline">
            https://tools.google.com/dlpage/gaoptout
          </a>)をご利用ください。</p>
          <h3 className="text-base font-semibold text-[#d0d4e0] mb-2 mt-4">Cookie について</h3>
          <p>当サイトは Google アナリティクスの使用にともない Cookie を利用しています。
          ブラウザの設定により Cookie を無効にすることが可能ですが、
          その場合、一部の機能が正常に動作しない場合があります。</p>
          <h3 className="text-base font-semibold text-[#d0d4e0] mb-2 mt-4">個人情報の取り扱いについて</h3>
          <p>お問い合わせフォームよりご連絡いただいた場合、お名前・メールアドレス等の個人情報は、
          お問い合わせへの返答のみに使用します。第三者への提供はおこないません。</p>
        </section>
        <p className="text-sm text-[#6a7080] pt-4 border-t border-[#3a3f4a]">
          最終更新日:{new Date().getFullYear()}年
        </p>
      </div>
    </div>
  );
}
EOF

cat > ファイル << 'EOF' という書き方について

この書き方は「ヒアドキュメント」と呼ばれる記法です。
EOF と書かれた行が来るまでの内容を、そのままファイルに書き込みます。
nano などのエディタで1行ずつ入力する手間が省け、インデントや引用符もそのまま保存されます。

{new Date().getFullYear()}年 について

最終更新年の部分には {new Date().getFullYear()} というコードを使いました。
これは JavaScript でビルド時点の西暦を取得するコードです。
2026 などと直接書かずに済むため、年が変わっても自動で更新されます。

免責事項ページにプライバシーポリシーを含める理由

Google アナリティクスを使用しているサイトは、Google の利用規約により「Cookie を使用してデータを収集していること」をプライバシーポリシーに明記する義務があります。
免責事項ページにプライバシーポリシーのセクションをまとめて掲載することで、ページ数を増やさずに対応できます。

Next.jsブログに新規作成した「免責事項」ページのブラウザ表示画面

今回作成した「免責事項」のページの一部を表示した画像です。
ご紹介したコードは、免責事項として一般的なものと思われるものを記載していますが、適宜、ご自身のサイトに合わせて変更してください。

STEP 3:アフィリエイトページを作成する

アフィリエイトページは、広告掲載の方針・ステルスマーケティング規制への対応・掲載予定のサービスを説明するページです。
現時点では「将来的に掲載予定」として公開しておき、契約が確定した時点でサービス名を追記します。

STEP 2 と同じ手順でフォルダとファイルを作成します。

mkdir -p ~/example-blog/app/affiliate
cat > ~/example-blog/app/affiliate/page.tsx << 'EOF'
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'アフィリエイトについて | あなたのブログ名',
  description: 'あなたのブログ名 におけるアフィリエイト広告の掲載方針について説明しています。',
};

export default function AffiliatePage() {
  return (
    <div className="max-w-3xl mx-auto px-4 py-12">
      <h1 className="text-2xl font-bold text-[#e2e4ea] mb-8 pb-3 border-b-2 border-[#0f6e56]">
        アフィリエイトについて
      </h1>
      <div className="space-y-10 text-[#c0c4d0] leading-relaxed">
        <section>
          <h2 className="text-lg font-bold text-[#e2e4ea] mb-3">広告掲載について</h2>
          <p>当サイト(あなたのブログ名)は、将来的にアフィリエイト広告を掲載する予定です。
          現時点では広告の掲載はおこなっていません。</p>
          <p className="mt-3">アフィリエイト広告とは、商品・サービスを紹介したリンクを読者がクリックし、
          購入・申し込みに至った場合に運営者が報酬を受け取る仕組みです。
          読者の方が追加の費用を負担することはありません。</p>
        </section>
        <section>
          <h2 className="text-lg font-bold text-[#e2e4ea] mb-3">広告の明示について</h2>
          <p>当サイトでは、景品表示法および 2023 年 10 月施行のステルスマーケティング規制に基づき、
          広告・PR を含む記事には必ず以下の表示をおこないます。</p>
          <ul className="mt-3 space-y-1 list-disc list-inside">
            <li>記事一覧カードの日付横に「PR」バッジを表示</li>
            <li>記事タイトルの下に「PR」バッジを表示</li>
            <li>記事本文の冒頭に「記事内に広告が含まれています」と表示</li>
          </ul>
        </section>
        <section>
          <h2 className="text-lg font-bold text-[#e2e4ea] mb-3">掲載予定の広告サービス</h2>
          <p>将来的に以下のアフィリエイトサービスへの参加を予定しています。
          参加が確定しているものはありません。</p>
          <ul className="mt-3 space-y-1 list-disc list-inside">
            <li>Google AdSense</li>
            <li>Amazon アソシエイツ</li>
            <li>A8.net</li>
            <li>バリューコマース</li>
          </ul>
          <p className="mt-3">各サービスへの参加が確定した時点で、このページを更新します。</p>
        </section>
        <section>
          <h2 className="text-lg font-bold text-[#e2e4ea] mb-3">記事内容の独立性について</h2>
          <p>当サイトに掲載する情報は、広告収入の有無にかかわらず、
          実際に使用した体験・検証をもとに書いています。
          広告掲載の有無が記事の評価・内容に影響することはありません。</p>
        </section>
        <p className="text-sm text-[#6a7080] pt-4 border-t border-[#3a3f4a]">
          最終更新日:{new Date().getFullYear()}年
        </p>
      </div>
    </div>
  );
}
EOF

完成するとアフィリエイトについてページ(/affiliate)が表示されるようになります。
広告掲載の方針・PR 表示の具体的な方法・掲載予定サービスの4つのセクションで構成されたシンプルなページです。

Next.jsブログに新規作成した「アフィリエイトについて」ページのブラウザ表示画面

今回作成した「アフィリエイトについて」のページの一部を表示した画像です。
ご紹介したコードは、アフィリエイトを利用している旨を伝える文章として一般的なものと思われるものを記載していますが、適宜、ご自身のサイトに合わせて変更してください。

STEP 4:お問い合わせページを作成する

お問い合わせページは Google フォームへのリンク方式で実装します。

Contact Form 7 や Akismet のようなプラグインが使えない Next.js ブログでも、Google フォームを使えばスパム対策・メールアドレス非公開・入力フォームの整備をすべて Google に任せることができます。

URL を後から差し替えられる設計にする

ページファイルの先頭に CONTACT_FORM_URL という定数を用意しています。

const CONTACT_FORM_URL = '';
  • 空文字('')のとき:「現在、お問い合わせフォームを準備中です」のメッセージを表示
  • URL が設定されているとき:「お問い合わせフォームを開く」ボタンを表示

Google フォームを作成したら、この1行だけ書き換えてデプロイするだけでボタンが表示されます。
ページ全体を修正する必要はありません。

mkdir -p ~/example-blog/app/contact
cat > ~/example-blog/app/contact/page.tsx << 'EOF'
import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'お問い合わせ | あなたのブログ名',
  description: 'あなたのブログ名 へのお問い合わせはこちらからどうぞ。',
};

const CONTACT_FORM_URL = '';

export default function ContactPage() {
  return (
    <div className="max-w-3xl mx-auto px-4 py-12">
      <h1 className="text-2xl font-bold text-[#e2e4ea] mb-8 pb-3 border-b-2 border-[#0f6e56]">
        お問い合わせ
      </h1>
      <div className="space-y-6 text-[#c0c4d0] leading-relaxed">
        <p>あなたのブログ名 へのお問い合わせは、以下のフォームからお送りください。</p>
        <div className="bg-[#2a2d38] border border-[#3a3f4a] rounded-lg p-5 text-sm space-y-2">
          <p className="text-[#9ca3af] font-semibold mb-3">フォームの入力項目</p>
          <ul className="space-y-1 list-disc list-inside text-[#c0c4d0]">
            <li>氏名</li>
            <li>メールアドレス</li>
            <li>題名</li>
            <li>メッセージ本文(任意)</li>
          </ul>
        </div>
        {CONTACT_FORM_URL ? (
          <div className="pt-2">
            <a href={CONTACT_FORM_URL} target="_blank" rel="noopener noreferrer"
              className="inline-block bg-[#0f6e56] hover:bg-[#0a5242] text-white font-semibold px-8 py-3 rounded-lg transition-colors">
              お問い合わせフォームを開く
            </a>
            <p className="mt-3 text-xs text-[#6a7080]">Google フォームが別タブで開きます。</p>
          </div>
        ) : (
          <div className="bg-[#2a2d38] border border-[#4a5060] rounded-lg p-5 text-sm text-[#9ca3af]">
            現在、お問い合わせフォームを準備中です。しばらくお待ちください。
          </div>
        )}
        <section className="pt-4">
          <h2 className="text-base font-bold text-[#e2e4ea] mb-2">返信について</h2>
          <p className="text-sm">内容によってはご返信できない場合や、返信にお時間をいただく場合があります。
          あらかじめご了承ください。</p>
        </section>
      </div>
    </div>
  );
}
EOF

三項演算子(? :)について

コードの中に {CONTACT_FORM_URL ? ( ... ) : ( ... )} という記述があります。
これは「三項演算子」と呼ばれる条件分岐の書き方です。

{条件 ? 条件が真(true)のときの表示 : 条件が偽(false)のときの表示}

CONTACT_FORM_URL に URL が入っていれば(true と判断される)ボタンを表示し、空文字であれば(false と判断される)「準備中」のメッセージを表示します。

現時点では CONTACT_FORM_URL = '' のままにしておきます。
Google フォームを作成した後に、ここに URL を貼り付けてデプロイするだけで「フォームを開く」ボタンが表示される状態になります。

Next.jsブログに新規作成した「お問い合わせ」ページのブラウザ表示画面

今回作成した「お問い合わせ」のページを表示した画像です。
まだ、お問い合わせフォームとのリンクを準備していないため、「準備中です」というメッセージが表示されていますが、今後、フォームを作成して、リンクを挿入します。

STEP 5:PR バッジを記事カードに追加する

トップページの記事カードに PR バッジを追加します。
app/page.tsxapp/page/[pageNum]/page.tsx の2ファイルに同じ変更を行います。

nano ~/example-blog/app/page.tsx

ファイルの中で {/* 記事情報 */} というコメントを含む <div className="p-4"> のブロックを探してください。以下のように変更します。

変更前:

<div className="p-4">
  <h2 className="text-sm font-bold text-[#e2e4ea] leading-relaxed line-clamp-3">
    {post.title}
  </h2>
  <p className="text-xs text-[#6b7280] mt-2 text-right">
    {post.date}
  </p>
</div>

変更後:

<div className="p-4">
  <h2 className="text-sm font-bold text-[#e2e4ea] leading-relaxed line-clamp-3">
    {post.title}
  </h2>
  <div className="flex items-center justify-end gap-2 mt-2">
    {post.pr && (
      <span className="bg-[#b45309] text-white text-xs font-bold px-2 py-0.5 rounded shrink-0">
        PR
      </span>
    )}
    <p className="text-xs text-[#6b7280]">{post.date}</p>
  </div>
</div>

各クラスの意味

クラス意味
flex items-center justify-end横並び・上下中央揃え・右寄せ
gap-2PR バッジと日付の間隔(8px)
bg-[#b45309]アンバー(琥珀色)の背景。サイトカラーと区別できる色
shrink-0PR バッジが縮まないようにする

PR バッジの色をアンバーにした理由

当ブログのサイトカラーはティールグリーン(#0f6e56)です。
PR バッジも同じ色にすると、カテゴリーバッジと区別がつきません。

景品表示法では「読者がひと目で広告だとわかる」表示が求められています。
そのため、あえてサイトカラーとは異なるアンバー色を採用しました。

{post.pr && ( ... )} の書き方について

&& は「かつ」の意味を持つ演算子ですが、JSX(Next.js のコード)の中では別の使い方もできます。
{条件 && <表示内容>} と書くと、条件が true のときだけ表示内容をレンダリングします。
post.prfalse の記事では何も表示されません。

同じ変更を app/page/[pageNum]/page.tsx にも行ってください。

nano ~/example-blog/app/page/\[pageNum\]/page.tsx

STEP 6:個別記事ページに PR バッジと広告バナーを追加する

個別記事ページには2つの要素を追加します。

要素表示場所役割
PR バッジタイトルの直下広告記事だとひと目でわかるラベル
広告バナーアイキャッチ画像の直前本文を読む前の具体的な告知
nano ~/example-blog/app/posts/\[id\]/page.tsx

PR バッジの追加

<h1> タグの直後に以下を追加してください。

{/* PR バッジ */}
{postData.pr && (
  <span className="inline-block bg-[#b45309] text-white text-xs font-bold px-2 py-0.5 rounded mt-2">
    PR
  </span>
)}

広告バナーの追加

{/* アイキャッチ画像 */} というコメントの直前に以下を追加してください。

{/* 広告表示バナー */}
{postData.pr && (
  <div className="mb-4 px-4 py-2 bg-[#2a2d38] border border-[#4a5060] rounded text-xs text-[#9ca3af] text-center">
    記事内に広告が含まれています。
  </div>
)}
Next.jsブログのトップページで「PR」バッジが正しく表示されるかを確認したテスト画面

上の画像は、トップページで PR が正しく表示されるかをテストしたものです。
PRTRUE の記事は、日付の横に PR という文字が表示されるようになります。

Next.jsブログの個別記事ページで「PR」バッジと広告表示バナーが正しく表示されるかを確認したテスト画面

PRTRUE の個別記事をテストで表示したものです。
タイトル部分の日付の上に、 PR の文字が表示され、アイキャッチ画像の上に、「記事内に広告が含まれています。」の注意書きが表示されています。

STEP 7:ビルドしてデプロイする

すべての変更が完了したらビルドしてデプロイします。

cd ~/example-blog && npm run build 2>&1 | tail -30

ビルドが成功すると、ルート一覧に以下が表示されます。

○ /affiliate
○ /contact
○ /disclaimer

は「Static(静的コンテンツ)」の意味で、3ページが正しく生成されていることを確認できます。

cd ~/example-blog && ./deploy.sh

STEP 8:Markdown のフロントマターに pr フィールドを追加する

新しいフィールドを追加したので、Obsidian のテンプレートと既存記事への対応が必要です。

Obsidian テンプレートを更新する

Obsidian のテンプレートファイル(Next-Post-Template.md)を開き、draft: true の次の行に以下を追加します。

draft: true
pr: false

これにより、新規記事を作成するたびに自動的に pr: false が入ります。

既存記事に pr: false を追加する

すでに公開済みの記事にも pr: false を追加します。
フロントマターに pr の記述がない記事は false として扱われるため機能上は問題ありませんが、明示的に書いておくことで意図が明確になります。

各記事ファイルを Obsidian で開き、draft: false の次の行に pr: false を追加してください。

PR フィールドの運用ルール

今後の運用について重要なルールを確認しておきます。

pr: true にするのは、実際に広告リンクを記事に掲載した場合のみです。

広告を含まない記事に pr: true を付けると、読者への虚偽表示になります。
景品表示法は「広告でないものを広告と偽ること」も問題になり得ます。

アフィリエイト契約完了
       ↓
記事内に広告リンクを実際に貼る
       ↓
その記事のみ pr: false → pr: true に変更
       ↓
Obsidian からデプロイ

動作確認

ブラウザで以下のページを確認してください。

  • https://next.あなたのブログ名.com/disclaimer → 免責事項ページが表示されること
  • https://next.あなたのブログ名.com/affiliate → アフィリエイトページが表示されること
  • https://next.あなたのブログ名.com/contact → 「準備中」メッセージが表示されること

PR バッジの動作確認は、任意の記事のフロントマターに pr: true を一時的に追加してデプロイし、以下を確認してください。

  • トップページの記事カードで日付の左横に「PR」バッジが表示されること
  • 個別記事ページのタイトル下に「PR」バッジが表示されること
  • 個別記事ページのアイキャッチ画像の直前に「記事内に広告が含まれています。」が表示されること

確認後は pr: truepr: false に戻してデプロイしてください。

まとめ

今回は免責事項・アフィリエイト・お問い合わせの3ページを作成し、PR 表示の仕組みも実装しました。

この Next.js ブログサイト構築シリーズでは、Obsidian を使って記事を管理し、バッチ処理でデプロイする方法を構築してきていますが、Obsidian のノートのプロパティで、簡単に PR のオン・オフを切り替えられるような設計にしたことで、非常に使い勝手の良い仕組みになったと感じています。

また、今回の実装で特に意識したのは「先手を打つ」という考え方です。
アフィリエイト未契約でもページとバッジの仕組みを先に用意しておくことで、実際に広告を掲載するタイミングで慌てずに済みます。

残タスク

お問い合わせページは Google フォームの URL を差し込む形式にしているため、フォームを作成した後に CONTACT_FORM_URL の値を更新するだけで対応できます。

アフィリエイトページも、各 ASP への申請が完了したタイミングで具体的なサービス名の記載を追記する予定です。

次回の作業

第22回では SNS シェアボタンの実装を予定しています。
X(Twitter)やFacebookなどの SNS へのシェアボタンを個別記事ページに追加します。
API キー不要で実装できる手軽な機能です。

コメント

タイトルとURLをコピーしました