はじめに
GA4 を利用するため、Cookie Consent Banner を自前で埋め込んだ。本来?、Cookie を利用する場合、自前での対応はほぼ不可能であるため、Consent Management Platform(CMP)を利用すべきであるが、それだと JavaScript 埋め込むだけなので、今回は自前での実装を行う。
CMP を利用するか否かは、
”訴えられない”ように Google Analytics を使う方法を考える
を参考に、慎重に検討する必要がある。
手順
Cookie Notice 等は既に用意されているものとする。
- GA4 のスクリプトを default では送信しないように埋め込む
- Cookie Conset Banner を埋め込み、同意状態を LocalStorage に保存する
- 同意が取れたら送信する
今回は、安全第一で、同意が取れなければ送らないという方針にしているが、対応する国やエリアによっては、拒否されるまでは取って良かったりなどあるので、どこ向けかで方針は変わる。いずれにしても、Cookie 同意の対応は、今回紹介する Banner 埋め込みが2割、ポリシーが8割(こっちが無理)という感じだ。
Cookie 利用に同意するユーザは 3 割程度らしいので、なんとかデータをたくさん取りたいマーケター的には、拒否されるまで取って良いところでは、取って欲しいと言うに違いない。
GA4 のスクリプトを埋め込む
Next.js には、3rd Party 用のライブラリがあり、ただ埋め込むだけなら簡単だ。
import { GoogleAnalytics } from '@next/third-parties/google'
export default function Page() {
return <GoogleAnalytics gaId="G-XYZ" />
}
@next/third-paties
では、Google Tag Manager, Google Analytics, Google Maps, YouTube
などが上記のようなお手軽さで利用できる。ただし、このライブラリは現在 Experimental であり、今回やりたいことはできないので、残念ながら使えない。今後に期待。詳細は、こちら を参照。
埋め込むべきタグは、基本のタグ、
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-S84EEZJCTF"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXX');
</script>
これに、同意ステータスのデフォルトを追加したものになる。
gtag('consent', 'default', {
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'analytics_storage': 'denied'
});
region
オプションなどもあるので、エリアごとのデフォルト値が設定可能だ。詳細は、同意設定を管理する(ウェブ) を参照。できることは日々変わっているので、定期的に確認を。
Next.js では、サードパーティのスクリプトをロードしたり、インラインスクリプトを読み込むには、next/script
を使う。strategy
オプションで、ロードのタイミングを調整できる。詳細は以下を参照。
以上を踏まえ、GA4 コンポーネントは以下のようになる
'use client';
import Script from 'next/script';
type Props = {
gaId: string;
};
export default function GA4({ gaId }: Props) {
return (
<>
<Script
strategy="afterInteractive"
src={`https://www.googletagmanager.com/gtag/js?id=${gaId}`}
/>
<Script
id="google-analytics"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('consent', 'default', {
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'analytics_storage': 'denied'
});
gtag('config', '${gaId}', {
page_path: window.location.pathname,
})
`,
}}
/>
</>
);
}
これを、今回は全てのページに埋め込みたいので、RootLayout に埋め込む。読み込みは、strategy
オプションで調整されるので、どこに配置するかは気にしなくて良い。afterInteractive
(default) は、Next.js のコードの邪魔をしない。
Cookie Consent Banner を埋め込む
まず、同意状態を LocalStorage に保存するので、get, set, remove の helper を作っておく。
import 'client-only';
export function getLocalStorage(key: string, defaultValud: any) {
const stickyValue = localStorage.getItem(key);
return stickyValue !== null && stickyValue !== 'undefined'
? JSON.parse(stickyValue)
: defaultValud;
}
export function setLocalStorage(key: string, value: any) {
localStorage.setItem(key, JSON.stringify(value));
}
export function removeLocalStorage(key: string) {
localStorage.removeItem(key);
}
client-only
パッケージはインストールが必要。
CookieConsentBanner コンポーネントのスケルトンは、以下のようになる
'use client'
export default function CookieConsentBanner() {
// ユーザの同意状態
const [consentMode, setConsentMode] = useState<ConsentModeType | null>(null);
// LocalStorage から同意状態をロード
useEffect(() => {...}, [setConsentMode]);
// 同意状態が変わったら、update イベントを送信
useEffect(() => {...}, [ConsentMode]);
// ボタンが押されたら、LocalStorage, State を更新
const setCookieConsent = (value: boolean) => {...}
return <div>...</div>
ユーザの同意状態
// ユーザの同意状態
const [consentMode, setConsentMode] = useState<ConsentModeType | null>(null);
type ConsentModeType = {
ad_storage: 'granted' | 'denied' | undefined;
ad_user_data: 'granted' | 'denied' | undefined;
ad_personalization: 'granted' | 'denied' | undefined;
analytics_storage: 'granted' | 'denied' | undefined;
};
同意も拒否もしていない状態を null
とする。
表示部分
return (
<div
className={clsx(styles.cookieConsentBanner, {
[styles.cookieConsentBannerHidden]: consentMode !== null,
})}
>
<p>
当社サイトでは、Cookieを使用しています。各規約をご確認の上ご利用ください: <br />
<Link href="/cookies">Cookie Policy</Link>,
<Link href="/privacy">Privacy Policy</Link> および
<Link href="/terms">Terms of Use</Link>
</p>
<div className={styles.buttons}>
<button
className={styles.cookieDecline}
onClick={() => setCookieConsent(false)}
>
拒否する
</button>
<button
className={styles.cookieAccepted}
onClick={() => setCookieConsent(true)}
>
Cookie を受け入れる
</button>
</div>
</div>
)
}
選択同意は今回はなしにした。必要な場合は、選択させるバルーンなどを設置したりなどが必要。同意または拒否済みの場合は、表示しないように style を調整した。
初期状態のロード
// LocalStorage から同意状態をロード
useEffect(() => {
const storedConsentMode = getLocalStorage('consentMode', null);
setConsentMode(storedConsentMode);
}, [setConsentMode]);
ここでは LocalStorage から読み込んだものを State に入れているだけだが、ポリシーを更新した場合や、同意から一定期間経過した場合や、LocalStorage に保存されている内容が不適切な場合などに、同意を取り直すようにしたい場合は、その内容をここに書く。同意したポリシーのバージョンや、同意日時などは、LocalStorage に一緒に入れておけば良い。
LocalStorage/State の更新
// ボタンが押されたら、LocalStorage, State を更新
const setCookieConsent = (value: boolean) => {
const newValue = value === true ? 'granted' : 'denied';
const newConsentMode: ConsentModeType = {
ad_storage: newValue,
ad_user_data: newValue,
ad_personalization: newValue,
analytics_storage: newValue,
};
setLocalStorage('consentMode', newConsentMode);
setConsentMode(newConsentMode);
}
前述の通り、選択同意はしないので、全同意か全拒否のどちらかになっている。だったら、LocalStorage にも true/false
を入れておけば良いのでは?と思うが、その通りである。その通りであるが、将来的に選択同意のロジックを入れるかも知れないし、ポリシー同意のバージョン、日時も入れておきたいので、このようにしてある。
update イベントを送信
// 同意状態が変わったら、update イベントを送信
useEffect(() => {
if (consentMode !== null) {
window.gtag('consent', 'update', {
ad_storage: consentMode.ad_storage,
ad_user_data: consentMode.ad_user_data,
ad_personalization: consentMode.ad_personalization,
analytics_storage: consentMode.analytics_storage,
});
}
}, [ConsentMode]);
これで、同意していればデータ取得が開始し、拒否していればデータ取得は開始しない。同意も拒否もしていなければ、呼び出されない(ので取得されない)。
RootLayout に埋め込む。
まとめ
今回追加・修正したファイルは以下の通り。
src/app/
├── lib
│ └── storageHelper.ts
├── components
│ ├── CookieConsentBanner.tsx
│ └── GA4.tsx
└── layout.tsx
実際にこの方法で埋め込んだ Lang x Lang では、
- 同意から一定期間が経過
- ポリシーの変更があった
- LocalStorage に保存された内容が不正
な場合に、同意を取り直すようにしている(ここで、removeLocalStorage
を使う)。ただし、近い将来 CMP を利用する予定なので、これは一時的な対応だ。実際の動作は Lang x Lang で確認できる。Local Storage の中身を確認してみると良い。
繰り返しになるが、Cookie Consent Management は自前での対応はほぼ不可能なので、今回紹介した方法は、真っ黒をグレーに変える程度の対応である。
なお、実際にこれで運用してみて、データはきちんと取れてはいるが、同意率3割程度というのは、まぁだいたいそんなものかなという感じだ。うざすぎるバナーで離脱説も納得だが、ユーザにとって Essential Cookie 以外、受け入れるメリットがない。ユーザはちゃんと考えてるということですね。
(おしまい)