Next.jsで作成したサイトでGoogle AnalyticsのUAとGA4を併用する

2021-09-10 (Fri)
Next.jsでGoogle AnalyticsのUAとGA4を併用する際の注意点

Table of Contents

はじめに

今回も自作ブログの改善に関するお話です。

ブログへのアクセスを解析するため、Google Analyticsを導入しました。
Next.jsでGoogle Analyticsを使用するとき、特にUAとGA4を併用する場合には注意すべき点がありますので、その点についてまとめてみました。

今後、Next.jsでサイト構築する人の参考になれば幸いです。

UAとGA4について

Google Analyticsは言わずとしれたWebページのアクセス解析サービスです。
Google Analytics自体については調べればいくらでも情報が出てくるので、本記事ではこれ以上の説明はしません。

現在、Google Analyticsでは以下の2つのバージョンのものが提供されています。

  • Universal Analytics (UA)
  • Google Analytics 4 (GA4)

以前から提供されていたバージョンがUAで、GA4が2020年10月からサービスを開始した新バージョンのようです。

「じゃあ新バージョンを使えばいいんでしょ?」と思いがちですが、GA4は根本から設計が変わっており、まだ十分に成熟していないようです。
しかし、今後はGA4に移行していくことは明らかであるため、UAを使っていれば良い、ということでもなさそうです。

このような背景で、欲張りな人の一つの選択肢として、UAとGA4を併用するという方法があります。
従来のUAを使いつつ、今後の移行も見据えてGA4にもデータを蓄積しておく、という作戦です。
私は欲張りなので、この作戦で行くことにしました。

Next.jsでGoogle Analyticsを使う

Google Analyticsを導入する場合、以下のScriptを<Head>内に記載することで、トラッキングを行います。

HTML
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=(測定ID)"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag() {
    dataLayer.push(arguments);
  }
  gtag('js', new Date());

  gtag('config', '(測定ID)');
</script>

しかし、Next.jsでGoogle Analyticsを使用する場合には少し注意すべきポイントがあります。

注意点1: ページ遷移を自動ではカウントしない

Next.jsでは、ページ遷移時にJavaScriptでURLを変更します。
そのため、最初にサイトにアクセスした際は<Head>内のScriptによりアクセスを検知しますが、そこから別のページに遷移したときにはそれを検知できません。
したがって、ページ遷移時にはこちらから明示的に伝えてあげる必要があります。
これはnext/routerを使うことで実現できます。

実際の実装についてはあとでまとめて記載します。

注意点2: GA4ではページ遷移も自動で検知してくれる

私はここで思いっきりハマりました…

UAとは異なり、GA4ではJavaScriptによりページ遷移でも、それを自動で検知してくれるようです。
はじめはGA4でもUAと同様にnext/routerを使って、ページ遷移時にGoogle Analyticsに通知していました。
しかし、実際に動作を確認すると、一度のページ遷移で2回分のアクセスとしてカウントされていることに気づきました。
これはダブルカウントと呼ばれるもののようです。
これでは正確なアクセス数を知ることができなくなってしまいます。

Next.jsでGoogle Analyticsを使用する方法に関する記事はネット上にそこそこあり、注意点1にはすぐにたどり着くことができました。
しかし、こちらについては明確に説明している記事は現状ないと思います。
動作確認を慎重に行っていないと気づけないため、ダブルカウントのまま運用している人も多いのではないかと推察します。

実装例

前述の注意点を踏まえて、以下のように実装しました。
なお、実装にあたっては以下の記事を参考にしております。

環境変数の設定

まずは環境変数として、UAとGA4それぞれの測定IDを記述します。

.env.local
NEXT_PUBLIC_UA_ID=(UAの測定ID)
NEXT_PUBLIC_GA4_ID=(GA4の測定ID)

もちろん、これはローカル環境での環境変数の設定なので、本番環境としてはVercelなどのデプロイ先で環境変数として設定します。

この環境変数を読み込むファイルを作成します。
また、あとで使用するため、ページアクセスをGoogle Analyticsに伝える関数pageviewも実装しておきます。

gtag.tsx
export const GA4_TRACKING_ID = process.env.NEXT_PUBLIC_GA4_ID;
export const UA_TRACKING_ID = process.env.NEXT_PUBLIC_UA_ID;

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

このとき、gtagの型定義がないため、エラーが出ます。
@types/gtag.jsをインストールすることで解消できます。

yand add -D @types/gtag.js

Headタグ内の記述

次に、<Head>に記述する部分を実装します。

GoogleAnalytics.tsx
import { GA4_TRACKING_ID, UA_TRACKING_ID } from "src/libs/gtag";

export const GoogleAnalytics = () => {
  let scriptText = `
    window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}
    gtag('js', new Date());
  `;
  if (GA4_TRACKING_ID) {
    scriptText += `
      gtag('config', '${GA4_TRACKING_ID}', {
        page_path: window.location.pathname,
      });
    `;
  }
  if (UA_TRACKING_ID) {
    scriptText += `
      gtag('config', '${UA_TRACKING_ID}', {
        page_path: window.location.pathname,
      });
    `;
  }
  return (
    <>
      {(GA4_TRACKING_ID || UA_TRACKING_ID) && (
        <>
          <script
            async
            src={`https://www.googletagmanager.com/gtag/js?id=${GA4_TRACKING_ID}`}
          />
          <script
            dangerouslySetInnerHTML={{
              // eslint-disable-next-line @typescript-eslint/naming-convention
              __html: scriptText,
            }}
          />
        </>
      )}
    </>
  );
};

これを_document.tsxで呼び出します。

_document.tsx
import Document, { Head, Html, Main, NextScript } from "next/document";
import { GoogleAnalytics } from "src/component/GoogleAnalytics";

export default class MyDocument extends Document {
  render() {
    return (
      <Html lang="ja">
        <Head>
          <GoogleAnalytics />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

これで最初にサイトにアクセスしたときのトラッキングはできるはずです。

UAでのページ遷移時対応

最後にUAでトラッキングするため、JavaScriptでのページ遷移時でもアクセスを検知できるようにします。
具体的には、next/routerでページ遷移を検出し、先ほど実装したpageview関数を実行します。

usePageView.ts
import { useRouter } from "next/router";
import { useEffect } from "react";
import { pageview, UA_TRACKING_ID } from "src/libs/gtag";

export const usePageView = () => {
  const router = useRouter();

  useEffect(() => {
    if (UA_TRACKING_ID) {
      const handleRouteChange = (
        path: string,
        { shallow }: { shallow: boolean }
      ) => {
        if (!shallow) {
          pageview(path);
        }
      };

      router.events.on("routeChangeComplete", handleRouteChange);
      return () => {
        router.events.off("routeChangeComplete", handleRouteChange);
      };
    }
  }, [router.events]);
};

これを_app.tsxで呼び出せばOKです!

_app.tsx
import { AppProps } from "next/app";
import { usePageView } from "src/hooks/usePageView";

const App = ({ Component, pageProps }: AppProps) => (
  usePageView();

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

export default App;

残課題

前述の実装で、Next.jsでGoogle AnalyticsのUAとGA4を併用するという目標は達成できました。
しかし、本当はもう少し改善したかったことがあります。
それは、Scriptの遅延読み込みです。

Next.js 11からnext/scriptが実装され、Scriptを遅延読み込みできるようになりました。
Google AnalyticsのScriptなどは初回のページ読み込み時ではなく、一通りページを読み込んだあとにロードしてくれれば十分です。
その分、ページのレンダリングが早くなり、パフォーマンスが向上します。

こちらの記事ではnext/scriptを用いた遅延読み込みについても記述されていましたが、残念ながら私の環境では正常に動作しませんでした (ページアクセスをカウントしてくれない)。
今後も試してみて、うまくいきましたらまた記事にしたいと思います。

まとめ

なんとか、Google AnalyticsのUAとGA4をNext.jsで併用できるようになりました。
ページアクセスなどを解析することはブログ運営には必須だと思うので、ダブルカウントなどを起こさずに正常にトラッキングできるようにするのは重要だと思います。
参考になる点がございましたら幸いです。

Author Profile
liebe-magi

りーべ / liebe-magi

ものづくりが大好きな自称フルスタック(?)エンジニア。大学・大学院でコンピュータサイエンスを専攻し、現在は某企業の研究所所属。専門は組み合わせ最適化問題や機械学習など。主に使用している言語はPython、JavaScript (TypeScript)、Rust、Go。最近は競技プログラミングに興味を持ち、AtCoderのコンテスト (ABC) に毎週参加中 (現在緑)。趣味はマジック、漫画・アニメ、ゲーム(電源・電源問わず)。