Google Ad Managerを導入してみた

# はじめに
オークファンの佐藤です。ここしばらくは特定商取引法対応やフロント側の検索見直しなど、レガシーコードを横串で眺めて一気にリファクタリングすることを進めてきました。
そのあたりの話はまとまったら書きたいですが、今回は二年ほど前に実施した、**Google Ad Manager (GAM)** 導入の話をしてみようと思います。

（´-`）.｡oO（自分が導入したときも、Google Ad Managerに関する情報があまり出てこなかったなあと思い出しながら、これから実施する人の参考になるように……）

# 課題意識
オークファンの売り上げの一部は、ネットワーク広告やクライアントの広告掲載費、アフィリエイトバナー経由の商品購入から発生しています。
これらは、純広告受注や、楽天・Amazon・Yahoo!ショッピングなどが展開するキャンペーンに対応して、不定期に内容を差し替えることがあります。
しかし、この運用がHTML手書きでした。そのため運用・開発を巻き込んで入稿・設定・テスト・リリースが不定期に繰り返され、業務自体が煩雑になっていました。
最悪の場合、期間指定のリリース依頼という厄介なものもありました。準備できないままそういう案件に遭遇すると、リリース禁止期間でも容赦なくリリースすることを強いられます。

特に純広告は、表示して終わりではありません。
クリック数を測定するには専用のクリックカウンター、クリエイティブをアップロードするには専用のアップロードサーバー、出し分けを行うには個別のプログラムと条件を指定……と、広告を確実に配信するための作業は、都度エンジニアが対応することになります。もちろん公開前にはテストが必要です。
これらはかなり定型化できるはずです。

このような課題を解決するには、自前でシステムを組んでもいいですが、**アドサーバー**を導入してしまうのが一番いいでしょう。
アドサーバーを導入することにより、クリエイティブ・配信条件・測定を一挙に外部化し、一元管理することができます。

一番導入しやすかったのがGoogle Ad Manager だったため、オークファンに導入することにしました。
月間延べ**1億5000万インプレッションまで無料** などで、ほとんどの環境では無料で使えることでしょう。

https://admanager.google.com/intl/ja/home/

（´-`）.｡oO（神）

# Google Ad Manager導入
## 広告枠の定義
最初に考えることは、そもそも「**どの表示スペースが広告であるか**」、つまり「広告枠」の定義です。
過去の運用ではページのHTMLを直接触っていたため、どんなことでもできました。逆に言うと、「どの部分を変更する」という合意が成立しないまま、担当者の裁量でページが書き換えられていたということです。

この定義は一番重要です。場合によっては、実装よりここが大変かもしれません。

## 運用業務・枠管理の発生
GAMを導入するということは、開発側がよきに図らっていた作業が、管理画面経由でだれでも担当できるようになるということです。いままで依頼していた営業・運用側が直接操作可能になります。
同時に、複数の担当者が複数の意図で広告を操作する……という現象が発生する可能性があります。
つまり、枠がコンフリクトする可能性が生じるのです。

システム的には当然のことですが、業務的には意外とこれが厄介です。
HTMLで運用していた時代であれば、ソースコードが最後の防波堤になるため、開発者が気づいてストップをかけることができました。管理画面経由だと、**強い意志と権限**があれば押し切られてしまいます。
スプレッドシートで枠の内容を事前調整する、といった運用が必要不可欠です。

## 広告設定
アドサーバーによる広告配信は、「広告枠」（広告ユニット）と「広告内容」（広告表示内容）を「表示条件」でマッチングさせ、「インプレッション」と「クリック」を測定してくれます。
「広告内容」には複数のクリエイティブ・リンク先があり、「表示条件」は広告ごとに、非常に細かく設定可能です。
普段はGoogle AdSenseなどのネットワーク広告をセットしておいて、一定期間だけアフィリエイト広告に差し替える、といったこともできます。


基本的な設定内容は[GAMのマニュアル](https://support.google.com/admanager/answer/1638622)に譲りますが、開発者が気にしておいた方がいいのは、Key-Value設定です。

### Key-Value
HTMLから渡した値（Key-Value）をもとに、GAM側でロジックを組み、配信条件に使用することができます。
たとえば、検索ワードやカテゴリによって広告内容を出し分けることが可能になり、ユーザーの意図に近い広告を出せるようになります。嫌がられない広告は、ページ全体のクオリティアップにも貢献します。

もちろんGAM管理画面側での設定も必要ですし、設定内容はいくらでも細かくできます。
そのため実装はアイデア次第ですが、以下の設定はぜひおすすめします。

- 本番・ステージングの区別
    - 配信中の広告設定を維持したまま、新規広告をステージング側だけテスト可能になる

## 広告タグの発行・実装
HTMLタグは、標準では以下のように配布されます。

ヘッダー用（head内）
```html
<script async src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>
<script>
  window.googletag = window.googletag || {cmd: []};
  googletag.cmd.push(function() {
    googletag.defineSlot('/xxxxxxx/yyyyy', [300, 250], 'div-gpt-ad-xxxxxxxxxxx-0').addService(googletag.pubads());
    googletag.pubads().enableSingleRequest();
    googletag.enableServices();
  });
</script>
```

表示個所用（body内）
```html
<!-- /xxxxxxx/yyyyy -->
<div id='div-gpt-ad-xxxxxxxxxxx-0' style='min-width: 300px; min-height: 250px;'>
  <script>
    googletag.cmd.push(function() { googletag.display('div-gpt-ad-xxxxxxxxxxx-0'); });
  </script>
</div>

```

同一ページに複数広告がある場合、ヘッダー用のタグはまとめます。
```html
<script async src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>
<script>
  window.googletag = window.googletag || {cmd: []};
  googletag.cmd.push(function() {
    googletag.defineSlot('/xxxxxxx/yyyy', [300, 250], 'div-gpt-ad-xxxxxxxxxxx-0').addService(googletag.pubads());
    googletag.defineSlot('/xxxxxxx/zzzz', [300, 250], 'div-gpt-ad-xxxxxxyyyyy-0').addService(googletag.pubads());

    googletag.pubads().enableSingleRequest();
    googletag.enableServices();
  });
</script>

```

各タグを表示位置に貼るだけでも広告は出せますが、Key-Valueのターゲティングを全広告に配布するためには、呼び出し箇所を共通化する必要があります。

```html
    // defineSlot直後のsetTargetingは、その広告にだけ効く
    googletag.defineSlot('/xxxxxxx/yyyyy/some_banners_970x250', [970, 250], '1380278').setTargeting('hoge', 'fuga')addService(googletag.pubads());
    // pubads() に対するsetTargetingは、そこまでのすべての広告に効く
    googletag.pubads().setTargeting('HOGE', 'FUGA')
```

## デバッグ
GAMは標準で、フロントエンドからの広告配信状況を分析できます。
たとえばGoogle Chromeだと、Developer Toolsを起動して、Consoleタブから
```javascript
googletag.openConsole();
```
を実行すると、ページ下部に広告スロットの情報が表示されます。

![gam_open_console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9384/65f5ae0e-4a68-e013-04f6-2772a9c083b1.png)


「ページレベルのターゲティング」でにはsetTargetingした内容が表示されます。この値がGAM側に渡り、広告が選ばれて配信されるわけです。
![gam_console_targeting2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9384/7516890e-b174-d25d-3d50-6f4e1cc362da.png)


配信者のGAMにログインできる場合、「配信診断」をクリックすると、どういうロジックでこの広告が配信されたかをデバッグすることができます。
![gam_request_simulation2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9384/1954fdcf-cae8-0dd1-6753-f76c08e6bc50.png)


このテストはGAM管理画面上でも実施できるため、Key-Valueに基づく配信を単独でテストできます。

# 注意すること
## AdBlockに引っかかる
当然ですが、Google Ad Managerで配信するものは広告とみなされます。自社コンテンツをレコメンドしたいがために使用していても、広告は広告なので、AdBlockに引っかかります。
UX上必ず通らなければいけない部品をGAM化するのは避けましょう。

## SEO効果がない
GAMのコンテンツはIframe経由で配信されるため、配信元のコンテンツとは別枠になります。その結果として、GAM経由で配信したリンク先への評価は発生しません。
このため、検索流入を狙うコンテンツは、GAM以外の方法で直接HTML内にリンクを貼り出す必要があります。つまり、HTML直書きは根絶できないわけです。

## 共通ヘッダーの出し分け
多くのサイトでは、ヘッダーと内部パーツを別部品で管理し、ヘッダーは共通で使用し、内部パーツは個別に分けることが多いはずです。
その場合広告タグも、定義がヘッダーに、表示部分が内部パーツに分断されます。
余計なタグ定義や、タグ定義なしの表示処理が発生するとスクリプトエラーになるため、ヘッダー側の表示出し分け処理がややこしくなる傾向にあります。

# やってみて
まず、開発への依頼は激減しました！　結果として各エンジニアがほかの非定型作業に集中できるようになり、業務の質は上がったはずです。

加えて、広告運用側も楽になっています。
開発依頼がなくなると作業待ちも発生せず、運用側単独で業務が完結したため、スケジュールも見積もりやすくなりました。
クリエイティブアップロード・広告設定などの作業が増えた一方で、テスト準備や開発待ちなどが減ったため、全体としてはやや効率化しています。

# 今後の課題
GAMのターゲティングは、スクリプトを1行追加すれば容易に追加できます。要求に対して対応するのにはいいのですが、機能に応じてターゲティングが増えて、同じ変数が別名でターゲティングされるなど、カオス化する傾向にあります。
広告担当者は枠単位で考えるのでやむを得ないのですが、GAMタグを出力する処理ごと単一クラスにして、事故らないようにきれいに処理したほうがいいですね。
これは純粋に実装上の問題なので、最終的にリファクタリングで対応していく方針です。

# 参考URL

https://techblog.gmo-ap.jp/2020/12/09/understanding-prebidjs-gam/

（´-`）.｡oO（実装時に知っておけばよかった、というレベルの神記事です）