スマホ 1 台を回して 3〜8 人で遊ぶパーティーゲームのサイト (mawashite.com) を 1 人で運営しています。サーバーを持たない静的サイトなのでアクセスログがなく、ページビューだけでは「開いた」と「遊びきった」の区別が付きません。どのゲームが最後まで遊ばれているのかを知るために、GA4 のイベントを設計し直しました。
その途中で 1 つやらかしました。イベントに載せた人数などの数値パラメータがレポートで内訳に分解できず、「数値で送ったのが原因だ」と結論して文字列に変えたところ、内訳は出るようになりました。ところが数週間後、この記事を書くために公式ヘルプを読み直したら、間違っていたのは結論のほうでした。対処は効いたのに、効いた理由を取り違えたまま、その理由をコードのコメントにまで書き残していた——対処が効いたことは、理由が合っていたことの証拠にならない。これがこの記事でいちばん伝えたいことです。
以下、イベントの設計、理由を取り違えた経緯、そこから引いた検証の一般則の順で書きます。個人開発でアクセス解析を入れる人向けです。
設計: イベントは「答えたい問い」の数だけ
イベント名から考えると際限なく増えるので、「これが分かれば判断できる」という問いから逆に決めました。遊びに関わる部分で送っているのは 6 つです。
game_start— 紹介ページを見た人のうち何人が実際に始めたかgame_complete— 最後まで遊びきった割合play_again— お題が面白かったかの、いちばん素直な代理指標share_result— 遊び終わった人の拡散share_site— まだ遊んでいない人の誘いnav_click— どの導線からどこへ回遊したか
share_result と share_site を分けているのは、同じ共有でもコンバージョンの前と後にあたるからです。プレイヤー名はどのイベントにも含めません。共有テキストには名前が入りますが、OS の共有シートに渡るだけで解析側には届かない設計にしています。
このうち nav_click は自前実装です。GA4 の拡張計測 (設定だけでクリックやスクロールを自動収集する機能) が拾うのは外部リンクだけで、サイト内のどのリンクが押されたかは分からないためです。実装で効いた判断は 2 つあります。1 つは、計測用の属性をリンク 1 本ずつではなく、導線のまとまり (ヘッダー・フッター・本文中のおすすめ枠のような、リンクを束ねる単位) に付けることです。1 本ずつ付けると、後からリンクを足したときの付け忘れで数字が黙って減ります。もう 1 つは、イベント名を導線ごとに増やさず、1 つの nav_click のパラメータで分けることです。イベント名を分割すると、導線全体の合計が一発で取れなくなります。game_complete のほうは結果画面のリロードで二重に積まれるので、送信済みかをセッションに残して 1 ゲーム 1 回に絞りました。
事例: 数値パラメータをやめたら直った。理由は取り違えていた
game_start には人数とラウンド数を載せていて、最初は素直に数値で送っていました。ところがレポートを開いても「3 人プレイが何割か」という内訳が出ません。出るのは合計と平均だけです。
前提を 2 つ補足します。GA4 のイベントパラメータは、送るだけではレポートに現れません。管理画面 (カスタム定義) で登録して初めて集計され、登録先は 2 種類あります。カスタムディメンションに登録すると値ごとの内訳が出て、カスタム指標に登録すると合計や平均が出ます (排他ではなく、同じパラメータを両方に登録することもできます)。つまり「合計と平均しか出ない」という症状は、送り方と登録先のどちらが原因でも起こりえます。
当時の僕はこう結論しました。gtag は数値と文字列を別のキーで送る (開発者ツールの Network を見ると、文字列のパラメータは ep.、数値のパラメータは epn. という接頭辞のキーに分かれています)。数値のほうは指標に紐づき、指標は合計と平均しか出せない。だから数値で送ったぶんは内訳に分解できないのだ、と。対処として String() で包み、同じ間違いを繰り返さないよう送信関数の引数の型から number を落としました。
export function trackEvent(name: string, params?: Record<string, string>): void {
if (typeof window === 'undefined') return;
window.gtag?.('event', name, params);
}
内訳は取れるようになりました。ここまでが「対処が効いた」です。
数週間後、この記事を書くために公式ヘルプを読み直して、結論のほうが間違っていたことに気づきました。
イベント パラメータの整数値は、アプリのイベント スコープのカスタム ディメンションでは解析されませんが、ウェブイベントでは解析されます。そのため、数値ではなく英数字の値を追加することをおすすめします。
ウェブなら、整数値で送ったパラメータもカスタムディメンションとして登録でき、内訳は出るのです。では当時なぜ出なかったのか。症状が「合計と平均は出る」だった以上、送ったデータは届いて処理されていたはずで、残る説明は「ディメンションではなく指標として登録していた」です。ただし登録は過去に遡って効かないので、当時のデータで確かめる手段はもうありません。
それでも文字列で送り続けています。引用のとおり公式が英数字値を勧めていて、文字列に寄せればアプリとウェブの挙動差も、指標に登録するという誤り方も、型の時点で消えるからです。処方は変えず、コメントに書いた理由だけ差し替えました。
考察: 「効いた」と「理由が合っている」を分けて扱う
この事例の教訓は、対処が効いたことを、理由が合っていた証拠として扱わないことです。症状「内訳が出ない」の原因候補は「数値で送ったから」と「指標として登録したから」の 2 つあり、僕の対処は送り方と登録先を同時に変えていました。症状は消えましたが、どちらが原因だったのかは消えた後も分かっていません。原因を言い当てたければ、候補を 1 つずつ切り替えて確かめる必要があります。今回は処方が同じ場所に着地したので実害がありませんでしたが、理由を間違えて覚えた処方は、条件が変わったときに間違った応用を生みます。
計測にはもう 1 つ、同型の罠があります。「届いた」と「処理された」の混同です。GA4 の計測エンドポイントは不正なペイロードにも 204 を返すので、204 は「届いた」証拠であって「処理された」証拠ではありません。さらに GA4 は既知のボットからのヒットを既定で除外し、この除外は無効化も量の確認もできないと公式ヘルプ (既知の bot トラフィックの除外) に明記されています。実際、ヘッドレスブラウザから送ると 204 が返るのにレポートには一切現れませんでした。判定に使うボットのリストは非公開なので、理由の特定はここでもできません。分かるのは結果だけです。
なので検証は層で分けています。送信の成否は自動テストでは見ず、重複排除やパラメータ組み立ての純関数だけをテストする。実際に処理されたかは、実機の通常ブラウザから送って GA4 の DebugView で 1 件ずつ見る。あわせて、動作確認のたびに本番の数字が汚れないよう、実行時にホスト名を見て本番ドメイン以外では計測タグ自体を読み込まないようにしています。
まとめ
パラメータを文字列に寄せる処方自体は、公式の推奨どおりでお勧めできます。持ち帰ってほしいのはその奥で、対処の有効性と理由の正しさは別々に検証が要ること、そして計測の確認は「届いた (204)」「処理された (DebugView)」「理由が分かっている (候補を 1 つずつ潰した)」の 3 段で別物だということです。パラメータは登録するまでレポートに出ず、登録は遡及しないので、送り始める前に登録まで済ませておくと過去データを失いません。この計測が数えている現物は 知ったかリレー (実在しないお題を知ったかぶりの解説でつなぐゲーム) などで遊べます。