Next.jsでGoogle Optimaizeの実装

6 min read読了の目安(約5500字

Next.jsでGoogle Optimaizeを実装しようとするとgtag.jsを記述しないといけないのですが、公式で記載されているものをそのまま搭載してもPVを取ってくれなかったり、ちゃんとテスト出来ない状態になってしまうのでそれ用に記述を変えてあげないといけないとのことで、やっていきたいと思います。
今回はTypeScriptでの実装になります。

参考にさせていただいたサイト


.envの作成

.envに記載した環境変数はprocess.envでアクセス出来ますので.envにGAのIDを記載します。

NEXT_PUBLIC_GA_TRACKING_ID=<YOUR_GA_ID>

何回か公式サイトを読んで.envに何故書くのか?と思っているのですが(別に普通にexportで呼び出しても使えるのを確認しているので)
サイトアクセスしている間だけアクセスしたいのでグローバル変数として設定しているんだ、と参考サイトを読んで納得しました。
(間違いありましたら指摘お願いします)


gtag.tsxを作成

こちらも参考サイト確認しながら作成します。
ディレクトリ直下に/lib/を作成して配下gtag.tsxを作成します。
GAのトラッキングコードとオプティマイズスニペット用のIDもここで記載します。
ここで2つとも記載するのはIDを別々で管理するよりひとつのファイルで管理するほうが煩雑にならないで良い、という判断です。
ページビューの送信とイベント送信の記述もします。

gtag.tsx
export const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_TRACKING_ID || ''
export const OPT_TRACKING_ID = <YOUR_OPT_ID>;

// https://developers.google.com/analytics/devguides/collection/gtagjs/pages
export const pageview = (url: string) => {
  window.gtag('config', GA_TRACKING_ID, {
    page_path: url,
  })
}

// https://developers.google.com/analytics/devguides/collection/gtagjs/events
type GaEventProps = {
  action: string
  category: string
  label: string
  value?: number
}

export const event = ({ action, category, label, value }: GaEventProps) => {
  window.gtag('event', action, {
    event_category: category,
    event_label: label,
    value: value,
  })
}

_document.tsxへ記述

_document.tsxはmeta要素などheadの中で共通呼び出しをするものなどに使用するのでこちらでスクリプトの記述をします。
gtag.tsxを_document.tsxで呼び出してhead内に呼び出す記述をします。

_document.tsx
import Document, { Html, Head, Main, NextScript } from 'next/document'
import { GA_TRACKING_ID, OPT_TRACKING_ID } from '../lib/gtag'

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    return { ...initialProps }
  }

  render() {
    return (
      <Html lang="ja">
        <Head>
	{/* GA_TRACKING_IDがあればスクリプトを呼び出す記述 */}
          {GA_TRACKING_ID && (
            <>
              <script async={true} src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`} />
              <script dangerouslySetInnerHTML={{
                  __html: `
                window.dataLayer = window.dataLayer || [];
                function gtag(){dataLayer.push(arguments);}
                gtag('js', new Date());
                gtag('config', '${GA_TRACKING_ID}', {
                  page_path: window.location.pathname,
                });`,
                }}
              />
              <script src={`https://www.googleoptimize.com/optimize.js?id=${OPT_TRACKING_ID}`}></script>
            </>
          )}
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default MyDocument

_app.tsxに共通処理を書く

下記の処理を書いていきます。

  • PVをカウントするイベント
  • アクティベーションイベントの処理と発火タイミングを記載

PVはRouterのURL書き換えが完了した時に発火するrouteChangeCompleteイベントというときに発火しないとサイトを回遊したときにちゃんと取ってくれないので設定します。

アクティベーションイベントはオプティマイズの機能で、エクスペリエンスがトリガーされるタイミングつまりABテストが反映されるタイミングを指定できます。
最初はページが読み込まれたときなのですが、これをカスタムイベントにしないとSPAの仕様上
ABテスト反映→オリジナル→ABテスト反映となってしまい正常な数値が取れなかったり、新しく追加したABテスト用の文言が反映されなかったりと正常な数値が取れないABテストの表示がうまくいかないなど不具合が出ますので発火タイミングをカスタムイベント後に設定してあげる必要があります。

まずオプティマイズ側のアクティベーションをカスタムイベントにします。

その後_app.tsxにふたつのイベントを記載します。

Googleの公式サイトにカスタムイベントをするときの記述がありますがそれをそのまま入れてもTypeScriptにてプロパティ 'dataLayer' は型 'Window & typeof globalThis' に存在しません。というのが表示されます。

 window.dataLayer.push({ event: "optimize.activate" });

このWEB上のグローバル変数を動かせる記述はあるのですがあちこちで動かせるように記載すると保守性が下がるのでラッパーのところで記載してあげます。

declare const window: Window['window'] & {
  dataLayer: object[];
};

上記の記載をしてあげることでこの内部だけでグローバル変数を動作するようにすることができます。

_app.tsx
import '../styles/globals.css'
import { useRouter } from "next/router";
import * as gtag from "../lib/gtag";
import { useEffect } from "react";

declare const window: Window["window"] & {
  dataLayer: object[];
};

function MyApp({ Component, pageProps }) {

  const router = useRouter();
  const pathName = router.pathname;
  // PVイベント用
  useEffect(() => {
    const handleRouteChange = (url: string) => {
      gtag.pageview(url)
    }

    // ルートの変化が完了したときに発火
    router.events.on('routeChangeComplete', handleRouteChange)
    return () => {
      router.events.off('routeChangeComplete', handleRouteChange)
    }
  }, [router.events])

  // カスタムイベント用
  useEffect(() => {
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push({ event: "optimize.activate" });
  }, [pathName]);

  return <Component {...pageProps} />
}

export default MyApp


これでNext.jsでGoogle Optimaizeの実装が完了です!
インストールされているか確認して成功していれば問題なくABテストを開始することが出来るはずです!

一応Githubにサンプルを出していますのでよければ参考にしてください。
サンプル


分からないなりに調べながらやりましたが、
すごく時間かかりましたが結構かんたんに搭載できる!!
とそう思ったのですが結構調べるのに苦労したので参考になればなと思います。