Next.jsでバナー広告(AdStir)導入のトライ&エラー記録 ~SPAとdocument.writeの壁~

はじめに

Copilot

ブログやメディアサイトを運営していると、広告ネットワークの導入は収益化の重要な一歩です。

Webエンジニアであれば何度も行ってきたであろう広告の導入で、今回も数時間で終わるだろうと思っていましたが、非同期処理ではまり、二日間を要しました。

今回は、Next.js(App Router構成)で日本の広告ネットワーク「AdStir」のバナー広告を導入しようとした際の、実装・トラブル・解決までの記録をまとめました。

この記事のポイント

  • Next.js(SPA)で広告タグを埋め込む際のトライ&エラー
  • CSP(Content Security Policy)との格闘
  • Hydration Errorやdocument.writeの罠
  • 最終的に「非同期タグ」が必要だった理由とその発見プロセス

最初の実装:公式タグをそのまま埋め込む

まずはAdStir管理画面で取得した「スマホ向けバナー広告(320x50)」の公式タグを、Reactコンポーネントに埋め込みました。

// ...existing code...
const adstirHtml = `
  <script type="text/javascript">
    var adstir_vars = {
      ver: "4.0",
      app_id: "MEDIA-XXXXXXX",
      ad_spot: 1,
      center: false
    };
  </script>
  <script type="text/javascript" src="https://js.ad-stir.com/js/adstir.js"></script>
`;

return (
  <div dangerouslySetInnerHTML={{ __html: adstirHtml }} />
);
// ...existing code...

結果
→ 広告は表示されず、ブラウザのコンソールにエラーが出現。

Hydration Error(React error #418)との戦い

Next.js(App Router)はSSRとCSRのHTML差分に敏感です。広告タグの埋め込みで「Hydration mismatch」エラーが頻発。

試したこと

  • Next.jsの`<Script>`コンポーネント(strategy: "afterInteractive")
  • `useEffect`で動的に`script`タグを挿入
  • dynamic import({ ssr: false })でクライアント専用化
import Script from 'next/script';

<>
  <Script id="adstir-vars" strategy="afterInteractive">{`
    var adstir_vars = { ver: "4.0", app_id: "MEDIA-XXXXXXX", ad_spot: 1, center: false };
  `}</Script>
  <Script id="adstir-src" strategy="afterInteractive" src="https://js.ad-stir.com/js/adstir.js" />
</>

結果
→ Hydrationエラーは減ったが、広告は依然として表示されない。

CSP(Content Security Policy)との格闘

広告ネットワークは多くの外部ドメインを利用します。CSPでブロックされると、スクリプトやiframeが読み込まれません。

対応例(抜粋)

const csp = [
  "default-src 'self'",
  "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.ad-stir.com https://*.ad-stir.com ...",
  "frame-src 'self' https://*.ad-stir.com ...",
  // ...他にもadsappier, pubmatic, rubiconproject, amoad, sp-trkなど都度追加
].join('; ');

運用ポイント

  • 広告表示のたびに新しいCSP警告が出る
  • ブラウザのコンソールで警告を確認し、必要なドメインを追加
  • ワイルドカード(*)は使えないので、都度手動で追加

document.writeの罠と非同期タグの必要性

最大の壁
AdStirの標準タグはdocument.write()を使って広告枠を生成します。
しかし、React/Next.js(SPA)では、document.write()は「非同期で挿入されたスクリプト」からは実行できません。

エラー例

A call to document.write() from an asynchronously-loaded external script was ignored.

なぜ?

  • Reactのレンダリング後にscriptタグを挿入すると、ブラウザは「同期的なHTML解析中」ではないと判断
  • そのため、document.write()は無視され、広告枠が生成されない

試したこと

  • useEffectでscript.async = falseにしてみる
  • 変数定義→本体スクリプトの順で挿入
  • dangerouslySetInnerHTMLでインライン埋め込み

結果
→ どれもdocument.write()が無視され、広告は表示されない

非同期タグの発見と導入

らちが明かないとAdStirサポートに問い合わせたところ、「SPA対応の非同期タグ」が別途配布されていることが判明。

非同期タグの実装例

import { useEffect, useState, useRef } from 'react';

export default function AdStirBanner() {
  const [isMobile, setIsMobile] = useState(false);
  const scriptLoadedRef = useRef(false);

  useEffect(() => {
    const checkMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
    setIsMobile(checkMobile);

    if (checkMobile && !scriptLoadedRef.current) {
      const script = document.createElement('script');
      script.async = true;
      script.type = 'text/javascript';
      script.src = '{非同期タグ向けのスクリプト}';
      document.body.appendChild(script);
      scriptLoadedRef.current = true;
    }
  }, []);

  if (!isMobile) return null;

  return (
    <div style={{ height: '60px', maxWidth: '320px', margin: '0 auto' }}>
      <div
        //ここに広告SDKの情報を記載
      />
    </div>
  );

結果
→ 非同期タグならdocument.write()を使わず、SPAでも広告が表示される!

!注意!

今回非同期タグの情報はこちらに記載していません。というのもAdstirに問い合わせしなくてはならないものなので、易々と公開していいものではないかと判断しました。

まとめと学び

  • AdStirの標準タグ(document.write依存)はSPA/React/Next.jsでは動作しない
  • CSPは広告ネットワークの追加ごとに都度対応が必要
  • Hydration ErrorはSSR/CSRの差分が原因。dynamic importやクライアント限定描画で回避可能
  • 最終的な解決策は「非同期タグ」の導入。ベンダーに問い合わせて入手するのが最短ルート
  • ブラウザのコンソールでエラーを監視し、CSPや実装を都度調整する運用が現実的

Copilot活用ポイント

  • 実装案の生成(Script, useEffect, dynamic import, CSPテンプレート)
  • エラー原因の整理(Hydration mismatch, document.writeの仕様)
  • デバッグ補助(ログ挿入、onload/onerrorハンドラ)

実装する時間が大幅に削減できいろいろなトライ&エラーを試すことができました。広告は過去に何度も導入した経験があり、そんなに時間がかからないだろうと思っていましたが、二日間要しました。しかし、仕事や練習の合間をぬった二日間で、Copilotを使っていなければ一週間以上かかっていたでしょう。

広告導入で困ったら、まずは「非同期タグがあるか」をベンダーに確認しましょう!
SPA/React/Next.jsでの広告実装は、ベンダーの仕様とブラウザの制約を理解することが最重要です。

コメント (0)

まだコメントがありません。最初のコメントを投稿してみませんか?

コメントを投稿する

0/500