-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[YS-384] 믹스패널 도입 #86
base: develop
Are you sure you want to change the base?
[YS-384] 믹스패널 도입 #86
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
Walkthrough이번 풀 리퀘스트는 Mixpanel 분석 통합과 페이지 메타데이터 정의를 확장하는 변경 사항을 포함합니다. 여러 레이아웃 파일에 SEO와 페이지 정보를 위한 Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant LoginHook
participant Mixpanel
User->>LoginHook: 로그인 요청
LoginHook-->>User: 로그인 성공
LoginHook->>Mixpanel: identifyUser(사용자 이메일) 호출
LoginHook->>Mixpanel: setUserProperties({ email, role }) 호출
sequenceDiagram
participant User
participant HeaderMenu
participant Mixpanel
User->>HeaderMenu: 로그아웃 클릭
HeaderMenu->>Mixpanel: logoutUser() 호출
HeaderMenu-->>User: 홈으로 리다이렉션
Suggested reviewers
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
🧹 Nitpick comments (8)
src/app/edit/[post_id]/layout.tsx (1)
5-8
: 메타데이터 구현이 좋습니다만 설명을 조금 더 구체적으로 개선하세요.제목과 설명이 동일한 값('그라밋 | 공고 수정')으로 설정되어 있습니다. SEO 최적화를 위해서는 description을 더 구체적으로 작성하는 것이 좋습니다.
export const metadata: Metadata = { title: '그라밋 | 공고 수정', - description: '그라밋 | 공고 수정', + description: '그라밋에서 등록한 실험 공고를 수정할 수 있는 페이지입니다.', };src/app/post/[post_id]/components/ExperimentPostOutline/ExperimentPostOutline.tsx (1)
128-136
: 이벤트 추적 구현이 잘 되었습니다만 컨텍스트를 더 추가하면 좋을 것 같습니다.버튼 클릭 시 Mixpanel 이벤트 추적이 적절히 구현되었습니다. 하지만 이벤트 추적 시 현재 조회 중인 포스트 ID와 같은 추가 컨텍스트를 포함하면 더 유용한 분석 데이터를 얻을 수 있을 것입니다.
<button className={checkButton()} onClick={() => { trackEvent('ApplyMethod Interaction', { action: 'Click ApplyMethod Modal', + post_id: postDetailData.id, + post_title: postDetailData.title, }); setIsModalOpen(true); }} >src/app/login/hooks/useNaverLoginMutation.ts (1)
18-21
: 로그인 성공 시 사용자 추적 구현이 잘 되었습니다.로그인 성공 후 사용자를 식별하고 속성을 설정하는 구현이 적절합니다. 다만, Mixpanel 추적 기능에 오류가 발생하더라도 로그인 기능에 영향을 주지 않도록 에러 처리를 추가하는 것이 좋을 것 같습니다.
if (isRegistered) { API.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`; sessionStorage.setItem('refreshToken', refreshToken); sessionStorage.setItem('role', memberInfo.role); - identifyUser(memberInfo.oauthEmail); - setUserProperties({ email: memberInfo.oauthEmail, role: memberInfo.role }); + try { + identifyUser(memberInfo.oauthEmail); + setUserProperties({ email: memberInfo.oauthEmail, role: memberInfo.role }); + } catch (error) { + console.error('Mixpanel tracking failed:', error); + // 추적 실패해도 로그인 흐름은 계속 진행 + } router.push('/'); return; }src/app/login/hooks/useGoogleLoginMutation.ts (1)
19-21
: 사용자 식별 및 속성 설정이 잘 구현되었습니다.로그인 성공 후 사용자를 식별하고 속성을 설정하는 코드가 적절하게 배치되었습니다. 토큰 설정 후 페이지 리디렉션 전에 Mixpanel 추적 코드를 실행하는 위치가 최적의 위치입니다.
고려해볼 점: 현재는 등록된 사용자만 추적하고 있는데, 미등록 사용자의 회원가입 흐름도 추적하는 것이 분석에 도움이 될 수 있습니다. 필요하다면 26~28라인 사이에 추적 코드를 추가하는 것을 고려해보세요.
src/app/home/components/ExperimentPostContainer/ExperimentPostCardListContainer/ExperimentPostCardListContainer.tsx (1)
28-28
: 로딩 상태 변수 이름 명확화 필요
isLoading
을isListLoading
으로 재명명한 것은 혼동을 방지하는 좋은 시도이지만, 프로퍼티로 받는isLoading
과 구분이 여전히 명확하지 않을 수 있습니다.보다 명확한 변수명을 고려해보세요:
- isLoading: isListLoading, + isLoading: isPostListLoading, // 또는 isFetchingPostssrc/lib/mixpanelClient.ts (3)
1-2
: ESLint 규칙 비활성화에 대한 검토가 필요합니다.전역적으로 ESLint 규칙을 비활성화하는 것은 코드 품질에 영향을 줄 수 있습니다. 특히
@typescript-eslint/no-explicit-any
는 TypeScript의 타입 안전성을 저하시킵니다. 필요한 경우에만 특정 라인에 대해 규칙을 비활성화하는 것이 좋습니다.-/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable no-console */ +// 필요한 경우에만 특정 라인에 대해 규칙 비활성화
1-73
: 유틸리티 함수를 추가하여 코드 중복을 줄이는 것이 좋습니다.각 함수에서 반복되는 조건 검사를 하나의 유틸리티 함수로 추출하여 코드의 가독성과 유지보수성을 높일 수 있습니다.
다음과 같은 유틸리티 함수를 파일 상단에 추가할 수 있습니다:
const isClient = typeof window !== 'undefined'; /** * Mixpanel 작업을 안전하게 실행하는 유틸리티 함수 * @param callback Mixpanel 관련 작업을 수행하는 콜백 함수 */ const safelyRunMixpanel = (callback: () => void) => { if (!isClient) return; if (!MIXPANEL_TOKEN) return; try { if (!isMixpanelInitialized) { initMixpanel(); } callback(); } catch (error) { console.error('Mixpanel operation error:', error); } };이후 각 함수에서 이 유틸리티를 사용하여 코드를 간소화할 수 있습니다.
1-73
: 자동 페이지 추적 기능 추가를 고려해보세요.현재 구현은 수동으로 이벤트를 추적하는 방식입니다. Next.js의 라우터 이벤트를 활용하여 페이지 전환을 자동으로 추적하는 기능을 추가하면 유용할 것입니다.
다음과 같은 함수를 추가하여 Next.js 라우터와 통합할 수 있습니다:
/** * Next.js 라우터와 Mixpanel을 통합하여 페이지 뷰를 자동으로 추적 */ export const setupPageViewTracking = () => { if (!isClient) return; // Next.js 13 App Router if (typeof window !== 'undefined') { const handleRouteChange = (url: string) => { trackEvent('Page View', { 'Page URL': url, 'Page Title': document.title }); }; // pathname 변경 감지 let lastPathname = window.location.pathname; const observer = new MutationObserver(() => { const pathname = window.location.pathname; if (pathname !== lastPathname) { lastPathname = pathname; handleRouteChange(pathname); } }); observer.observe(document, { subtree: true, childList: true }); // 초기 페이지 로드 시 이벤트 발생 handleRouteChange(window.location.pathname); } };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (17)
package.json
(1 hunks)src/app/edit/[post_id]/layout.tsx
(1 hunks)src/app/home/components/ExperimentPostContainer/ExperimentPostCardListContainer/ExperimentPostCardListContainer.tsx
(2 hunks)src/app/join/layout.tsx
(1 hunks)src/app/login/hooks/useGoogleLoginMutation.ts
(2 hunks)src/app/login/hooks/useNaverLoginMutation.ts
(2 hunks)src/app/login/layout.tsx
(1 hunks)src/app/my-posts/layout.tsx
(1 hunks)src/app/post/[post_id]/components/ExperimentPostOutline/ExperimentPostOutline.tsx
(2 hunks)src/app/post/[post_id]/components/ParticipationGuideModal/ParticipationGuideModal.tsx
(3 hunks)src/app/post/[post_id]/layout.tsx
(1 hunks)src/app/providers.tsx
(2 hunks)src/app/upload/layout.tsx
(1 hunks)src/app/user/profile/layout.tsx
(1 hunks)src/components/Header/Menu.tsx
(2 hunks)src/components/Icon/icons/AlertOutlined.tsx
(1 hunks)src/lib/mixpanelClient.ts
(1 hunks)
🔇 Additional comments (20)
src/components/Icon/icons/AlertOutlined.tsx (1)
15-15
: JSX 속성명 표기법 수정 적절함JSX에서 SVG 속성을 사용할 때
clip-path
와 같은 하이픈 형식 대신clipPath
와 같은 카멜케이스를 사용해야 합니다. 이 변경은 React 컴포넌트 내에서 SVG 요소의 표기법을 표준화하는 올바른 접근입니다.src/app/join/layout.tsx (1)
1-1
: SEO 최적화를 위한 메타데이터 추가메타데이터를 추가하여 SEO 최적화와 페이지 정보를 명확히 한 것은 좋은 변경입니다. 이는 Mixpanel 도입과 함께 사용자 트래킹 개선을 위한 중요한 일부이며, 페이지의 식별과 분석에 도움이 됩니다.
Also applies to: 6-9
src/app/upload/layout.tsx (1)
1-2
: SEO 최적화를 위한 메타데이터 추가실험 공고 등록 페이지에 메타데이터를 추가한 것은 SEO 최적화와 페이지 식별에 도움이 됩니다. Mixpanel과 함께 사용하면 페이지별 사용자 행동을 더 정확히 추적할 수 있어 분석 데이터의 질을 향상시킵니다.
Also applies to: 5-8
package.json (1)
25-25
: Mixpanel 라이브러리 의존성 추가Mixpanel 브라우저 라이브러리와 해당 TypeScript 타입 정의를 추가한 것은 적절합니다. 이를 통해 PR 목표인 사용자 트래킹 및 분석 기능을 구현할 수 있습니다.
한 가지 고려할 점은 보안입니다. Mixpanel과 같은 외부 분석 도구는 사용자 데이터를 수집하므로 개인정보 보호법(GDPR, CCPA 등)을 준수해야 합니다. 추후 사용자 동의 메커니즘이 구현되어 있는지 확인하세요.
Also applies to: 33-33
src/app/post/[post_id]/layout.tsx (1)
1-8
: SEO를 위한 메타데이터 추가 적절합니다.Next.js의
Metadata
타입을 활용하여 페이지의 SEO 메타데이터를 적절하게 정의했습니다. 공고 조회 페이지에 맞는 제목과 설명을 추가한 것은 좋은 접근법입니다.src/app/login/layout.tsx (1)
1-8
: 로그인 페이지 메타데이터 구성이 잘 되었습니다.Next.js의
Metadata
타입을 사용하여 로그인 페이지에 적합한 메타데이터를 정의했습니다. 제목과 설명이 동일하게 설정되어 있는데, 이는 로그인 페이지의 간단한 성격을 고려하면 적절합니다.src/app/user/profile/layout.tsx (1)
1-10
: 회원 정보 페이지 메타데이터 구성이 잘 되었습니다.Next.js의
Metadata
타입을 활용하여 회원 정보 페이지의 메타데이터를 적절하게 정의했습니다. 제목과 설명이 페이지 목적에 맞게 설정되어 있습니다.src/app/my-posts/layout.tsx (2)
1-11
: 작성 글 목록 페이지 메타데이터 구성이 잘 되었습니다.Next.js의
Metadata
타입을 사용하여 작성 글 목록 페이지에 적합한 메타데이터를 정의했습니다. 이는 SEO 최적화에 도움이 될 것입니다.
8-11
:❓ Verification inconclusive
Mixpanel 트래킹 구현을 고려해보세요.
PR 목표에서 언급된 Mixpanel 트래킹 기능이 이 파일에는 구현되어 있지 않습니다. 페이지 뷰와 같은 이벤트를 트래킹하는 코드를 추가하는 것을 고려해보세요.
다른 파일에 Mixpanel 구현이 되어 있는지 확인이 필요합니다:
🏁 Script executed:
#!/bin/bash # Mixpanel 관련 구현 코드 검색 echo "Searching for Mixpanel imports and usage..." rg -i "mixpanel" --type ts --type tsxLength of output: 162
Mixpanel 트래킹 구현 검토 필요
현재
src/app/my-posts/layout.tsx
파일에는 페이지 뷰 등 이벤트 트래킹을 위한 Mixpanel 관련 코드가 구현되어 있지 않습니다. PR 목표에 언급된 Mixpanel 트래킹 기능 도입과 관련하여, 코드베이스 내 다른 파일에서도 Mixpanel 구현을 확인할 필요가 있습니다.단, 이전 검색 시 tsx 파일 타입 인식 문제로 인해 정확한 결과를 얻지 못했습니다. 수동 재검증을 위해 아래 명령어로
.ts
와.tsx
파일에서 Mixpanel 관련 사용 여부를 다시 확인해 보시기 바랍니다:#!/bin/bash # Mixpanel 관련 구현 재검증: .ts 및 .tsx 파일 검색 echo "Searching for Mixpanel imports and usage in .ts and .tsx files..." rg -i mixpanel --glob '*.ts' --glob '*.tsx'검증 결과에 따라 페이지 뷰 등의 이벤트 트래킹 추가 구현이 필요하시면 해당 사항을 반영해 주세요.
src/app/edit/[post_id]/layout.tsx (1)
1-1
: Metadata 타입 임포트 적절합니다.Next.js의
Metadata
타입을 올바르게 가져와서 타입 안전성을 확보했습니다.src/app/post/[post_id]/components/ExperimentPostOutline/ExperimentPostOutline.tsx (1)
28-28
: Mixpanel 이벤트 추적 기능 추가가 잘 되었습니다.Mixpanel의
trackEvent
함수를 올바르게 가져와 사용자 상호작용을 추적할 준비가 되었습니다.src/app/providers.tsx (1)
5-7
: 필요한 함수와 훅을 올바르게, 간결하게 임포트했습니다.Mixpanel 기능과 React 훅을 효과적으로 가져왔습니다.
src/app/login/hooks/useNaverLoginMutation.ts (1)
6-6
: Mixpanel 사용자 추적 함수 임포트가 올바르게 되었습니다.Mixpanel 사용자 식별 및 속성 설정 함수를 적절하게 가져왔습니다.
src/components/Header/Menu.tsx (2)
10-10
: 로그아웃 추적 기능 구현이 잘 되었네요!Mixpanel 로그아웃 기능을 가져오는 import 문이 잘 추가되었습니다.
23-23
: 로그아웃 시 사용자 추적 구현이 적절합니다.로그아웃 함수에서 세션 스토리지를 지우기 전에
logoutUser()
를 호출하여 사용자의 로그아웃 이벤트를 추적하는 것은 좋은 구현입니다. 리다이렉션 전에 추적 이벤트를 발생시키는 위치가 적절합니다.src/app/login/hooks/useGoogleLoginMutation.ts (1)
6-6
: Mixpanel 사용자 식별 함수 가져오기 적절합니다.로그인 과정에서 사용자를 식별하고 속성을 설정하기 위한 함수들을 적절히 import 했습니다.
src/app/post/[post_id]/components/ParticipationGuideModal/ParticipationGuideModal.tsx (2)
22-22
: Mixpanel 이벤트 추적 함수 가져오기 적절합니다.사용자 상호작용을 추적하기 위한
trackEvent
함수를 적절히 import 했습니다.
96-101
: 링크 클릭 추적 구현이 잘 되었습니다.외부 링크 클릭을 추적하는 코드가 적절하게 구현되었으며, 이벤트 속성에 링크 URL을 포함시켜 더 구체적인 분석이 가능하도록 한 점이 좋습니다.
src/app/home/components/ExperimentPostContainer/ExperimentPostCardListContainer/ExperimentPostCardListContainer.tsx (2)
12-14
: 로딩 상태 UI 구현을 위한 컴포넌트 가져오기 적절합니다.로딩 상태를 표시하기 위한 UI 컴포넌트와 스타일을 재사용하여 가져오는 것은 좋은 접근 방식입니다.
31-38
: 로딩 상태 UI 구현이 잘 되었습니다.로딩 중 스피너와 텍스트를 표시하는 UI가 깔끔하게 구현되었습니다. 사용자 경험을 향상시키는 좋은 개선사항입니다.
useEffect(() => { | ||
if (typeof window !== 'undefined') { | ||
const pageTitle = document.title; | ||
|
||
trackEvent('Page Viewed', { page: pageTitle }); | ||
setUserProperties({ last_visited_page: pageTitle }); | ||
} | ||
}, []); // |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
페이지 조회 추적 구현에 개선이 필요합니다.
페이지 조회 이벤트 추적의 기본 구현은 좋지만, 몇 가지 개선이 필요합니다:
- 빈 의존성 배열(
[]
)을 사용하면 컴포넌트가 마운트될 때만 효과가 실행됩니다. SPA에서 페이지 간 이동 시 컴포넌트는 다시 마운트되지 않으므로 페이지 변경이 추적되지 않습니다. - 34번 줄의 주석(
//
)은 불필요합니다.
Next.js의 usePathname
을 사용하여 경로 변경 시마다 이벤트를 추적하도록 수정하세요:
import { isServer, QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { useEffect } from 'react';
+import { usePathname } from 'next/navigation';
import { setUserProperties, trackEvent } from '@/lib/mixpanelClient';
import MSWProvider from '@/mocks/MSWProvider';
function makeQueryClient() {
return new QueryClient();
}
let browserQueryClient: QueryClient | undefined = undefined;
function getQueryClient() {
if (isServer) {
return makeQueryClient();
} else {
if (!browserQueryClient) browserQueryClient = makeQueryClient();
return browserQueryClient;
}
}
export default function Providers({ children }: { children: React.ReactNode }) {
const queryClient = getQueryClient();
+ const pathname = usePathname();
useEffect(() => {
if (typeof window !== 'undefined') {
const pageTitle = document.title;
trackEvent('Page Viewed', { page: pageTitle, path: pathname });
setUserProperties({ last_visited_page: pageTitle });
}
- }, []); //
+ }, [pathname]);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
useEffect(() => { | |
if (typeof window !== 'undefined') { | |
const pageTitle = document.title; | |
trackEvent('Page Viewed', { page: pageTitle }); | |
setUserProperties({ last_visited_page: pageTitle }); | |
} | |
}, []); // | |
import { isServer, QueryClient, QueryClientProvider } from '@tanstack/react-query'; | |
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; | |
import { useEffect } from 'react'; | |
import { usePathname } from 'next/navigation'; | |
import { setUserProperties, trackEvent } from '@/lib/mixpanelClient'; | |
import MSWProvider from '@/mocks/MSWProvider'; | |
function makeQueryClient() { | |
return new QueryClient(); | |
} | |
let browserQueryClient: QueryClient | undefined = undefined; | |
function getQueryClient() { | |
if (isServer) { | |
return makeQueryClient(); | |
} else { | |
if (!browserQueryClient) browserQueryClient = makeQueryClient(); | |
return browserQueryClient; | |
} | |
} | |
export default function Providers({ children }: { children: React.ReactNode }) { | |
const queryClient = getQueryClient(); | |
const pathname = usePathname(); | |
useEffect(() => { | |
if (typeof window !== 'undefined') { | |
const pageTitle = document.title; | |
trackEvent('Page Viewed', { page: pageTitle, path: pathname }); | |
setUserProperties({ last_visited_page: pageTitle }); | |
} | |
}, [pathname]); | |
return ( | |
<QueryClientProvider client={queryClient}> | |
<MSWProvider>{children}</MSWProvider> | |
<ReactQueryDevtools /> | |
</QueryClientProvider> | |
); | |
} |
trackEvent('ApplyMethod Interaction', { | ||
action: 'Link Copied', | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
클립보드 복사 이벤트 추적 개선 필요
클립보드 복사가 성공적으로 완료된 후에만 이벤트를 추적하도록 코드 위치를 조정하는 것이 좋겠습니다. 현재 구현에서는 복사 작업이 실패해도 이벤트가 기록될 수 있습니다.
다음과 같이 수정해보세요:
const handleCopyContent = (text: string) => {
navigator.clipboard.writeText(text).then(() => {
setIsCopyToastOpen(true);
+ trackEvent('ApplyMethod Interaction', {
+ action: 'Link Copied',
+ });
});
-
- trackEvent('ApplyMethod Interaction', {
- action: 'Link Copied',
- });
};
export const logoutUser = () => { | ||
if (!MIXPANEL_TOKEN) return; | ||
|
||
mixpanel.reset(); | ||
// console.log('Mixpanel user data reset'); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
logoutUser 함수에 SSR 대응 및 불필요한 주석 제거가 필요합니다.
서버 사이드 체크 및 주석 처리된 콘솔 로그 제거가 필요합니다.
export const logoutUser = () => {
+ if (!isClient) return;
if (!MIXPANEL_TOKEN) return;
+ try {
mixpanel.reset();
- // console.log('Mixpanel user data reset');
+ } catch (error) {
+ console.error('Mixpanel reset error:', error);
+ }
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export const logoutUser = () => { | |
if (!MIXPANEL_TOKEN) return; | |
mixpanel.reset(); | |
// console.log('Mixpanel user data reset'); | |
}; | |
export const logoutUser = () => { | |
if (!isClient) return; | |
if (!MIXPANEL_TOKEN) return; | |
try { | |
mixpanel.reset(); | |
} catch (error) { | |
console.error('Mixpanel reset error:', error); | |
} | |
}; |
export const identifyUser = (userId: string) => { | ||
if (!MIXPANEL_TOKEN) return; | ||
mixpanel.identify(userId); | ||
mixpanel.people.set_once({ | ||
signup_date: new Date().toISOString(), // 최초 가입 시점 기록 | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
identifyUser 함수에 SSR 대응과 예외 처리가 필요합니다.
서버 사이드 렌더링과 잠재적인 오류 처리를 위한 개선이 필요합니다.
export const identifyUser = (userId: string) => {
+ if (!isClient) return;
if (!MIXPANEL_TOKEN) return;
+
+ try {
mixpanel.identify(userId);
mixpanel.people.set_once({
signup_date: new Date().toISOString(), // 최초 가입 시점 기록
});
+ } catch (error) {
+ console.error('Mixpanel identify error:', error);
+ }
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export const identifyUser = (userId: string) => { | |
if (!MIXPANEL_TOKEN) return; | |
mixpanel.identify(userId); | |
mixpanel.people.set_once({ | |
signup_date: new Date().toISOString(), // 최초 가입 시점 기록 | |
}); | |
}; | |
export const identifyUser = (userId: string) => { | |
if (!isClient) return; | |
if (!MIXPANEL_TOKEN) return; | |
try { | |
mixpanel.identify(userId); | |
mixpanel.people.set_once({ | |
signup_date: new Date().toISOString(), // 최초 가입 시점 기록 | |
}); | |
} catch (error) { | |
console.error('Mixpanel identify error:', error); | |
} | |
}; |
export const initMixpanel = () => { | ||
if (!MIXPANEL_TOKEN) { | ||
console.warn('Mixpanel token is missing! Check your .env file.'); | ||
return; | ||
} | ||
|
||
if (!isMixpanelInitialized) { | ||
mixpanel.init(MIXPANEL_TOKEN, { | ||
debug: process.env.NODE_ENV === 'development', | ||
track_pageview: false, | ||
persistence: 'localStorage', | ||
}); | ||
isMixpanelInitialized = true; | ||
// console.log('Mixpanel initialized'); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
initMixpanel 함수에 SSR 대응 로직이 필요합니다.
서버 사이드 렌더링 환경에서 안전하게 실행되도록 클라이언트 측 체크가 필요합니다. 또한 주석 처리된 콘솔 로그는 제거하는 것이 좋습니다.
export const initMixpanel = () => {
+ if (!isClient) return;
if (!MIXPANEL_TOKEN) {
console.warn('Mixpanel token is missing! Check your .env file.');
return;
}
if (!isMixpanelInitialized) {
mixpanel.init(MIXPANEL_TOKEN, {
debug: process.env.NODE_ENV === 'development',
track_pageview: false,
persistence: 'localStorage',
});
isMixpanelInitialized = true;
- // console.log('Mixpanel initialized');
}
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export const initMixpanel = () => { | |
if (!MIXPANEL_TOKEN) { | |
console.warn('Mixpanel token is missing! Check your .env file.'); | |
return; | |
} | |
if (!isMixpanelInitialized) { | |
mixpanel.init(MIXPANEL_TOKEN, { | |
debug: process.env.NODE_ENV === 'development', | |
track_pageview: false, | |
persistence: 'localStorage', | |
}); | |
isMixpanelInitialized = true; | |
// console.log('Mixpanel initialized'); | |
} | |
}; | |
export const initMixpanel = () => { | |
if (!isClient) return; | |
if (!MIXPANEL_TOKEN) { | |
console.warn('Mixpanel token is missing! Check your .env file.'); | |
return; | |
} | |
if (!isMixpanelInitialized) { | |
mixpanel.init(MIXPANEL_TOKEN, { | |
debug: process.env.NODE_ENV === 'development', | |
track_pageview: false, | |
persistence: 'localStorage', | |
}); | |
isMixpanelInitialized = true; | |
} | |
}; |
export const trackEvent = (event: string, properties?: Record<string, string>) => { | ||
if (!MIXPANEL_TOKEN) { | ||
console.warn('Mixpanel Token is missing.'); | ||
return; | ||
} | ||
|
||
if (!isMixpanelInitialized) { | ||
// console.warn('Mixpanel is not initialized. Initializing now...'); | ||
initMixpanel(); | ||
} | ||
mixpanel.track(event, properties); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
trackEvent 함수의 타입 제한과 SSR 대응이 필요합니다.
현재 properties
는 문자열 값만 허용하나, Mixpanel은 숫자, 불리언, 객체 등 다양한 타입을 지원합니다. 또한 서버 사이드 체크가 필요합니다.
-export const trackEvent = (event: string, properties?: Record<string, string>) => {
+export const trackEvent = (event: string, properties?: Record<string, any>) => {
+ if (!isClient) return;
+
if (!MIXPANEL_TOKEN) {
console.warn('Mixpanel Token is missing.');
return;
}
if (!isMixpanelInitialized) {
- // console.warn('Mixpanel is not initialized. Initializing now...');
initMixpanel();
}
mixpanel.track(event, properties);
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export const trackEvent = (event: string, properties?: Record<string, string>) => { | |
if (!MIXPANEL_TOKEN) { | |
console.warn('Mixpanel Token is missing.'); | |
return; | |
} | |
if (!isMixpanelInitialized) { | |
// console.warn('Mixpanel is not initialized. Initializing now...'); | |
initMixpanel(); | |
} | |
mixpanel.track(event, properties); | |
}; | |
export const trackEvent = (event: string, properties?: Record<string, any>) => { | |
if (!isClient) return; | |
if (!MIXPANEL_TOKEN) { | |
console.warn('Mixpanel Token is missing.'); | |
return; | |
} | |
if (!isMixpanelInitialized) { | |
initMixpanel(); | |
} | |
mixpanel.track(event, properties); | |
}; |
export const setUserProperties = (properties: Record<string, string>) => { | ||
if (!MIXPANEL_TOKEN) return; | ||
mixpanel.people.set(properties); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
setUserProperties 함수의 타입 및 에러 처리 개선이 필요합니다.
타입 확장 및 예외 처리, SSR 대응이 필요합니다.
-export const setUserProperties = (properties: Record<string, string>) => {
+export const setUserProperties = (properties: Record<string, any>) => {
+ if (!isClient) return;
if (!MIXPANEL_TOKEN) return;
+
+ try {
mixpanel.people.set(properties);
+ } catch (error) {
+ console.error('Mixpanel set properties error:', error);
+ }
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export const setUserProperties = (properties: Record<string, string>) => { | |
if (!MIXPANEL_TOKEN) return; | |
mixpanel.people.set(properties); | |
}; | |
export const setUserProperties = (properties: Record<string, any>) => { | |
if (!isClient) return; | |
if (!MIXPANEL_TOKEN) return; | |
try { | |
mixpanel.people.set(properties); | |
} catch (error) { | |
console.error('Mixpanel set properties error:', error); | |
} | |
}; |
Issue Number
close #81
As-Is
믹스 패널 도입 x
To-Be
믹스 패널 도입
Check List
Test Screenshot
(Optional) Additional Description
믹스 패널 도입 및 PM님의 요청사항 중 일부 trackingEvent만 추가하여 테스트 했습니다.
이외에도 믹스 패널에서 기본으로 제공하는 트래킹(최초 방문 시 유입 경로 등)을 확인하였습니다.
서버에선 실행이 되지 않도록 window 객체 접근 가능한지 여부로 분기 처리를 하였고, pathname (ex. /edit/AD31D, /upload 등)으로 page 이름을 등록할 수 있으나 좀 더 명확히 구분하기 위해 metadata 추가 및 document.tilte을 가져와 page 이름으로 기록 되게 하였습니다.
머지 이후 배포 환경에서의 트래킹도 잘 되는지 확인할 예정입니다. 확인이 끝나면 아직 도입 못한 유저 트래킹 목록 중 구현 가능한 트래킹을 추가하겠습니다. 믹스 패널을 처음 사용해봐서 조금 더 공부가 필요합니당,, 수집 중인 데이터 및 조회 방법은 따로 정리해서 팀 전체 공유 드리겠습니다 🙌
📍
.env
에 mix panel 토큰을 추가하였습니다. 노션 확인해주세요!Summary by CodeRabbit