refactor: 전역 트래킹 코드를 dataLayer.push 객체 형태로 변경#48
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughGoogle Analytics의 Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
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: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/recommend/page.tsx (1)
151-163:⚠️ Potential issue | 🟡 Minor
user_cookie_id가 비어 있는 이벤트가 발생할 수 있습니다.
external_map_opened에서browser_id미존재 시 생성하지 않아 식별 누락이 생길 수 있습니다. 다른 화면과 동일하게 생성/저장 fallback을 맞춰주세요.수정 예시
- const browserId = localStorage.getItem('browser_id'); + let browserId = localStorage.getItem('browser_id'); + if (!browserId) { + browserId = `bid_${Math.random().toString(36).substring(2, 15)}${Date.now().toString(36)}`; + localStorage.setItem('browser_id', browserId); + } @@ - user_cookie_id: browserId, + user_cookie_id: browserId,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/recommend/page.tsx` around lines 151 - 163, external_map_opened 이벤트에서 localStorage.getItem('browser_id')가 없으면 user_cookie_id가 비어 전송될 수 있으니, page 컴포넌트의 해당 블록에서 browser_id를 읽는 부분을 찾아(pushDataLayer 호출 바로 앞의 localStorage.getItem('browser_id') 사용) 브라우저 아이디가 없을 경우 새로 생성해 localStorage에 저장하고 그 값을 user_cookie_id로 사용하도록 보완하세요; 즉 meetingId/ place 체크 후 browserId 생성 로직을 추가하고 pushDataLayer에 생성된 값(또는 기존 값)을 항상 전달하도록 수정하십시오.
🧹 Nitpick comments (2)
app/meeting/[id]/page.tsx (1)
79-84: 브라우저/역할 식별 로직은 공통 헬퍼로 추출을 권장합니다.동일 로직이 반복되어 이벤트 스키마 수정 시 누락 가능성이 큽니다.
lib/gtm.ts또는 별도 util로getOrCreateBrowserId,getUserRole를 공통화하면 안전합니다.Also applies to: 141-149, 176-180
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/meeting/`[id]/page.tsx around lines 79 - 84, Extract the repeated browser/role identification logic into reusable helpers (e.g., getOrCreateBrowserId and getUserRole) placed in lib/gtm.ts or a new util; replace the localStorage-based inline logic in app/meeting/[id]/page.tsx (the blocks that generate browser_id with Math.random and Date.now and the role-detection code used in multiple places) with calls to these helpers so all three occurrences use the same implementation and schema. Ensure getOrCreateBrowserId returns the existing localStorage browser_id or creates, stores, and returns the new id, and getUserRole centralizes role derivation logic; update imports and calls in page.tsx to use these functions everywhere the original inline logic was used.app/create/page.tsx (1)
172-173: 변수명purposesStr는 실제 타입과 맞지 않습니다.Line 172의 값은 문자열이 아니라 배열(
string[])이라purposesList또는purposes같은 이름이 더 정확합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/create/page.tsx` around lines 172 - 173, The variable purposesStr is misnamed because getPurposes() returns a string[]; rename purposesStr to a more accurate name like purposes or purposesList wherever it's declared and used (e.g., the const currently assigned from getPurposes()) and update any subsequent references (such as the if check "if (purposesStr.length > 0)") to use the new identifier to reflect the actual array type returned by getPurposes().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/join/joinForm.tsx`:
- Around line 69-88: The current pushDataLayer call (event
'participant_registration') inside joinForm.tsx runs before the API result and
thus records failures as conversions; update the logic so pushDataLayer is
invoked only after a successful registration response (move the block into the
API success callback/then branch where the registration response is handled) and
keep using meetingId, browserId, isRemembered and userRole when sending, or if
you need to track attempts separately rename the pre-call event to
'participant_registration_attempt' and send that before the API while reserving
'participant_registration' for the success path (use pushDataLayer and the same
payload keys).
---
Outside diff comments:
In `@app/recommend/page.tsx`:
- Around line 151-163: external_map_opened 이벤트에서
localStorage.getItem('browser_id')가 없으면 user_cookie_id가 비어 전송될 수 있으니, page 컴포넌트의
해당 블록에서 browser_id를 읽는 부분을 찾아(pushDataLayer 호출 바로 앞의
localStorage.getItem('browser_id') 사용) 브라우저 아이디가 없을 경우 새로 생성해 localStorage에 저장하고
그 값을 user_cookie_id로 사용하도록 보완하세요; 즉 meetingId/ place 체크 후 browserId 생성 로직을 추가하고
pushDataLayer에 생성된 값(또는 기존 값)을 항상 전달하도록 수정하십시오.
---
Nitpick comments:
In `@app/create/page.tsx`:
- Around line 172-173: The variable purposesStr is misnamed because
getPurposes() returns a string[]; rename purposesStr to a more accurate name
like purposes or purposesList wherever it's declared and used (e.g., the const
currently assigned from getPurposes()) and update any subsequent references
(such as the if check "if (purposesStr.length > 0)") to use the new identifier
to reflect the actual array type returned by getPurposes().
In `@app/meeting/`[id]/page.tsx:
- Around line 79-84: Extract the repeated browser/role identification logic into
reusable helpers (e.g., getOrCreateBrowserId and getUserRole) placed in
lib/gtm.ts or a new util; replace the localStorage-based inline logic in
app/meeting/[id]/page.tsx (the blocks that generate browser_id with Math.random
and Date.now and the role-detection code used in multiple places) with calls to
these helpers so all three occurrences use the same implementation and schema.
Ensure getOrCreateBrowserId returns the existing localStorage browser_id or
creates, stores, and returns the new id, and getUserRole centralizes role
derivation logic; update imports and calls in page.tsx to use these functions
everywhere the original inline logic was used.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
app/create/page.tsxapp/meeting/[id]/page.tsxapp/recommend/page.tsxapp/result/[id]/page.tsxcomponents/join/joinForm.tsxcomponents/share/shareContent.tsxlib/gtm.ts
| // ⭐️ 1. 비즈니스 로직 실행 전 GA 이벤트 선(先) 전송! (dataLayer 방식 적용) | ||
| if (typeof window !== 'undefined') { | ||
| let browserId = localStorage.getItem('browser_id'); | ||
| if (!browserId) { | ||
| browserId = `bid_${Math.random().toString(36).substring(2, 15)}${Date.now().toString(36)}`; | ||
| localStorage.setItem('browser_id', browserId); | ||
| } | ||
|
|
||
| // ⭐️ 개발자님의 완벽한 엣지케이스 방어 로직! | ||
| // 로컬스토리지에 '이 방의 생성자(방장)'라는 징표가 있는지 확인 | ||
| // 방장/참여자 구분 로직 | ||
| const isHost = localStorage.getItem(`is_host_${meetingId}`) === 'true'; | ||
| const userRole = isHost ? 'host' : 'participant'; | ||
|
|
||
| sendGAEvent('event', 'participant_registration', { | ||
| pushDataLayer({ | ||
| event: 'participant_registration', | ||
| meeting_url_id: meetingId, | ||
| user_cookie_id: browserId, // 생성 때 썼던 그 browserId와 일치하게 됨! | ||
| role: userRole, // 완벽하게 방장/참여자 구분 완료 | ||
| user_cookie_id: browserId, | ||
| role: userRole, | ||
| remember_me: isRemembered ? 'yes' : 'no', | ||
| }); | ||
| } |
There was a problem hiding this comment.
participant_registration 이벤트가 실패 케이스에도 기록됩니다.
현재는 API 성공 여부와 무관하게 전송되어 전환 지표가 과대 집계될 수 있습니다. 성공 시점으로 이동하거나, 시도 이벤트로 이름을 분리해 주세요.
수정 예시 (성공 시점 전송)
- // ⭐️ 1. 비즈니스 로직 실행 전 GA 이벤트 선(先) 전송! (dataLayer 방식 적용)
- if (typeof window !== 'undefined') {
- let browserId = localStorage.getItem('browser_id');
- if (!browserId) {
- browserId = `bid_${Math.random().toString(36).substring(2, 15)}${Date.now().toString(36)}`;
- localStorage.setItem('browser_id', browserId);
- }
-
- // 방장/참여자 구분 로직
- const isHost = localStorage.getItem(`is_host_${meetingId}`) === 'true';
- const userRole = isHost ? 'host' : 'participant';
-
- pushDataLayer({
- event: 'participant_registration',
- meeting_url_id: meetingId,
- user_cookie_id: browserId,
- role: userRole,
- remember_me: isRemembered ? 'yes' : 'no',
- });
- }
-
// ⭐️ 2. 기존 참여 API 호출 로직
try {
// `@ts-ignore`
const result = await participantEnter.mutateAsync({
@@
if (result.success) {
+ if (typeof window !== 'undefined') {
+ let browserId = localStorage.getItem('browser_id');
+ if (!browserId) {
+ browserId = `bid_${Math.random().toString(36).substring(2, 15)}${Date.now().toString(36)}`;
+ localStorage.setItem('browser_id', browserId);
+ }
+ const isHost = localStorage.getItem(`is_host_${meetingId}`) === 'true';
+ const userRole = isHost ? 'host' : 'participant';
+ pushDataLayer({
+ event: 'participant_registration',
+ meeting_url_id: meetingId,
+ user_cookie_id: browserId,
+ role: userRole,
+ remember_me: isRemembered ? 'yes' : 'no',
+ });
+ }
setMeetingUserId(meetingId, name, isRemembered);
router.push(`/meeting/${meetingId}`);
} else {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/join/joinForm.tsx` around lines 69 - 88, The current pushDataLayer
call (event 'participant_registration') inside joinForm.tsx runs before the API
result and thus records failures as conversions; update the logic so
pushDataLayer is invoked only after a successful registration response (move the
block into the API success callback/then branch where the registration response
is handled) and keep using meetingId, browserId, isRemembered and userRole when
sending, or if you need to track attempts separately rename the pre-call event
to 'participant_registration_attempt' and send that before the API while
reserving 'participant_registration' for the success path (use pushDataLayer and
the same payload keys).
🚀 refactor: 전역 트래킹 코드를 dataLayer.push 객체 형태로 변경
📝 변경사항
✅ 체크리스트
📸 스크린샷
💬 리뷰어 전달사항
Summary by CodeRabbit
릴리스 노트
참고: 이번 업데이트는 내부 기술 개선사항으로, 사용자가 인지할 수 있는 기능 변화는 없습니다.