Google Analytics(GA4) 누적 방문자 추적 통합 (경량, frontend 전용)

요약AlgoSu frontend(Next.js 15.5.15 App Router)에 GA4 추적을 통합하여 누적 방문자를 Google GA4 대시보드에서 확인 가능하게 했다. 옵션 A(GA4 대시보드 직접 확인, 경량) 선택 — 앱 내 수치 표출/GA4 Data API/service account/백엔드 없음. @next/third-parties/google의 GoogleAnalytics 컴포넌트 사용, 측정 ID 미설정 시 no-op(Sentry enabled 패턴 계승), CSP 갱신. 핵심 교훈: npm install vs npm ci lockfile 정합 함정 — Architect의 npm install이 49개 패키지를 prune하여 로컬 테스트 통과 + CI npm ci 실패를 야기, Oracle 검증 단계에서 포착 후 원본 lockfile 복원+npm install --package-lock-only로 교정.

날짜
영향도높음

목표

  • AlgoSu frontend(Next.js 15.5.15 App Router)에 GA4 추적을 통합한다.
  • 누적 방문자를 Google GA4 대시보드에서 확인 가능하게 한다.
  • 범위를 경량 옵션(GA4 대시보드 직접 확인)으로 제한하여 앱 내 표출/Data API/백엔드 의존성을 배제한다.

배경

AlgoSu 서비스의 누적 방문자 수를 추적하기 위한 분석 도구가 필요했다. 두 가지 구현 옵션이 검토되었다.

  • 옵션 A: GA4 대시보드에서 직접 확인. @next/third-parties/google 컴포넌트 삽입만으로 완결. 측정 ID 발급 후 대시보드 접속으로 수치 확인.
  • 옵션 B: 앱 내 방문자 수치 표출. GA4 Data API + service account(SealedSecret) + 백엔드 엔드포인트 + frontend 컴포넌트 전체 스택 필요.

사용자가 **옵션 A(경량)**를 선택했다.

결정

D0. 누적 방문자 조회 경로 — GA4 대시보드 직접 확인 (옵션 A)

앱 내 표출(옵션 B)은 GA4 Data API + service account SealedSecret + 백엔드 엔드포인트를 필요로 하며 이번 sprint 범위를 초과한다. 결정: 옵션 A로 진행, 옵션 B는 별도 sprint 후보로 이월.

D1. GA 통합 방식 — @next/third-parties/google 공식 컴포넌트

Next.js 공식 @next/third-parties 패키지의 GoogleAnalytics 컴포넌트를 사용한다. 측정 ID는 NEXT_PUBLIC_GA_MEASUREMENT_ID 환경변수로 주입한다.

방식접근선택
@next/third-parties/googleNext.js 공식 third-parties, 서버 컴포넌트 호환, 자동 스크립트 최적화
직접 <script> 삽입gtag.js 수동 주입❌ (비관용적, 최적화 미적용)

D2. 조건부 활성화 — 측정 ID 미설정 시 no-op

측정 ID가 falsy(미설정/빈 문자열)이면 컴포넌트가 null을 반환(no-op)한다. 기존 Sentry(sentry.client.config.tsenabled: !!NEXT_PUBLIC_SENTRY_DSN) 패턴을 계승한다.

구현

Phase A — 의존성 추가

@next/third-parties@^15.5.15 추가 (npm install15.5.18 resolve).

lockfile 교정 (핵심 사건): Architect의 초기 npm install 실행 시 @monaco-editor/react의 peer인 monaco-editorwebpack/dompurify/marked 등 49개 필수 패키지가 package-lock.json에서 잘못 prune되었다(−662 라인). npm install은 lenient하여 로컬 test:coverage(1381 통과)는 성공했으나, CI가 사용하는 strict npm ciEUSAGE Missing: monaco-editor from lock file로 실패했다. Oracle 검증 단계에서 포착 후 02018fc 원본 lockfile을 완전 복원하고 npm install --package-lock-only로 신규 2개 dep만 추가하여 diff를 +24/−11로 정상화, npm ci EXIT=0(1112 패키지) 확인.

Phase B — GoogleAnalytics 서버 컴포넌트 래퍼 신규

src/components/analytics/GoogleAnalytics.tsx 신규:

  • @next/third-parties/google에서 GoogleAnalytics as GA import(네이밍 충돌 회피 alias)
  • NEXT_PUBLIC_GA_MEASUREMENT_ID falsy 시 null 반환

Phase C — RootLayout 통합

src/app/layout.tsx — body 내 children<GoogleAnalytics /> 삽입.

Phase D — CSP 갱신

next.config.ts Content-Security-Policy 헤더 갱신:

  • script-src: googletagmanager.com 추가
  • img-src: googletagmanager.com, google-analytics.com, *.google-analytics.com 추가
  • connect-src: google-analytics.com, *.google-analytics.com, *.analytics.google.com, googletagmanager.com 추가

기존 cdn.jsdelivr.net 등 기존 출처는 유지.

Phase E — .env.example 갱신

NEXT_PUBLIC_GA_MEASUREMENT_ID= + 용도 주석 추가.

Phase F — 테스트 7케이스

src/components/analytics/GoogleAnalytics.test.tsx 신규 — 7케이스:

  • 측정 ID 설정 시 <GoogleAnalytics> 렌더링 확인
  • undefined / 빈 문자열 / 미설정 시 null 반환(no-op) 확인
  • 컴포넌트 branch coverage 100%

@testing-library/dom devDependency 복원(기존 npm install prune 부수 피해).

검증

게이트

  • npm ci → added 1112 packages, EXIT=0 (CI install 정합 확인)
  • npx tsc --noEmit → EXIT=0, 오류 0건
  • npx next lint → EXIT=0 (Error 0, 기존 UI 컴포넌트 Warning만)
  • npx jest src/components/analytics7/7 통과 (branch coverage 100%)
  • test:coverage (전체): 133 suites, 1381 tests, 임계값(lines 83 / branches 71 / functions 82 / statements 81) 충족

ADR 인덱스 게이트

  • node scripts/check-adr-index-count.mjs --strict → 영구 8 / 토픽 1 / sprint 148
  • node scripts/check-adr-en-coverage.mjs --lint157/157 (100%)
  • node scripts/check-doc-refs.mjs → 0 broken

신규 패턴

  • 의존성 추가 시 npm install --package-lock-only + npm ci 검증 패턴 — 신규 npm 패키지 추가 시 npm install --package-lock-only로 lockfile만 갱신(node_modules 불변) + 이후 npm ci EXIT=0 검증 필수. npm install 단독은 lockfile prune 위험 내재.
  • Sentry no-op 계승 패턴 — 환경변수 기반 선택적 서드파티 통합 시 !!ENV_VAR falsy 조건으로 컴포넌트 null 반환(no-op). sentry.client.config.tsenabled: !!NEXT_PUBLIC_SENTRY_DSNGoogleAnalytics 컴포넌트로 일반화.

Critic 교차 리뷰

  • R1 CLEAN ✅ (Codex codex review --base 02018fc, model gpt-5.5, session 019e6d5d-5dfb-7e23-9aa7-f26ad2bdc863): "The GA4 integration is conditionally enabled, wired into the root layout, and the CSP/dependency updates appear consistent with the new third-party script usage. I did not identify any discrete regression introduced by the diff."
  • 발견 Critical/High/Medium/Low 모두 0건. Critic이 @next/third-parties/google GA 구현 소스·CSP·의존성을 직접 탐색 검증. R2+ 불필요 — placeholder 회귀 차단 결정 준수, R1만 영속화.