gatsby 블로그에 조회수 추가하기
서론
gatsby 는 정적 사이트 생성기로, 훌륭한 블로그 툴입니다. 😍😍
다만 조회수등 여러가지 다이나믹한 기능들은 당연히 되지 않는데요. 물론 언제나 그렇듯 답은 있습니다.

예전 웹 초창기때는 여러 카운터 서비스가 있었을듯, 요즘에는 방문자 분석을 위해서 구글 애널리틱스를 많이 활용하고 있는데요.
저희는 ✨ 구글 애널리틱스 ✨ 를 활용하여, 게시글의 조회수를 조회해보도록 하겠습니다.
API 를 활성하여 보자
https://developers.google.com/analytics/devguides/reporting/data/v1/quickstart-client-libraries
먼저 저 API 문서를 통해서 확인했었는데요. API 부터 활성화 시켜봅시다.

이후에 ✨ DOWNLOAD CLIENT CONFIGURATION ✨ 를 눌러서 credentials.json 파일을 꼭 놓치지 말아주세요.
구글 애널리틱스에 접근 권한을 추가하기
아까 다운 받았던 credentials.json 파일을 여시면, servcie account email 항목이 있는데요. 이 부분을 복사하여야 합니다. 대충 이렇게 생겼어요!
이런 이메일을 접근 권한에 추가하여야 합니다! 구글 애널리틱스 설정 화면에 들어가봅시다.
관리 > 속성 > 속성 액세스 관리
여기에서 추가 버튼으로, 아까 복사한 이메일을 붙여넣기 합니다. 권한은 뷰어로만 주시면 됩니다.

권한 설정은 아직 끝난게 아닙니다…
저희 블로그는 github action 을 통해서 빌드 배포가 진행되고 있는데요. 민감한 키들을 코드에 남기는건 보안상에 아주 좋지 않습니다. 실제로 github 검색을 통해서 키가 유출되는 일이 많은 정도이니까요.
그래서 저희는 .env 파일을 통해서 별도로 민감한 키를 관리하고자 합니다. 또한 github environemnt 기능을 통해서 코드에서 분리하기도 하고요. (추가로 .gitignore 에 빼두셔야 합니다.)
아까 다운받은 json 파일을 jq 명령어를 이용해서 한줄 스트링으로 바꾸고 이걸 .env 파일로 세팅했습니다.
jq -c . < ./credentials.json
이걸 ANALYTICS_CREDENTIALS 항목으로 저장하고, Single Quote 로 묶어줬습니다. 또한 조회할 사이트의 속성 ID 들도 ANALYTICS_PROPERTY_ID 항목으로 저장했습니다.
ANALYTICS_CREDENTIALS='{"type":"service_account","project_id":"foobar-project-9999999999999","private_key_id":"..."}'
ANALYTICS_PROPERTY_ID='999999999'
https://ji5485.github.io/post/2021-06-26/create-env-with-github-actions-secrets/
위 문서를 참조하시어, github 에서도 동일하게 추가 합니다.
먼저 조회할 API 에 대해 전문을 만들어 봅시다.
저는 볼르그에 게시글만 조회할 예정이며, 게시물들은 /articles 라는 디렉토리부터 시작되게끔 URL 을 설정 했습니다. 해당 조건들로 검색하기 위해서 아래 요청 parameter 들을 작성해봤습니다.
Google은 Analytics Reporting v4 API를 사용하여 데이터를 쿼리하는 방법을 보여주는 간단한 양식인 를인 Request Composer 제공합니다. 여기에서 생성한 JSON 을 저장해둡시다.

아래는 해당 JSON 전문입니다.
{
"dateRanges": [{ "startDate": "2022-05-30", "endDate": "yesterday" }],
"dimensionFilter": {
"filter": {
"fieldName": "pagePath",
"stringFilter": { "matchType": "BEGINS_WITH", "value": "/article/" }
}
},
"dimensions": [{ "name": "pagePath" }],
"metrics": [{ "name": "screenPageViews" }]
}
조회하는 코드를 작성해보자!
먼저 저는 gatby-node 파일을 통해서 빌드시에 조회수를 조회하고, 게시물 node 생성시에 field 를 추가하는 형식으로 조회수 항목을 추가하고자 했습니다.
그러기 위해서 다음과 같은 프로세스를 진행할 예정입니다.
- onPluginInit 시점시, 조회수를 조회한다.
- 이후 onCreateNode 시점에서 게시물일 경우, 조회수를 가져와서 연결한다.

import { BetaAnalyticsDataClient } from '@google-analytics/data';
import 'dotenv/config';
const articlePrefix = '/article';
const getViewCount = async () => {
let analyticsResult = [];
try {
const analyticsDataClient = new BetaAnalyticsDataClient({
credentials: JSON.parse(process.env.ANALYTICS_CREDENTIALS || '{}'),
});
analyticsResult = await analyticsDataClient.runReport({
property: `properties/${process.env.ANALYTICS_PROPERTY_ID || ''}`,
dateRanges: [{ startDate: '2022-05-30', endDate: 'today' }],
dimensions: [{ name: 'pagePath' }],
metrics: [{ name: 'screenPageViews' }],
dimensionFilter: {
filter: {
fieldName: 'pagePath',
stringFilter: {
matchType: 'BEGINS_WITH',
value: `${articlePrefix}/`,
},
},
},
});
} catch (error) {
console.error(error);
}
// analytics data arrange
return (
analyticsResult
.filter((item: any) => item !== null && item.rows)
.map((item: any) => {
return item.rows.map((row: any) => {
return {
path: row.dimensionValues[0].value,
totalCount: row.metricValues[0].value,
};
});
})[0] || []
);
};

코드를 길게 설명하지는 않겠습니다. 주요 보여야 하는 포인트는 다음과 같습니다.
- credentials 항목을 process.env.ANALYTICS_CREDENTIALS 으로 넘김
- 아까 Request Composer 에서 생성한 쿼리에서 property 항목을 추가
- 조회한 결과를 path: string, totalCount: string 항목으로 리턴함
이 함수를 아까 이야기했던 onPluginInit 에서 호출하도록 합니다. 이후 cache 로 적재 했는데요. 자세한 내용은 Gatsby Node API Helpers 문서를 참조 하세요!
export const onPluginInit = async ({ cache }) => {
await cache.set('viewCount', await getViewCount());
};
이후 onCreateNode 에서 slug를 비교하고 조회수 필드를 추가하는 로직을 구현 했습니다.
export const onCreateNode = async ({ node, getNode, actions, cache }) => {
const viewCount = await cache.get('viewCount');
// total count
const slug = `${articlePrefix}${createFilePath({ node, getNode, basePath: `./contents` })}`;
const totalCount = (viewCount.filter((item: any) => item.path === slug)[0] || { totalCount: 0 })
.totalCount;
createNodeField({ node, name: 'totalCount', value: parseInt(totalCount) });
};
이렇게 조회수 항목을 추가하는 코드는 끝났습니다.
다음으로 graphql 에서 정상적으로 출력하는 것을 확인해보겠습니다.
끝

보시면 정상적으로 조회수가 추가된 것을 확인할 수 있습니다. 이제 UI 단에서 추가만 하면 끝!

출처 및 참고
- https://decodenatura.com/how-to-add-a-trending-section-to-gatsby-blog/
- https://developers.google.com/analytics/devguides/reporting/data/v1/quickstart-client-libraries
- https://googleapis.dev/nodejs/analytics-data/latest/index.html
- https://www.npmjs.com/package/google-auth-library
- https://ji5485.github.io/post/2021-06-26/create-env-with-github-actions-secrets/
관련 태그 글
Phaser 4 Pseudo 3D 레이싱 게임 - 가드레일, 저속 조향, 0-100km/h를 다시 잡기
Apex Seoul의 밤 다운힐 비주얼을 왼쪽 절벽·가드레일, 오른쪽 옹벽·수목 구조로 재정리하고, 가드레일 충돌, 저속 조향 pose, standing start 파워밴드까지 주행 로그와 자동 QA로 다시 맞췄습니다.
Phaser 4 Pseudo 3D 레이싱 게임 - 내리막 고속 grip에 비용을 만드는 법
Apex Seoul에서 200km/h grip 주행이 너무 쉽게 느껴진 이유를 주행 로그로 분해하고, 코스 곡률을 바꾸지 않은 채 하향 코너 속도 예산·understeer·안전 여유 비용·자동 검증으로 다운힐의 선택을 만들었습니다.
Phaser 4 Pseudo 3D 레이싱 게임 - 드리프트를 상태와 로그로 튜닝하기
Apex Seoul에 스페이스 브레이크 기반 드리프트와 카운터 스티어를 넣고, 입력을 놓았을 때의 복귀·전환을 별도 상태로 분리했습니다. 주행 로그와 자동 시나리오로 감각을 수치화하며, 스핀은 아직 구현하지 않기로 한 이유도 정리합니다.
Phaser 4 Pseudo 3D 레이싱 게임 - 다운힐 진행 구조와 RPM 계기판 만들기
Apex Seoul을 한 번 내려오는 다운힐 런으로 정리하고, 고속 조향과 코너 감속을 실제 주행 로그로 다시 튜닝한 뒤, FT86 기반 Raven Coupe에 NA 고회전 RPM, 기어, 토크 곡선, fuel cut 상태를 붙였습니다.