Rechart를 활용한 GA4 데이터 시각화 구현하기
루커스튜디오(Looker Studio) 및 태블로(Tableau)등에서 구글 애널리틱스 데이터를 연동하여 시각화 대시보드를 구현하는 것은 생산성 측면에서 가장 용이한 방법이라고 생각합니다. 그런 점에서 시각화 대시보드를 개발할 경우 목적과 활용(사용자)방법을 고려하는 것이 중요합니다. 특히 개발을 통해 시각화 대시보드를 구현할 경우 개발에 소요되는 리소스, 시간, 주사용자 등의 요소를 감안하여 공통적인 거시지표전달을 목적으로 사용하는 것을 권장드립니다.
개발환경 설정
저는 yarn을 패키지 관리나 도구로 사용하여 프로젝트를 생성하였습니다. yarn을 사용하지 않는 경우 npm을 그대로 사용하셔도 됩니다.
프로젝트 생성
yarn create react-app <your-app-name>
리액트 최신 문서의 프로젝트 생성 방법이 다를 수 있으니, 이 부분 감안하여 테스트에 참고하시기 바랍니다.
GCP 환경 설정
OAuth 동의 화면: API 및 서비스 > OAuth 동의화면을 선택한 다음 OAuth 동의 화면 설정에 필요한 값을 등록합니다.
앱 도메인에는 테스트를 위해 아래와 같이 모두 로컬호스트 주소를 입력하였습니다.
사용자 인증 정보에도 설정을 진행합니다.
사용자 인증 정보에서 생성된 클라이언트 ID는 GA4 연동 시 필요한 정보입니다.
관련 모듈 설치
Rechart 설치
yarn add rechart
gapi-script 설치
yarn add gapi-script
React 컴포넌트 개발
컴포넌트에 Google Login / Logout 기능과 구글 애널리틱스 데이터를 연동하는 기능을 구현합니다.
모듈 및 구글 연동에 필요한 ID 정보 선언
- CLIENT_ID: GCP 사용자 인증정보에서 확인 (위 내용 중 GCP 환경 설정 참고)
- DISCOVERY_DOCS 및 SCOPES: 'API 사용해보기' 설정 참고
- PROPERTY_ID: 구글 애널리틱스 > Admin > Property > Property Settings에서 복사
import React, { useState, useEffect } from 'react';
import { LineChart, AreaChart, XAxis, YAxis, CartesianGrid, Tooltip, Legend, Area, Line, ResponsiveContainer } from 'recharts';
import { gapi } from 'gapi-script';
const CLIENT_ID = '<your client id>';
const DISCOVERY_DOCS = ['https://analyticsdata.googleapis.com/$discovery/rest?version=v1beta'];
const SCOPES = 'https://www.googleapis.com/auth/analytics https://www.googleapis.com/auth/analytics.readonly';
const PROPERTY_ID='<your GA4 property id>';
//--- 이하 생략 ---
구글 OAuth 로그인 및 로그아웃 추가
Chart란 이름으로 함수컴포넌트를 만들고 구글 로그인기능을 추가하였습니다. 로그인 상태를 관리하도록 useState에 loggedIn 플래그를 추가하였습니다. useEffect를 선언하여 Chart 컴포넌트 마운트 시 구글 로그인 상태 체크 및 로그인 실행이 되도록 적용하였습니다.
// 선언부 코드 생략
const Chart = () => {
const [data, setData] = useState([]);
const [loggedIn, setLoggedIn] = useState(false);
// 마운트 될 때 로그인 처리하도록 선언
useEffect(() => {
gapi.load('client:auth2', initializeGapi);
}, []);
const initializeGapi = () => {
gapi.client.init({
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES,
})
.then(() => {
setLoggedIn(gapi.auth2.getAuthInstance().isSignedIn.get());
})
.catch((error) => {
console.error('gapi client 초기화 실패:', error);
});
};
// login
const handleLogin = () => {
gapi.auth2.getAuthInstance().signIn().then(() => {
setLoggedIn(true);
})
.catch((error) => {
console.error('Google 인증 실패: ', error);
})
}
// logout
const handleLogout = () => {
gapi.auth2.getAuthInstance().signOut()
.then(() => {
setLoggedIn(false);
setData([]);
})
.catch((error) => {
console.error('로그아웃 중 에러 발생: ', error);
});
}
// GA4 데이터 가져오기
const handleFetchData = () => {
// 생략
}
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<h1>Google Analytics Data API Example</h1>
<div>
{!loggedIn && (
<button onClick={handleLogin}>Login with Google</button>
)}
{loggedIn && (
<div>
<button style={{ marginTop: '10px', marginRight: '10px' }} onClick={handleFetchData}>Fetch Data</button>
<button onClick={handleLogout}>Logout</button>
</div>
)}
</div>
... 생략 ...
</div>
);
};
export default Chart;
GA4 데이터 연동
batchRunReports를 선언합니다. batchRunReports는 GA4 리포트 조회용 함수입니다. 리포트 종류 및 관련 속성에 대해 자세한 정보는 구글 애널리틱스 개발 문서(Google Analytics Data API)에서 확인할 수 있습니다.
- property: GA4 속성(Property)에서 읽어온 property 값
- dimensions: GA4 dimension. 아래 예시에서는 date dimension을 선언했습니다.
- metrics: GA4 metrics. 아래 예시에서는 activeUsers, sessions를 선언했습니다.
- dateRanges: 조회기간. startDate, endDate에는 아래와 같이 동적으로 최근 일주일 또는 지정된 날짜를 지정할 수 있습니다.
const handleFetchData = () => {
gapi.client.analyticsdata.properties.batchRunReports({
property: `properties/${PROPERTY_ID}`,
resource: {
requests: [
{
dimensions: [
{
name: 'date'
}
],
metrics: [
{
name: 'activeUsers'
},
{
name: 'sessions'
}
],
dateRanges: [
{
startDate: '7daysAgo',
endDate: 'yesterday'
}
]
}
]
}
}).then(response => {
const { rows } = response.result.reports[0];
const transformedData = rows.map(row => ({
date: row.dimensionValues[0].value,
activeUsers: parseInt(row.metricValues[0].value, 10),
sessions: parseInt(row.metricValues[1].value, 10)
}));
console.log(transformedData);
setData(transformedData);
}).catch((error) => {
console.error('fetchData 에러 발생: ', error);
})
}
transformedData: GA4 데이터를 받아 전처리한 데이터. Rechart에 조회시 사용합니다.
{date: '20230612', activeUsers: 29, sessions: 37}
Rechart 추가
- XAxis: date(GA dimension) 선언
- YAxis: metrics value
- Area: activeUsers, sessions
const Chart = () => {
// 코드 생략
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
// 코드 생략
{data.length > 0 && (
<div style={{ width: '800px', height: '500px' }}>
<AreaChart width={800} height={500} data={data} margin={{
top: 20, right: 20, bottom: 20, left: 20,
}}>
<defs>
<linearGradient id="colorSessions" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#8884d8" stopOpacity={0.8}/>
<stop offset="95%" stopColor="#8884d8" stopOpacity={0}/>
</linearGradient>
<linearGradient id="colorActiveUsers" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#82ca9d" stopOpacity={0.8}/>
<stop offset="95%" stopColor="#82ca9d" stopOpacity={0}/>
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Legend />
<Area type="monotone" dataKey="activeUsers" stroke="#82ca9d" fillOpacity={1} fill="url(#colorActiveUsers)" />
<Area type="monotone" dataKey="sessions" stroke="#8884d8" fillOpacity={1} fill="url(#colorSessions)" />
</AreaChart>
</div>
)}
</div>
);
};
export default Chart;
전체 소스
const Chart = () => {
const [data, setData] = useState([]);
const [loggedIn, setLoggedIn] = useState(false);
// 마운트 될 때 로그인 처리하도록 선언
useEffect(() => {
gapi.load('client:auth2', initializeGapi);
}, []);
const initializeGapi = () => {
gapi.client.init({
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES,
})
.then(() => {
setLoggedIn(gapi.auth2.getAuthInstance().isSignedIn.get());
})
.catch((error) => {
console.error('gapi client 초기화 실패:', error);
});
};
// login
const handleLogin = () => {
gapi.auth2.getAuthInstance().signIn().then(() => {
setLoggedIn(true);
})
.catch((error) => {
console.error('Google 인증 실패: ', error);
})
}
// logout
const handleLogout = () => {
gapi.auth2.getAuthInstance().signOut()
.then(() => {
setLoggedIn(false);
setData([]);
})
.catch((error) => {
console.error('로그아웃 중 에러 발생: ', error);
});
}
// GA4 데이터 가져오기
const handleFetchData = () => {
gapi.client.analyticsdata.properties.batchRunReports({
property: `properties/${PROPERTY_ID}`,
resource: {
requests: [
{
dimensions: [
{
name: 'date'
}
],
metrics: [
{
name: 'activeUsers'
},
{
name: 'sessions'
}
],
dateRanges: [
{
startDate: '7daysAgo',
endDate: 'yesterday'
}
]
}
]
}
}).then(response => {
const { rows } = response.result.reports[0];
const transformedData = rows.map(row => ({
date: row.dimensionValues[0].value,
activeUsers: parseInt(row.metricValues[0].value, 10),
sessions: parseInt(row.metricValues[1].value, 10)
}));
console.log(transformedData);
setData(transformedData);
}).catch((error) => {
console.error('Error querying data: ', error);
})
}
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<h1>Google Analytics Data API</h1>
<div>
{!loggedIn && (
<button onClick={handleLogin}>Login with Google</button>
)}
{loggedIn && (
<div>
<button style={{ marginTop: '10px', marginRight: '10px' }} onClick={handleFetchData}>Fetch Data</button>
<button onClick={handleLogout}>Logout</button>
</div>
)}
</div>
{data.length > 0 && (
<div style={{ width: '800px', height: '500px' }}>
<AreaChart width={800} height={500} data={data} margin={{
top: 20, right: 20, bottom: 20, left: 20,
}}>
<defs>
<linearGradient id="colorSessions" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#8884d8" stopOpacity={0.8}/>
<stop offset="95%" stopColor="#8884d8" stopOpacity={0}/>
</linearGradient>
<linearGradient id="colorActiveUsers" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#82ca9d" stopOpacity={0.8}/>
<stop offset="95%" stopColor="#82ca9d" stopOpacity={0}/>
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Legend />
<Area type="monotone" dataKey="activeUsers" stroke="#82ca9d" fillOpacity={1} fill="url(#colorActiveUsers)" />
<Area type="monotone" dataKey="sessions" stroke="#8884d8" fillOpacity={1} fill="url(#colorSessions)" />
</AreaChart>
</div>
)}
</div>
);
};
export default Chart;
App.js
Chart 컴포넌트 추가
import React, { useEffect, useState } from 'react';
import { Route, Link, Routes } from 'react-router-dom';
import logo from './logo.svg';
import singularLogo from './img/singular-labs-logo.png'
import './App.css';
import Navbar from './Navbar';
import Events from './Events';
import Chart from './Chart';
function App() {
return (
<div className="App">
<Navbar />
<img src={singularLogo} alt='' style={{height: '20%',width:'20%'}} />
<Routes>
<Route path='/' element={<Events />}></Route>
<Route path='/ga' element={<Chart />}></Route>
</Routes>
</div>
);
}
export default App;
실행해보기
yarn start
실행결과
로그인 완료 후 Fetch Data 실행시 Rechart가 실행되도록 처리하였습니다.
참고자료
Rechart | npmjs
Google Analytics Data API(GA4) 연동하기
'Data & MarTech > Google Marketing Platform' 카테고리의 다른 글
[GA4] 크로스도메인 측정하기 (0) | 2023.06.16 |
---|---|
[GA4] GA4 이벤트(Event) 이해하기 (0) | 2023.06.14 |
[GA4] GTM 서버사이드태깅 - GA4 전자상거래 태그 적용 (0) | 2023.06.08 |
[GTM] GA4 서버사이드태깅(Server side tagging on Google Tag Manager) - 2. GA4 태그 추가 (0) | 2023.05.29 |
Google Analytics Data API(GA4) 연동하기 - 3. gcloud CLI (0) | 2023.05.22 |