[๐ ] ๊ณต๊ฐ ๋ธ๋ก๊ทธ ํต๊ณ ํ์ด์ง ๋ง๋ค๊ธฐ
โจ GPT-5.5์ ์์ฝ ใ
์ด์ํ๊ฒ ํ๋ ๋ฐฉ๋ฌธ์ ์นด์ดํฐ๋ฅผ ์ถ์ ํด ๋ก์ปฌ ์์ ์นด์ดํธ๋ฅผ ๋ง๊ณ , Cloudflare Worker์ D1๋ก ๊ณต๊ฐ ๊ฐ๋ฅํ ์ง๊ณํ analytics ํ์ด์ง๋ฅผ ๋ถ์ธ ๊ธฐ๋ก.
๋ฐฉ๋ฌธ์ ์นด์ดํฐ๊ฐ ์ด์ํ๋ค.
ํ๋ฃจ ๋ฐฉ๋ฌธ์์๊ฐ ๊ฐ์๊ธฐ 80์ ๋์๋ค.
์ฒ์์๋ ๊ทธ๋ฅ โ๊ฒ์์ ๊ฑธ๋ ธ๋?โ ์ ๋๋ก ๋๊ธธ ์๋ ์์๋ค. ๊ทธ๋ฐ๋ฐ ๋๋์ด ์ด์ํ๋ค. ์ต๊ทผ ๋ฉฐ์น ๋์ ๋ด๊ฐ ๋ธ๋ก๊ทธ๋ฅผ ๊ณ์ ๊ณ ์น๊ณ ์์๊ณ , AI ์์ ์ค์๋ ๋ก์ปฌ ์๋ฒ์ ๊ณต๊ฐ URL์ ๊ฝค ๋ง์ด ์ด์ด๋ดค๋ค.
๊ทธ๋์ ์์ฌ์ด ์๊ฒผ๋ค.
๋ด๊ฐ ๋ณด๊ณ ์๋ ์ซ์๊ฐ ์ง์ง ๋ฐฉ๋ฌธ์์ผ๊น.
์๋๋ฉด ๋ด๊ฐ ๋ง๋ ์นด์ดํฐ๊ฐ ๋ด ์์ ๊น์ง ๋ค ์ธ๊ณ ์๋ ๊ฑธ๊น.
์นด์ดํฐ๊ฐ ๋๋ฌด ์์งํ๋ค
๋จผ์ ๊ตฌ์กฐ๋ฅผ ๋ค์ ๋ดค๋ค.
์์ /devlog/github-pages-blog/github-pages-blog-visitor-counter/์์ ๋ง๋ ๋ฐฉ๋ฌธ์ ์นด์ดํฐ๋ Cloudflare Worker์ D1์ ์ด๋ค.
ํ์ด์ง๊ฐ ๋ก๋๋๋ฉด JS๊ฐ Worker์ /track์ ๋ณด๋ด๊ณ , Worker๊ฐ D1์ counters, page_counters, dedupe_views๋ฅผ ์ฌ๋ฆฐ๋ค. Google Analytics๋ baseline์ผ๋ก๋ง ์ฐ๊ณ , ์ดํ ์ฆ๋ถ์ D1์ ์๋ ๊ตฌ์กฐ์๋ค.
๋ฌธ์ ๋ ์ด ๊ตฌ์กฐ๊ฐ ๋๋ฌด ์ ์งํ๋ค๋ ์ ์ด๋ค.
๋ธ๋ผ์ฐ์ ๊ฐ ํ์ด์ง๋ฅผ ์ด๋ฉด ์ผ๋ค.
๋ก์ปฌ ๊ฐ๋ฐ ์๋ฒ์์ ์ด์ด๋ endpoint๋ production Worker๋ฅผ ๋ฐ๋ผ๋ณด๊ณ ์์๋ค. Worker ์ชฝ CORS ํ์ฉ origin์๋ localhost:4000๊ณผ 127.0.0.1:4000์ด ๋ค์ด ์์๋ค.
๊ทธ๋ฌ๋ฉด ๋น์ฐํ ๋ก์ปฌ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ ์ค์ D1 ์นด์ดํฐ๋ฅผ ์ฌ๋ฆด ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ dedupe ๊ธฐ์ค๋ visitorId + path์๋ค. ๊ฐ์ ๋ธ๋ผ์ฐ์ ์์ ๊ฐ์ ๊ธ์ 10๋ถ ์์ ์ฌ๋ฌ ๋ฒ ์ด๋ฉด ํ ๋ฒ๋ง ์ธ์ง๋ง, ์๋ก ๋ค๋ฅธ ๊ธ์ ๊ณ์ ์ด๋ฉด ์ ๋ถ ์ธ๋ ๊ตฌ์กฐ๋ค. ๋ธ๋ก๊ทธ ์ ๊ฒ ์ค ๊ธ์ ์ฌ๋ฌ ๊ฐ ์ด๋ฉด ๊ทธ๊ฒ ๊ทธ๋๋ก page view๊ฐ ๋๋ค.
D1์ ์ง์ ํ์ธํ๋ ๋ ๋ ธ๊ณจ์ ์ด์๋ค.
day:2026-06-06 = 14
day:2026-06-07 = 38
day:2026-06-08 = 81
day:2026-06-09 = 5
2026-06-08์๋ ์ค๋๋ Daily Review์ naver-* ๊ธ๋ค์ด ๋๊ฒ ํ ๋ฒ์ฉ ์ฐํ ํ์ ์ด ์์๋ค. ์ค์ ๋
์๋ผ๊ธฐ๋ณด๋ค ์์
์ค ํ์ด์ง๋ฅผ ํ์ ๋ชจ์์ ๊ฐ๊น์ ๋ค.
๋ก์ปฌ ์์ ์ ํ์๋ง ํ๊ณ ์ธ์ง ์๊ฒ ๋ง์๋ค
์ฒซ ๋ฒ์งธ ์์ ์ ๋จ์ํ๋ค.
๋ก์ปฌ์์๋ ์กฐํ์ ํ์๋ง ํ๊ณ /track์ ๋ณด๋ด์ง ์๊ฒ ํ๋ค.
localhost
127.0.0.1
::1
์ด ์ธ ํ๊ฒฝ์์๋ ํด๋ผ์ด์ธํธ JS๊ฐ ์นด์ดํธ ์์ฒญ์ ๋ณด๋ด์ง ์๋๋ค.
ํ์ง๋ง ํด๋ผ์ด์ธํธ๋ง ๋ฏฟ์ผ๋ฉด ์ ๋๋ค. JS๊ฐ ๊ผฌ์ด๊ฑฐ๋ ๋ค๋ฅธ ์์ฒญ์ด ๋ค์ด์ค๋ฉด ๋ค์ ๊ฐ์ ๋ฌธ์ ๊ฐ ์๊ธด๋ค. ๊ทธ๋์ Worker์์๋ /track์ production origin๋ง ์นด์ดํธํ๋๋ก ๋ง์๋ค.
TRACK_ALLOWED_ORIGIN=https://hyuk.blog
์ด์ ๋ก์ปฌ origin์์ /track์ ๋ณด๋ด๋ฉด Worker๊ฐ origin_not_tracked๋ก ๋ฌด์ํ๋ค.
์ถ๊ฐ๋ก production ํ๋ฉด์ ์ง์ ํ์ธํด์ผ ํ ๋๋ ์์๋ค. ๊ทธ๋์ ๋ธ๋ผ์ฐ์ localStorage์ opt-out์ ์ ์ฅํ๋ ์ฐํ๋ก๋ฅผ ๋ง๋ค์๋ค.
?hyuk_no_track=1
?visitor_tracking=off
?visitor_tracking=on
์ด๊ฑด ์จ๊ฒจ์ง ๊ด๋ฆฌ์ ๊ธฐ๋ฅ์ด๋ผ๊ธฐ๋ณด๋ค, ๋ด๊ฐ ๊ณต๊ฐ ํ์ด์ง๋ฅผ ์ ๊ฒํ ๋ ์นด์ดํฐ๋ฅผ ๋๋ฝํ์ง ์๊ธฐ ์ํ ์ค์์น๋ค.
ํต๊ณ๋ฅผ ์จ๊ธธ ํ์๊ฐ ์์๋ค
์ฒ์์๋ ์ธ๋ถ analytics๋ฅผ ๋๋ง ๋ณด๋ ํ์ด์ง๋ก ๋ง๋ค๊น ์๊ฐํ๋ค.
๊ทธ๋ฐ๋ฐ ๋ค์ ์๊ฐํด๋ณด๋, ๊ตณ์ด ๊ทธ๋ด ํ์๊ฐ ์์๋ค.
IP ์๋ฌธ, User-Agent ์๋ฌธ, full referrer URL, ๋จ๊ฑด ์ด๋ฒคํธ ๋ก๊ทธ๋ฅผ ์ ์ฅํ์ง ์์ผ๋ฉด ๋๋ค. ๊ณต๊ฐํด๋ ๋๋ ๊ฐ๋ง ์์งํ๋ฉด, ํต๊ณ ํ์ด์ง๋ ๊ณต๊ฐํด๋ ๋๋ค.
๊ทธ๋์ ๋ฐฉํฅ์ ๋ฐ๊ฟจ๋ค.
๋๋ง ๋ณด๋ ๋ก๊ทธ ํ์ด์ง๊ฐ ์๋๋ผ, ๋๊ตฌ๋ ๋ณผ ์ ์๋ ๊ณต๊ฐ ๋ธ๋ก๊ทธ ํต๊ณ ํ์ด์ง.
/analytics/
์๋จ ๋ฉ๋ด์๋ ํต๊ณ ๋งํฌ๋ฅผ ๋ถ์๋ค. ์ฌ์ด๋๋ฐ์ ์์ ๋ฐฉ๋ฌธ ํต๊ณ ์๋์๋ ๊ฐ์ ํ์ด์ง๋ก ๊ฐ๋ ๋งํฌ๋ฅผ ์ถ๊ฐํ๋ค.
D1์๋ ์ง๊ณ๋ง ์๋๋ค
์ ํ ์ด๋ธ์ ๋จ์ํ๋ค.
analytics_daily_dimensions
date
dimension
value
count
updated_at
ํ ๋ฒ์ page view๊ฐ ์ค์ ๋ก ์นด์ดํธ๋ ๋, Worker๋ ๊ณต๊ฐ ๊ฐ๋ฅํ ์ฐจ์์ ๋ถ๋ฅํด์ ํ๋ฃจ ๋จ์๋ก ๋์ ํ๋ค.
์์งํ๋ ๊ฐ์ ์ด๋ฐ ๊ฒ๋ค์ด๋ค.
page
content_group
locale
country
region
continent
colo
asn_org
device
viewport
browser
os
language
client_timezone
color_scheme
connection
traffic_source
referrer_domain
hour
weekday
์ฌ๊ธฐ์ ์ค์ํ ๊ฑด ์๋ฌธ์ ์ ์ฅํ์ง ์๋๋ค๋ ์ ์ด๋ค.
์๋ฅผ ๋ค์ด referrer๋ ์ ์ฒด URL์ด ์๋๋ผ domain๋ง ์ ์ฅํ๋ค. User-Agent๋ ์๋ฌธ์ ์ ์ฅํ์ง ์๊ณ browser, OS, device๋ก ๋ถ๋ฅํ๋ค. IP๋ ์ ์ฅํ์ง ์๋๋ค.
์ ํํ ๋ถ์ ๋๊ตฌ๋ผ๊ธฐ๋ณด๋ค๋ ๊ณต๊ฐ ๋ธ๋ก๊ทธ์ฉ ํ๋ฆํ์ด๋ค.
์ค๋, ์, ์ ์ฒด, ์ง์ ๊ธฐ๊ฐ
์ฒ์์๋ ๊ธฐ์กด ์นด์ดํฐ์ฒ๋ผ ์ค๋ / ์ / ์ด๋ง ์๊ฐํ๋ค.
๊ทธ๋ฐ๋ฐ ํต๊ณ ํ์ด์ง๋ผ๋ฉด ํน์ ๋ ์ง๋ ๊ธฐ๊ฐ๋ ๋ด์ผ ํ๋ค.
๊ทธ๋์ /analytics API๋ ์ด๋ ๊ฒ ๋ฐ๋๋ค.
/analytics?range=today
/analytics?range=month
/analytics?range=total
/analytics?range=custom&start=2026-06-09&end=2026-06-12
total์ GA ์ ์ฒด baseline์ด ์๋๋ผ, analytics_daily_dimensions๊ฐ ์์ด๊ธฐ ์์ํ 2026-06-09 ์ดํ์ ์ธ๋ถ ์ง๊ณ ๊ธฐ์ค์ด๋ค. ์ฌ์ด๋๋ฐ์ ์ด ์กฐํ์๋ ์ฌ์ ํ GA baseline + D1 increment๋ค.
๋์ ์์ง ์์๋ค.
์ ์ฒด ์กฐํ์ ์ซ์์ ์ธ๋ถ analytics ๊ธฐ์ค์ด ๋ค๋ฅด๋ค๋ ์ ์ ํ์ด์ง์ ์ด์ ๋ฉ๋ชจ์ ๋จ๊ฒผ๋ค.
๋ชจ๋ฐ์ผ์์ ๋จผ์ ์ฝํ๊ฒ ๋ง๋ค์๋ค
ํต๊ณ ํ์ด์ง๋ ํนํ ๋ชจ๋ฐ์ผ์์ ๊นจ์ง๊ธฐ ์ฝ๋ค.
ํ๋ฅผ ํฌ๊ฒ ๋ง๋ค๋ฉด ๋ฐ๋ก ๋ต๋ตํด์ง๋ค. ๊ทธ๋์ ํ๋ณด๋ค๋ ๋ง๋ ๋ฆฌ์คํธ๋ก ๊ฐ๋ค.
ํต์ฌ ์ซ์๋ ์์ ๋ค ๊ฐ๋ง ๋๋ค.
์ ํ ๊ธฐ๊ฐ
์ค๋
์ด๋ฒ ๋ฌ
์ ์ฒด
๊ทธ ์๋๋ ์นด๋ํ ๋ง๋ ๋ฆฌ์คํธ๋ค.
๋ชจ๋ฐ์ผ์์๋ ๊ธฐ๊ฐ ๋ฒํผ์ด 2ํ์ผ๋ก ์ ํ๊ณ , KPI๋ 2์ด, ์์ธ ์นด๋๋ 1์ด๋ก ๋ด๋ ค๊ฐ๋ค. ์ง์ ๊ธฐ๊ฐ ์ ๋ ฅ๋ ์์์ผ, ์ข ๋ฃ์ผ, ์ ์ฉ ๋ฒํผ์ด ์ธ๋ก๋ก ์์ธ๋ค.
๋ฐ์คํฌํฑ์์๋ KPI๊ฐ ํ ์ค์ด๊ณ , ์์ธ ์นด๋๋ 2์ด๋ก ํผ์ณ์ง๋ค.
๋ธ๋ผ์ฐ์ ์์ ํ์ธํ์ ๋ ๊ฐ๋ก overflow๋ ์์๋ค. ์ฝ์ ์ค๋ฅ๋ ์์๋ค.
ํ์ธํ ๊ฒ
์์ ํ ํ์ธ์ ์ด๋ ๊ฒ ํ๋ค.
node --check cloudflare/ga-stats-worker.js
node --check assets/js/custom/visitor-stats.js
node --check assets/js/custom/analytics-dashboard.js
bundle exec jekyll build
npx wrangler d1 execute hyuk-blog-view-counter --remote --file cloudflare/schema.sql
npx wrangler deploy --keep-vars
Worker ๋ฐฐํฌ ๋ค /analytics?range=today๋ ํ์ธํ๋ค.
์๋ต์ ์ ์์ด๊ณ , ์ ์ง๊ณ ํ ์ด๋ธ์ ์์ง ๋น์ด ์์๋ค. ๋น์ฐํ๋ค. ์ธ๋ถ analytics๋ ๋ฐฐํฌ ์ดํ ์ค์ ๊ณต๊ฐ ๋ฐฉ๋ฌธ๋ถํฐ ์์ธ๋ค.
๋ก์ปฌ origin์์ /track์ ๋ณด๋ด๋ ํ
์คํธ๋ ํ๋ค.
{"status":"ignored","reason":"origin_not_tracked"}
์ด์ ์ ์ด๋ ๋ด๊ฐ ๋ก์ปฌ์์ ๊ธ์ ํ์ธํ๋ ํ์๊ฐ ์์ฐ ์นด์ดํฐ๋ฅผ ์ฌ๋ฆฌ์ง๋ ์๋๋ค.
๋ฐฉ๋ฌธ์ ์นด์ดํฐ์์ ๊ณต๊ฐ analytics๋ก
์ฒ์์๋ ์์ ์ซ์ ์ธ ๊ฐ์๋ค.
์ค๋, ์, ์ด.
๊ทธ๋ฐ๋ฐ ์ค์ ๋ก ์ด์ํด๋ณด๋ ์ซ์ ์์ฒด๋ณด๋ค ๋ ์ค์ํ ๊ฑด ์ซ์๊ฐ ๋๋ฝํ์ง์ง ์๋ ๊ตฌ์กฐ์๋ค. ๊ทธ๋ฆฌ๊ณ ๊ณต๊ฐ ๋ธ๋ก๊ทธ๋ผ๋ฉด, ์จ๊ฒจ์ง ๊ด๋ฆฌ์์ฉ ๋ก๊ทธ๋ณด๋ค ๊ณต๊ฐ ๊ฐ๋ฅํ ํ๋ฆ์ ๋ณด์ฌ์ฃผ๋ ์ชฝ์ด ๋ ๋ง์๋ค.
์ด๋ฒ ์์ ์ผ๋ก ๋ฐฉ๋ฌธ์ ์นด์ดํฐ๋ ๋จ์ ์ฅ์์์ ์กฐ๊ธ ๋ ์ด์ ๋๊ตฌ์ ๊ฐ๊น์์ก๋ค.
์ ํํ ํ๊ณ ์ฅ๋ถ๋ ์๋๋ค.
ํ์ง๋ง ์ด์ ์ต์ํ ์ด๋ฐ ์ง๋ฌธ์๋ ๋ตํ ์ ์๋ค.
์ด๋ค ๊ธ์ด ์ฝํ๋์ง.
์ด๋ ๋๋ผ์์ ๋ค์ด์ค๋์ง.
๋ชจ๋ฐ์ผ์ด ๋ง์์ง ๋ฐ์คํฌํฑ์ด ๋ง์์ง.
๊ฒ์์ธ์ง ์ง์ ์ ์ ์ธ์ง.
๊ทธ๋ฆฌ๊ณ ๋ฌด์๋ณด๋ค, ๋ด๊ฐ ์์ ํ๋ฉด์ ๋ง๋ ํ์๋ ์ด๋๊น์ง ๋ง์์ผ ํ๋์ง.
์ด๋ฐ ๊ฑธ ๋ณด๋ ค๊ณ ๋ง๋ ํ์ด์ง๊ฐ ๊ฒฐ๊ตญ /analytics/๋ค.
๋๊ธ ๋จ๊ธฐ๊ธฐ