싱글 페이지 응용프로그램에 구글 애널리틱스 적용

구글 애널리틱스 Google Analytics는 웹 응용프로그램의 사용 데이터를 수집에 많이 사용됩니다.

웹사이트 이용에 대한 추적 데이터를 기반으로 무엇을 어떻게 … 가 시작될 수 있습니다.

구글 애널리틱스에서 기본적으로 제공하는 코드는 웹 응용프로그램 특정 페이지에 요청을 보내면 응답하는 페이지에 코드를 추가해서 처리가 가능합니다.

그런데, 싱글 페이지 응용프로그램이라면 어떻게 클라이언트 경로 이동을 추적할 수 있을지에 대한 내용을 간략하게 작성했습니다.

구글 애널리틱스 속성 추가

구글 애널리틱스에 추적할 웹 응용프로그램에 대한 속성을 추가합니다.

추적 속성을 추가하면 범용 사이트에서 사용가능한 추적코드를 얻을 수 있습니다.

추적코드

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-XXXXXXXX-X"></script>

<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-XXXXXXXX-X');
</script>

범용 사이트 태그(gtag.js)에 대한 설명에 의하면, 위 코드를 추적할 모든 웹페이지의 <head>에 첫번째 항목으로 붙여넣으면 됩니다.

그런데, SPA 의 경우 실제 페이지는 하나만 존재합니다.

클라이언트 코드에서 경로를 변경한 경우에도 추적코드를 실행해야 구글 애널리틱스에서 유효한 통계자료를 얻을 수 있습니다.

측정: gtag.js

측정 관련 API 는 Google Analytics Measurement gtag.js 페이지에서 찾을 수 있습니다.

클라이언트 측 경로 탐색시 경로 이동 추적을 위한 기능 확인은 Pageviews 관련 API 에서 찾을 수 있습니다.

config 명령으로 경로 탐색에 대한 추적이 가능합니다.

gtag('config', 'GA_MEASUREMENT_ID', { /* pageview parameters */});

Pageview parameters 의 정의는 아래와 같습니다.

interface IPageviewParameters {
    page_title?: string;
    page_location?: string;
    page_path?: string;
}

예제

공통

예를 들어 https://blog-service.bbon.me/users/@bbonkr/posts/hello-world 페이지로 이동하면 페이지가 로드된 후 아래 코드가 실행되면 되겠습니다.

gtag('config', 'UA-XXXXXXXX-X', {
        page_title: 'Hello world',
        page_location: 'https://blog-service.bbon.me/users/@bbonkr/posts/hello-world'
        page_path: '/users/@bbonkr/posts/hello-world'
});

페이지 제목은 window.document.title 속성으로 찾을 수 있습니다.

페이지 전체 경로는 window.location.href 속성으로 찾을 수 있습니다.

페이지 경로는 window.location.pathname 속성으로 찾을 수 있습니다.

next.js + React 로 작성된 웹 응용프로그램

편리한 SSR Server-side Rendering , 편리한 클라이언트 경로 처리 등의 이유로 next.js 와 React 로 작성된 웹 응용프로그램의 경우 아래와 같이 처리할 수 있습니다.

_document 를 재작성해서 _gtag.js 파일을 포함하도록 코드를 작성합니다.

ga.ts

export const gaTraceId: string = 'UA-XXXXXXXX-X';

document.tsx

import { gaTraceId } from './ga'

class MyDocument extends Document<IMyDocumentProps> {
    // 생략
    addGoogleAnalyticsScript(){
        return {
            __html: `
              window.dataLayer = window.dataLayer || [];
              function gtag(){dataLayer.push(arguments);}
              gtag('js', new Date());

              gtag('config', '${gaTraceId}');

            `,
        };
    }
    // 생략
    render() {
        const gaTraceId = 'UA-XXXXXXXX-X'
        return (
            <html lang='ko'>

                <Head>
                {/* ... */}
                </Head>
                <body>
                    <Main />
                    <NextScript />
                    <script async src={`https://www.googletagmanager.com/gtag/js?id=${gaTraceId}`}></script>
                                <script
                                    dangerouslySetInnerHTML={this.addGoogleAnalyticsScript()}
                                />
                </body>
            </html>
        );
    }
}

모든 페이지에 포함되는 컴포넌트를 작성합니다.

import React, {
    FunctionComponent,
    useEffect,
} from 'react';
import Router from 'next/router';
import { gaTraceId } from './ga'

export interface IAppLayourProps {
    children: React.ReactNode;
};

export const AppLayout: FunctionComponent<IAppLayoutProps> = ({children}) => {
    useEffect(() => {
        const handleRouteChangeComplete = (url) => {
            if(typeof window === 'object'){
                const { title } = window.document;
                const { href, pathname } = window.location;

                window.gtag('config', gaTraceId, {
                    page_title: title,
                    page_location: href,
                    page_path: pathname,
                });
            }            
        };

        Router.events.on('routeChangeComplete', handleRouteChangeComplete);

        return () => {
            Router.events.off('routeChangeComplete', handleRouteChangeComplete);
        };
    }, [])

    return (
        <div>
            {children}
        </div>
    );    
};

위와 같이 컴포넌트로 분리하면 모든 페이지 컴포넌트에서 Pageviews 측정을 위한 코드를 추가하지 않고 처리할 수 있습니다.

next.js 의 경로 처리 이벤트는 https://github.com/zeit/next.js/#router-events 페이지에서 확인할 수 있습니다.

useEffect 또는 componentDidMount 함수에서 window 객체의 속성값을 참조할 때, 문제가 있는 경우 setTimeout 함수로 조금 후에 실행되도록 조정할 수 있습니다.

댓글 남기기