Skip to content

chore: Google Analytics 기본 세팅 및 모임 생성(create) 이벤트 설정#45

Merged
kangdy25 merged 2 commits intomainfrom
chore/ga4-basic-setting
Feb 26, 2026
Merged

chore: Google Analytics 기본 세팅 및 모임 생성(create) 이벤트 설정#45
kangdy25 merged 2 commits intomainfrom
chore/ga4-basic-setting

Conversation

@kangdy25
Copy link
Collaborator

@kangdy25 kangdy25 commented Feb 25, 2026

🚀 chore: Google Analytics 기본 세팅 및 모임 생성(create) 이벤트 설정

📝 변경사항

  • Google Analytics 4 기본 세팅 (@next/third-parties)
  • 모임 생성 이벤트 설정

✅ 체크리스트

  • 코드 리뷰를 받았습니다
  • 테스트를 완료했습니다
  • 린터 에러가 없습니다
  • 타입 에러가 없습니다
  • 브라우저에서 테스트를 완료했습니다
  • 모바일에서 테스트를 완료했습니다 (해당되는 경우)

📸 스크린샷

UI 변경 사항이 있다면 이미지를 드래그해서 넣어주세요!

💬 리뷰어 전달사항

  • 리뷰어가 특별히 확인해야 할 사항이 있다면 적어주세요.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • 회의를 생성할 때 "아직 안정해졌어요" 옵션을 선택하는 경우, 기본 참석자 수가 30명에서 10명으로 조정됩니다.
    • 회의 URL 생성에 대한 분석 추적 기능이 추가되었습니다. 이를 통해 더욱 정확한 사용 현황을 파악할 수 있습니다.

@vercel
Copy link

vercel bot commented Feb 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mingling-frontend Ready Ready Preview, Comment Feb 25, 2026 1:59pm

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

Walkthrough

Google Analytics 4 추적을 통합하고, 회의 생성 페이지에서 용량 기본값 로직을 조정하며, 회의 생성 완료 후 GA4 이벤트를 전송하는 기능을 구현합니다.

Changes

Cohort / File(s) Summary
GA 통합 설정
app/layout.tsx, package.json
루트 레이아웃에 GoogleAnalytics 컴포넌트를 추가하고, 필요한 @next/third-parties 패키지 의존성을 추가했습니다.
회의 생성 로직
app/create/page.tsx
회의 생성 완료 후 GA4 이벤트(url_created)를 전송하고, "아직 안정해졌어요" 옵션 선택 시 용량 기본값을 30에서 10으로 변경했습니다. 브라우저 ID를 localStorage에서 관리하거나 신규 생성하여 GA 이벤트에 포함시킵니다.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested reviewers

  • kim3360
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed PR 설명이 템플릿 구조를 따르고 있으며, 주요 변경사항(GA4 기본 세팅, 모임 생성 이벤트 설정)을 명시하고 있고 체크리스트를 완료했습니다.
Title check ✅ Passed PR 제목이 변경 사항의 주요 내용을 정확하게 반영합니다. Google Analytics 기본 설정과 모임 생성 이벤트 설정이라는 두 가지 핵심 변경 사항을 명확하게 요약하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/ga4-basic-setting

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@kangdy25 kangdy25 changed the title chore/ga4 basic setting chore: Google Analytics 기본 세팅 및 모임 생성(create) 이벤트 설정 Feb 25, 2026
@kangdy25 kangdy25 requested a review from kim3360 February 25, 2026 14:03
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)가 소스 코드에 하드코딩되어 있습니다. 이렇게 하면 두 가지 문제가 생깁니다.

  1. 환경 오염: 개발/스테이징/운영 환경 모두 동일한 GA 속성으로 이벤트를 전송해 데이터가 오염됩니다.
  2. 유지보수 어려움: 측정 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

📥 Commits

Reviewing files that changed from the base of the PR and between 306aac2 and babf8e4.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • app/create/page.tsx
  • app/layout.tsx
  • package.json

Comment on lines +147 to +164
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',
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ 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.

@kangdy25 kangdy25 merged commit 32a9f89 into main Feb 26, 2026
4 checks passed
@kangdy25 kangdy25 deleted the chore/ga4-basic-setting branch February 26, 2026 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant