Oteto Blogのロゴ

【Next.js】SPAにGoogle Analytics(GA4)を導入する

1. トラッキングIDを取得

自身のサイトのトラッキングを行うために、まずはGoogle Analyticsから発行されるトラッキングIDを取得する必要がある。

方法に関しては以下の記事が参考になった。

【画像で解説】GA4でトラッキングIDを発行して設置する手順 | ナイルのマーケティング相談室

2. 環境変数に登録

NEXT_PUBLIC_GA_ID = "G-xxx"

.env.productionに取得したIDを追記する(ファイル未作成の場合は新規作成)。

NEXT_PUBLIC_GA_ID = ""

開発環境での計測は不要なため、.env.developmentでは空文字を指定する。

3. next/scriptを設置

"use client";
import Script from "next/script";

const ScriptGa = () => {
  return (
    <>
      <Script
        defer
        id="ga-connect"
        src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GA_ID}`}
      />
      <Script
        defer
        id="ga-track"
        dangerouslySetInnerHTML={{
          __html: `
        window.dataLayer = window.dataLayer || [];
        function gtag() {
          dataLayer.push(arguments);
        }
        gtag("js", new Date());
        gtag("config", '${process.env.NEXT_PUBLIC_GA_ID}');
        `,
        }}
      />
    </>
  );
};

export default ScriptGa;

上記のようにScriptGa.tsxを作成し、bodyタグをレンダリングしているコンポーネント(layout.tsxなど)内で呼び出せば完了。

  • 外部スクリプトの読み込みを最適化してくれるnext/scriptを利用
    • id属性には一意な値を指定
    • strategyはデフォルトでafterInteractive(ハイドレーション後に実行される)になるため指定は不要
  • process.env.NEXT_PUBLIC_GA_IDが空文字(開発環境)の時はそもそも何もレンダリングしないという方法もあるが、後述する遅延読み込みの自動テストを開発環境でも実行したいためその方法にはしていない

ちなみにSPAだとしても、ページ遷移の度にpageviewイベントを送信するといった処理は実装不要。どうやらGA4の設定で拡張計測機を有効にしておけば、historyを参照して<Link>タグを利用したclient-routingであってもイベントを送信してくれるらしい。

4. 遅延読み込みさせる(任意)

先ほどの方法だと単純に外部のスクリプトを読み込むので、当然初回読み込み時のパフォーマンスが下がってしまうという懸念点がある。

そこで当サイトの場合は、下記の理由からそれらのスクリプトを初回スクロール時に遅延読み込みすることにした。

  • アクセス後すぐに直帰したユーザーは計測対象に含めなくてよい(そこまで厳密に計測する必要がない)
  • 単純に初回表示速度を上げたい
"use client";
import { useEffect, useState } from "react";
import Script from "next/script";

const ScriptGa = () => {
  const [hasMovedMouse, setHasMovedMouse] = useState(false);

  useEffect(() => {
    window.addEventListener("scroll", insertScriptGa);
  }, []);

  const insertScriptGa = () => {
    window.removeEventListener("scroll", insertScriptGa);
    setHasMovedMouse(true);
  };

  if (!hasMovedMouse) return <></>;

方法としては上記のように、初回スクロール後に初めてscriptタグをレンダリングするように修正するだけ。これでユーザーがページを読み進めない限り、トラッキングのためのスクリプトが実行されない。

import { fireEvent, render, waitFor } from "@testing-library/react";
import ScriptGa from "@/app/__component/scriptGa";

describe("ScriptGaコンポーネント", () => {
  test("スクロールしないとscriptタグがレンダリングされない", async () => {
    render(<ScriptGa />);

    await waitFor(() => {
      expect(document.querySelectorAll("script")).toHaveLength(0);
    });
  });

  test("スクロールするとscriptタグがレンダリングされる", async () => {
    render(<ScriptGa />);
    fireEvent.scroll(window, { target: { scrollY: 100 } });

    await waitFor(() => {
      expect(document.querySelectorAll("script")).toHaveLength(2);
    });
  });
});

念の為最低限の自動テストを書いたが無事通った。