[analytics] 총동연 페이지, 지원서 페이지 방문 및 체류시간 로깅#684
Conversation
…ation-MOA-171 [feature] 총동연 페이지 방문 및 체류시간을 로깅한다
- clubId 없으면 요청 안 함
- 조기 리턴(if !clubId) 아래로 이동하고, 데이터/트래킹 훅들을 상단으로 배치해 훅 규칙 위반 방지
- ApplicationFormPage: useTrackPageView에 clubName 폴백 적용
…ation-MOA-172 [feature] 동아리 지원서 페이지 방문, 체류시간 로깅한다
|
Warning
|
| Cohort / File(s) | Summary |
|---|---|
쿼리 조건 게이팅frontend/src/hooks/queries/application/useGetApplication.ts |
useQuery에 enabled: !!clubId 추가로 clubId가 유효할 때만 쿼리 실행. 나머지 옵션은 동일. |
페이지뷰 트래킹 훅 내부 변경frontend/src/hooks/useTrackPageView.ts |
useEffect 의존성에서 clubName 제거. location.pathname 변경 시에만 재실행. |
ApplicationFormPage 데이터/트래킹 통합frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx |
상단에서 useGetClubDetail(clubId!), useGetApplication(clubId!)로 선 패칭하고 가드/에러 처리에 사용. useTrackPageView 도입(라벨: clubDetail?.name 또는 club:<id>). 중복 패칭 블록 제거. |
ClubUnionPage 트래킹 도입frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx |
useTrackPageView('ClubUnionPage') 호출 추가. 불필요한 스타일/헤더 import 제거. |
Sequence Diagram(s)
sequenceDiagram
autonumber
actor User
participant Page as ApplicationFormPage
participant Track as useTrackPageView
participant RQ as React Query
participant API as Backend API
User->>Page: 방문 (/clubs/:id/apply)
rect rgba(200,220,255,0.2)
note right of Page: 초기화/상단 통합
Page->>Track: trackPageView(label: clubName? or "club:<id>")
end
alt clubId가 유효함 (enabled: true)
Page->>RQ: useGetClubDetail(clubId)
Page->>RQ: useGetApplication(clubId)
RQ->>API: GET /clubs/:id, GET /applications/:clubId
API-->>RQ: 데이터 반환
RQ-->>Page: {clubDetail, formData}
Page->>User: 로딩→콘텐츠 렌더/검증/제출
else clubId 없음/무효 (enabled: false)
Page->>User: null 또는 가드 처리
end
opt 에러
RQ-->>Page: isError/clubError
Page->>User: 알림 및 네비게이션
end
sequenceDiagram
autonumber
actor User
participant CUP as ClubUnionPage
participant Track as useTrackPageView
User->>CUP: 방문 (/club-union)
CUP->>Track: trackPageView('ClubUnionPage')
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
- [feature] 동아리 지원서 페이지 방문, 체류시간 로깅한다 #683: 동일 범위의 훅 게이팅, 트래킹 훅 의존성 변경, ApplicationFormPage 통합 패칭 적용.
- [feature] 지원서 제작/수정/불러오기 API 연결하기 #499:
useGetApplication훅 도입/변경과 직접적으로 연관. - [fix] useRef에서 startTime.current 프로퍼티 누락 오류 수정 #408:
useTrackPageView훅 구현/사용 패턴 변경과 밀접히 관련.
Suggested labels
✨ Feature, 💻 FE
Suggested reviewers
- oesnuj
- PororoAndFriends
- suhyun113
Tip
🔌 Remote MCP (Model Context Protocol) integration is now available!
Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.
✨ Finishing Touches
- 📝 Generate Docstrings
🧪 Generate unit tests
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
develop-fe
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.
🪧 Tips
Chat
There are 3 ways to chat with CodeRabbit:
- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
I pushed a fix in commit <commit_id>, please review it.Open a follow-up GitHub issue for this discussion.
- Files and specific lines of code (under the "Files changed" tab): Tag
@coderabbitaiin a new review comment at the desired location with your query. - PR comments: Tag
@coderabbitaiin a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
Support
Need help? Create a ticket on our support page for assistance with any issues or questions.
CodeRabbit Commands (Invoked using PR/Issue comments)
Type @coderabbitai help to get the list of available commands.
Other keywords and placeholders
- Add
@coderabbitai ignoreanywhere in the PR description to prevent this PR from being reviewed. - Add
@coderabbitai summaryto generate the high-level summary at a specific location in the PR description. - Add
@coderabbitaianywhere in the PR title to generate the title automatically.
Status, Documentation and Community
- Visit our Status Page to check the current availability of CodeRabbit.
- Visit our Documentation for detailed information on how to use CodeRabbit.
- Join our Discord Community to get help, request features, and share feedback.
- Follow us on X/Twitter for updates and announcements.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/hooks/useTrackPageView.ts (1)
10-43: visibilitychange 리스너 해제 버그와 세션 리셋 누락으로 인한 중복/누락 트래킹
- 추가한 리스너를 익명 함수로 등록 후 다른 참조(
trackPageDuration)로 제거하고 있어 제거가 되지 않습니다. 메모리 누수 및 중복 트래킹 위험이 큽니다.- pathname 변경 시 새 페이지 세션으로 리셋하지 않아, 첫 페이지 이후 Duration이 기록되지 않거나 잘못 기록될 수 있습니다(
isTracked미리 true 상태 유지,startTime도 갱신 안 됨).- clubName을 deps에서 제거한 설계 의도(클럽명만 바뀌어도 PageView 재발행 방지)는 이해되지만, Duration에는 최신 clubName을 반영할 수 있도록 ref로 최신 값을 추적하는 편이 좋습니다.
아래 패치로 한 번에 해결 가능합니다.
import { useEffect, useRef } from 'react'; import { useLocation } from 'react-router-dom'; import mixpanel from 'mixpanel-browser'; const useTrackPageView = (pageName: string, clubName?: string) => { const location = useLocation(); const isTracked = useRef(false); const startTime = useRef(Date.now()); + // 최신 clubName을 Duration에 반영하기 위한 ref + const clubNameRef = useRef(clubName); + + // clubName 변경 시 최신값 보관 (Visited는 pathname 기준 1회 발행 유지) + useEffect(() => { + clubNameRef.current = clubName; + }, [clubName]); useEffect(() => { + // 새 페이지 진입 시 세션 리셋 + startTime.current = Date.now(); + isTracked.current = false; + mixpanel.track(`${pageName} Visited`, { url: window.location.href, timestamp: startTime.current, referrer: document.referrer || 'direct', clubName, }); const trackPageDuration = () => { if (isTracked.current) return; const duration = Date.now() - startTime.current; mixpanel.track(`${pageName} Duration`, { url: window.location.href, duration: duration, duration_seconds: Math.round(duration / 1000), - clubName, + clubName: clubNameRef.current, }); isTracked.current = true; }; - window.addEventListener('beforeunload', trackPageDuration); - document.addEventListener('visibilitychange', () => { - if (document.hidden) { - trackPageDuration(); - } - }); + window.addEventListener('beforeunload', trackPageDuration); + const handleVisibilityChange = () => { + if (document.hidden) { + trackPageDuration(); + } + }; + document.addEventListener('visibilitychange', handleVisibilityChange); return () => { trackPageDuration(); window.removeEventListener('beforeunload', trackPageDuration); - document.removeEventListener('visibilitychange', trackPageDuration); + document.removeEventListener('visibilitychange', handleVisibilityChange); }; - }, [location.pathname]); + }, [location.pathname]); }; export default useTrackPageView;
🧹 Nitpick comments (2)
frontend/src/hooks/queries/application/useGetApplication.ts (1)
4-10: useGetApplication의 clubId 파라미터를 optional로 변경하여 호출부의!단언을 제거하세요현재 아래 두 파일에서
useGetApplication(clubId!)호출이 발생합니다. hook 내부의enabled: !!clubId로 이미 호출을 보호하고 있으므로, 파라미터를 optional로 변경해 호출부의 non-null 단언을 제거할 수 있습니다.
- frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx:44
- frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx:30
아래 예시와 같이 수정하면 됩니다:
-export const useGetApplication = (clubId: string) => { +export const useGetApplication = (clubId?: string) => { return useQuery<ApplicationFormData>({ queryKey: ['applicationForm', clubId], - queryFn: () => getApplication(clubId), + queryFn: () => getApplication(clubId!), retry: false, enabled: !!clubId, }); };호출부에서는 이제
useGetApplication(clubId)로 변경해!단언을 제거하세요.
또한useQuery제네릭으로ApplicationFormData(실제 타입 이름)에 맞춰 타입을 명시하면formData사용처의 타입 안정성이 더욱 향상됩니다.frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (1)
24-31:clubId!제거를 위한 hook 시그니처 정리 제안현재 두 쿼리 훅 모두에
clubId!가 필요합니다. 훅 시그니처를string | undefined로 바꾸고 내부에서enabled: !!clubId로 보호하면 호출부의 단언을 제거할 수 있습니다.아래 변경은 훅 시그니처 조정 후 적용하세요.
- const { data: clubDetail, error: clubError } = useGetClubDetail(clubId!); + const { data: clubDetail, error: clubError } = useGetClubDetail(clubId); const { data: formData, isLoading, isError, error: applicationError, - } = useGetApplication(clubId!); + } = useGetApplication(clubId);별도 참고:
useGetClubDetail도enabled: !!clubId를 이미 사용한다면 동일하게 optional 인자를 받도록 정리하는 것이 일관적입니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
frontend/src/hooks/queries/application/useGetApplication.ts(1 hunks)frontend/src/hooks/useTrackPageView.ts(1 hunks)frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx(2 hunks)frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
frontend/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx}: Replace magic numbers with named constants for clarity.
Replace complex or nested ternary operators with if/else statements or IIFEs for readability.
Assign complex boolean conditions to named variables.
Use consistent return types for similar functions and hooks.
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle).
Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
Define constants near related logic or ensure names link them clearly.
Files:
frontend/src/hooks/queries/application/useGetApplication.tsfrontend/src/hooks/useTrackPageView.tsfrontend/src/pages/ClubUnionPage/ClubUnionPage.tsxfrontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx
frontend/**/*.tsx
📄 CodeRabbit Inference Engine (frontend/.cursorrules)
frontend/**/*.tsx: Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Separate significantly different conditional UI/logic into distinct components.
Colocate simple, localized logic or use inline definitions to reduce context switching.
Choose field-level or form-level cohesion based on form requirements.
Break down broad state management into smaller, focused hooks or contexts.
Use component composition instead of props drilling.
Files:
frontend/src/pages/ClubUnionPage/ClubUnionPage.tsxfrontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx
🧠 Learnings (1)
📓 Common learnings
Learnt from: seongwon030
PR: Moadong/moadong#195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
🧬 Code Graph Analysis (1)
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (2)
frontend/src/hooks/queries/club/useGetClubDetail.ts (1)
useGetClubDetail(6-20)frontend/src/hooks/queries/application/useGetApplication.ts (1)
useGetApplication(4-11)
🔇 Additional comments (4)
frontend/src/hooks/queries/application/useGetApplication.ts (1)
9-10: enabled 게이트 추가 적절 — 불필요한 API 호출 방지clubId가 없을 때 쿼리 실행을 막아 400/404 요청을 예방합니다. 의도에 부합하고 React Query 동작에도 안전합니다.
frontend/src/hooks/useTrackPageView.ts (1)
42-42: deps에서 clubName 제거 영향 점검 필요Pathname만 의존하도록 변경하면, 클럽명이 뒤늦게 로드되어도 Visited/Duration 이벤트의 clubName이 초기값(예:
club:<id>)으로 고정될 수 있습니다. 위 제안처럼 ref로 최신값을 Duration에 반영하면 의도(Visited 중복 방지)는 유지하면서 라벨 정확도를 개선할 수 있습니다.이 변경 의도가 “클럽명 변경만으로는 PageView 재발행하지 않기”라면 현재 설계가 맞는지 PM/분석 요건과 함께 확인 부탁드립니다.
frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx (1)
9-10: 페이지뷰 인스트루멘테이션 적절페이지 진입 시점에
useTrackPageView('ClubUnionPage')호출 위치가 적절합니다. Hook 내부 리스너 해제/세션 리셋 문제만 해결되면 정확한 방문/체류시간 로깅이 가능해집니다.frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (1)
32-35: Visited/Duration 라벨링 정확도 점검현재 deps가 pathname만이라, 초기에
clubDetail?.name이 없으면 fallback(club:<id>)으로 라벨이 고정됩니다. 위useTrackPageView개선안처럼 Duration에 최신 clubName을 반영할 수 있도록 ref 기반으로 보완하는 것을 권장합니다. 또한 이벤트 프로퍼티에clubId도 함께 전송하면 식별 안정성이 올라갑니다.이 페이지에서 기대하는 분석 스펙(Visited는 1회/경로, Duration은 최신 클럽명 포함)이 맞는지 확인 부탁드립니다.
#️⃣연관된 이슈
📝작업 내용
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit