chore: Google Analytics 기본 세팅 및 모임 생성(create) 이벤트 설정#45
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughGoogle Analytics 4 추적을 통합하고, 회의 생성 페이지에서 용량 기본값 로직을 조정하며, 회의 생성 완료 후 GA4 이벤트를 전송하는 기능을 구현합니다. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/create/page.tsx (1)
140-144:⚠️ Potential issue | 🟡 Minor프로덕션
console.log제거 필요
console.log('전체 응답:', result)및console.log('생성된 ID:', meetingId)는 프로덕션 코드에서 제거되어야 합니다. 서버 응답 및 내부 ID가 브라우저 콘솔에 노출됩니다.🔧 제안하는 수정
- console.log('전체 응답:', result); if (result.success && result.data?.meetingId) { const { meetingId } = result.data; - console.log('생성된 ID:', meetingId);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/create/page.tsx` around lines 140 - 144, Remove the production console logs that leak server responses and IDs: delete the console.log('전체 응답:', result) and console.log('생성된 ID:', meetingId) statements in the code that handles the API response (where the result variable and extracted meetingId are used). If you need to keep diagnostics for development, replace them with a conditional debug/log call (e.g., use an existing logger.debug or wrap logs with a NODE_ENV === 'development' check) so no sensitive data is printed in production.
🧹 Nitpick comments (2)
app/create/page.tsx (1)
149-154:browser_id생성에Math.random()대신crypto.randomUUID()사용 권장
Math.random()은 암호학적으로 안전하지 않으며 엔트로피가 낮습니다.crypto.randomUUID()는 RFC 4122 v4 UUID를 생성하여 충돌 가능성이 훨씬 낮고, 현재 지원되는 모든 브라우저에서 사용 가능합니다.♻️ 제안하는 수정
- let browserId = localStorage.getItem('browser_id'); - if (!browserId) { - // 없으면 새로 발급해서 브라우저에 각인! - const randomStr = Math.random().toString(36).substring(2, 15); - browserId = `bid_${randomStr}${Date.now().toString(36)}`; - localStorage.setItem('browser_id', browserId); - } + let browserId = localStorage.getItem('browser_id'); + if (!browserId) { + browserId = crypto.randomUUID(); + localStorage.setItem('browser_id', browserId); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/create/page.tsx` around lines 149 - 154, Replace the insecure Math.random()-based browser_id generation in the create/page.tsx block (the browserId/localStorage logic) with crypto.randomUUID(): when browserId is missing, call crypto.randomUUID(), prefix with "bid_" if you want to keep the existing format, assign to browserId and persist via localStorage.setItem('browser_id', browserId); update any variable names referencing browserId accordingly and optionally add a small feature-detect fallback if you need to support very old environments.app/layout.tsx (1)
53-53: GA Measurement ID를 환경 변수로 분리하고, 개발 환경 추적 방지 권장
gaId에 실제 측정 ID(G-3FN93H79SZ)가 소스 코드에 하드코딩되어 있습니다. 이렇게 하면 두 가지 문제가 생깁니다.
- 환경 오염: 개발/스테이징/운영 환경 모두 동일한 GA 속성으로 이벤트를 전송해 데이터가 오염됩니다.
- 유지보수 어려움: 측정 ID 변경 시 소스 코드를 수정해야 합니다.
♻️ 환경 변수 기반으로 변경 제안
.env.local(또는 환경별.env):NEXT_PUBLIC_GA_MEASUREMENT_ID=G-3FN93H79SZ- <GoogleAnalytics gaId="G-3FN93H79SZ" /> + {process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID && ( + <GoogleAnalytics gaId={process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID} /> + )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/layout.tsx` at line 53, The GoogleAnalytics measurement ID is hardcoded in app/layout.tsx via the GoogleAnalytics component (gaId="G-3FN93H79SZ"); change this to read from an environment variable (e.g. NEXT_PUBLIC_GA_MEASUREMENT_ID) and only initialize/render GoogleAnalytics when that env var is set and the app is in production (NODE_ENV === "production" or similar) to avoid dev/staging pollution and make future changes config-driven; update any references to gaId to use process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID and gate rendering/initialization in the layout or in the GoogleAnalytics wrapper component.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/create/page.tsx`:
- Around line 147-164: The current code creates and stores browser_id in
localStorage and calls sendGAEvent unconditionally; change this so browser_id is
only generated, saved, and sent after explicit tracking consent is confirmed
(e.g., check your CMP or a consent flag like tracking_consent in
localStorage/session) — update the logic around the browser_id generation and
sendGAEvent call to first verify consent (if consent true then generate/set
localStorage 'browser_id' and call sendGAEvent with
meetingId/capacity/browser_id; if consent false do nothing), and ensure that if
a browser_id exists but no consent is present you still do not send analytics
until consent is granted.
- Line 168: Remove the redundant shadowed declaration "const purposes =
getPurposes()" and use the existing purposes variable already declared earlier;
in other words, delete the second call inside the if block so the code
references the outer "purposes" (from getPurposes()) rather than redeclaring it
and causing a no-shadow ESLint error.
---
Outside diff comments:
In `@app/create/page.tsx`:
- Around line 140-144: Remove the production console logs that leak server
responses and IDs: delete the console.log('전체 응답:', result) and console.log('생성된
ID:', meetingId) statements in the code that handles the API response (where the
result variable and extracted meetingId are used). If you need to keep
diagnostics for development, replace them with a conditional debug/log call
(e.g., use an existing logger.debug or wrap logs with a NODE_ENV ===
'development' check) so no sensitive data is printed in production.
---
Nitpick comments:
In `@app/create/page.tsx`:
- Around line 149-154: Replace the insecure Math.random()-based browser_id
generation in the create/page.tsx block (the browserId/localStorage logic) with
crypto.randomUUID(): when browserId is missing, call crypto.randomUUID(), prefix
with "bid_" if you want to keep the existing format, assign to browserId and
persist via localStorage.setItem('browser_id', browserId); update any variable
names referencing browserId accordingly and optionally add a small
feature-detect fallback if you need to support very old environments.
In `@app/layout.tsx`:
- Line 53: The GoogleAnalytics measurement ID is hardcoded in app/layout.tsx via
the GoogleAnalytics component (gaId="G-3FN93H79SZ"); change this to read from an
environment variable (e.g. NEXT_PUBLIC_GA_MEASUREMENT_ID) and only
initialize/render GoogleAnalytics when that env var is set and the app is in
production (NODE_ENV === "production" or similar) to avoid dev/staging pollution
and make future changes config-driven; update any references to gaId to use
process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID and gate rendering/initialization in
the layout or in the GoogleAnalytics wrapper component.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (3)
app/create/page.tsxapp/layout.tsxpackage.json
| if (typeof window !== 'undefined') { | ||
| // 1. 브라우저 식별자(browser_id) 확인 및 생성 (Get or Create) | ||
| let browserId = localStorage.getItem('browser_id'); | ||
| if (!browserId) { | ||
| // 없으면 새로 발급해서 브라우저에 각인! | ||
| const randomStr = Math.random().toString(36).substring(2, 15); | ||
| browserId = `bid_${randomStr}${Date.now().toString(36)}`; | ||
| localStorage.setItem('browser_id', browserId); | ||
| } | ||
|
|
||
| // 2. 방 만든 브라우저가 누구인지 식별자를 담아서 이벤트 전송 | ||
| sendGAEvent('event', 'url_created', { | ||
| meeting_url_id: meetingId, | ||
| participant_count_expected: capacity, | ||
| browser_id: browserId, | ||
| entry_method: 'url_direct', | ||
| }); | ||
| } |
There was a problem hiding this comment.
browser_id 기반 추적 전 사용자 동의(GDPR/CCPA) 누락
browser_id를 사용자 동의 없이 localStorage에 저장하고 GA에 전송하는 것은 GDPR·CCPA 위반 소지가 있습니다. 이 식별자는 브라우저 수준에서 사용자를 추적하기 위한 목적으로 생성되므로, 쿠키/추적 동의와 동일하게 취급되어야 합니다.
사용자가 동의를 제공한 이후에만 browser_id를 생성·저장·전송하거나, 동의 관리 플랫폼(CMP)을 통해 GA 자체를 조건부로 로드하는 방식을 고려하세요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/create/page.tsx` around lines 147 - 164, The current code creates and
stores browser_id in localStorage and calls sendGAEvent unconditionally; change
this so browser_id is only generated, saved, and sent after explicit tracking
consent is confirmed (e.g., check your CMP or a consent flag like
tracking_consent in localStorage/session) — update the logic around the
browser_id generation and sendGAEvent call to first verify consent (if consent
true then generate/set localStorage 'browser_id' and call sendGAEvent with
meetingId/capacity/browser_id; if consent false do nothing), and ensure that if
a browser_id exists but no consent is present you still do not send analytics
until consent is granted.
| // ----------------------------------- | ||
|
|
||
| // purposes를 localStorage에 저장 (장소 추천 카테고리로 사용) | ||
| const purposes = getPurposes(); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
불필요한 purposes 변수 섀도잉 제거
Line 125에서 이미 const purposes = getPurposes()가 선언되어 있으며, if 블록 내부에서도 동일하게 접근 가능합니다. Line 168의 const purposes = getPurposes()는 동일한 함수를 다시 호출하는 불필요한 섀도잉으로, no-shadow ESLint 규칙이 활성화된 경우 에러가 됩니다.
🔧 제안하는 수정
- // purposes를 localStorage에 저장 (장소 추천 카테고리로 사용)
- const purposes = getPurposes();
- if (purposes.length > 0) {
+ // purposes를 localStorage에 저장 (장소 추천 카테고리로 사용)
+ if (purposes.length > 0) {Line 125의 purposes가 이미 if 블록 스코프에서 참조 가능하므로 별도 재선언이 필요 없습니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/create/page.tsx` at line 168, Remove the redundant shadowed declaration
"const purposes = getPurposes()" and use the existing purposes variable already
declared earlier; in other words, delete the second call inside the if block so
the code references the outer "purposes" (from getPurposes()) rather than
redeclaring it and causing a no-shadow ESLint error.
🚀 chore: Google Analytics 기본 세팅 및 모임 생성(create) 이벤트 설정
📝 변경사항
✅ 체크리스트
📸 스크린샷
💬 리뷰어 전달사항
Summary by CodeRabbit
릴리스 노트