[feature] 동아리 지원서 페이지 방문, 체류시간 로깅한다#683
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | Summary |
|---|---|
Analytics Hookfrontend/src/hooks/useTrackPageView.ts |
useEffect 의존성 배열에서 clubName 제거 — 이제 효과는 location.pathname 변경 시에만 재실행됨. 이벤트 리스너, 정리, 체류시간 계산 로직에는 변경 없음. |
Application Form Instrumentationfrontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx |
useTrackPageView를 import 및 호출하여 "ApplicationFormPage"와 clubDetail?.name으로 페이지 뷰/체류시간 계측 추가. 관련 데이터 페칭 호출 위치 정리(중복 제거). |
Query Enable Guardfrontend/src/hooks/queries/application/useGetApplication.ts |
React Query 옵션에 enabled: !!clubId 추가하여 clubId가 falsy일 때 쿼리 자동 실행을 방지함. |
Sequence Diagram(s)
sequenceDiagram
autonumber
actor User
participant App as ApplicationFormPage
participant Hook as useTrackPageView
participant Analytics as AnalyticsService
User->>App: /apply/... 진입
App->>Hook: useTrackPageView("ApplicationFormPage", clubName)
Note right of Hook: mount 시 Visited 이벤트 전송 및 startTime 기록
Hook->>Analytics: track("Visited", { page, clubName(captured), path })
rect rgba(200,230,255,0.18)
Note over Hook: 체류시간 측정(경과시간 누적)
end
User-->>App: 페이지 이탈 또는 경로 변경
Hook->>Analytics: track("Duration", { page, clubName(captured), path, ms })
Hook-->>App: cleanup 완료
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
Assessment against linked issues
| Objective | Addressed | Explanation |
|---|---|---|
| 동아리 지원서 페이지 체류시간 측정 (MOA-172) | ✅ |
Assessment against linked issues: Out-of-scope changes
| Code Change | Explanation |
|---|---|
enabled: !!clubId 추가 (frontend/src/hooks/queries/application/useGetApplication.ts) |
쿼리 실행 제어는 MOA-172의 "체류시간 측정" 목표와 직접적인 관련이 없으며 데이터-fetch 타이밍 최적화 목적임. |
Possibly related issues
- [feature] MOA-172 동아리 지원서 페이지 체류시간을 측정한다 #681: 동일 페이지 뷰·체류시간 계측 목표 — 관련성 높음.
- [feature] MOA-171 총동연 페이지 체류시간을 측정한다 #680:
useTrackPageView훅 변경 관련 이력 — 영향 가능성 있음.
Possibly related PRs
- [fix] useRef에서 startTime.current 프로퍼티 누락 오류 수정 #408: 동일 훅(
useTrackPageView)의 타임스탬프/시작시간 처리 수정 이력과 연관. - [feature] 지원서 제작/수정/불러오기 API 연결하기 #499: application 쿼리/페이지 관련 변경과 겹침.
- [feature] 동아리명은 믹스패널에 arguments로 전달 #398:
clubName처리 및 훅 의존성 관련 이전 변경과 연관.
Suggested labels
🔨 Refactor
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.
📜 Recent 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 (1)
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: run
✨ Finishing Touches
- 📝 Generate Docstrings
🧪 Generate unit tests
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
feature/#681-club-apply-page-duration-MOA-172
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.
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 (2)
frontend/src/hooks/useTrackPageView.ts (2)
10-29: 경로 변경 시 체류시간이 누락될 수 있음: startTime/isTracked를 이펙트 진입 시 초기화 필요pathname이 바뀔 때 이펙트가 재실행되지만, startTime과 isTracked는 초기화되지 않습니다. 한 번 Duration을 전송한 뒤(isTracked=true) 다음 페이지에서는 Duration이 영원히 전송되지 않는 문제가 발생합니다. 또한 다음 페이지 방문의 기준시각도 갱신되지 않아 timestamp/체류시간이 부정확해집니다.
아래처럼 이펙트 시작부에서 두 값을 초기화해 주세요. 동시에 clubName은 최신값을 사용하도록 ref를 읽도록 변경했습니다(아래 다른 코멘트와 연동).
useEffect(() => { + // 새 페이지 진입 시 타이머 및 1회 전송 상태 초기화 + isTracked.current = false; + startTime.current = Date.now(); mixpanel.track(`${pageName} Visited`, { url: window.location.href, timestamp: startTime.current, referrer: document.referrer || 'direct', - clubName, + clubName: latestClubNameRef?.current ?? 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: latestClubNameRef?.current ?? clubName, }); isTracked.current = true; };
30-41: visibilitychange 리스너 제거가 정상 동작하지 않음(다른 핸들러 참조로 메모리 누수/중복 트래킹 가능)등록 시 익명 함수를 사용하고, 제거 시 trackPageDuration을 전달하고 있어 removeEventListener가 효과가 없습니다. 동일한 핸들러 참조를 사용하도록 수정하세요.
- document.addEventListener('visibilitychange', () => { - if (document.hidden) { - trackPageDuration(); - } - }); + const visibilityHandler = () => { + if (document.hidden) { + trackPageDuration(); + } + }; + document.addEventListener('visibilitychange', visibilityHandler); return () => { trackPageDuration(); window.removeEventListener('beforeunload', trackPageDuration); - document.removeEventListener('visibilitychange', trackPageDuration); + document.removeEventListener('visibilitychange', visibilityHandler); };
🧹 Nitpick comments (1)
frontend/src/hooks/useTrackPageView.ts (1)
42-42: clubName을 의존성에서 제거한 영향 검토(의도 확인 요청) + 안전한 대안 제안의존성에서 clubName을 제외하면 페이지 마운트 시점에 clubDetail이 아직 없을 경우 Visited/Duration 이벤트에 clubName이 누락(또는 구버전)될 수 있습니다. 중복 트래킹을 막으면서도 Duration 시점에 최신 clubName을 넣고 싶다면 ref로 최신값만 동기화하는 방식을 권장합니다.
아래 코드를 이 훅 상단에 추가하고(이 라인은 선택된 범위 밖이므로 별도 추가가 필요), 이벤트 전송 시 clubName 대신 latestClubNameRef.current를 사용하세요. 위의 diff는 payload 수정까지 포함합니다.
// 최신 clubName을 유지해 Duration 시점에 반영 const latestClubNameRef = useRef(clubName); useEffect(() => { latestClubNameRef.current = clubName; }, [clubName]);이렇게 하면:
- 이펙트는 pathname에만 반응(중복 트래킹 방지)
- Duration 이벤트에는 최신 clubName 반영
- Visited의 clubName 누락은 여전히 가능(의도라면 OK). 필요 시 clubId 같이 안정적인 식별자도 함께 전송 고려.
📜 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 (2)
frontend/src/hooks/useTrackPageView.ts(1 hunks)frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx(2 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/pages/ApplicationFormPage/ApplicationFormPage.tsxfrontend/src/hooks/useTrackPageView.ts
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/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')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
🪛 Biome (2.1.2)
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx
[error] 44-44: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
🔇 Additional comments (1)
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (1)
6-6: 페이지 체류시간 계측 훅 도입 자체는 👍해당 페이지에 추적을 붙인 방향성은 타당합니다. 아래 라인의 훅 호출 위치만 조정하면 좋겠습니다(다음 코멘트 참조).
- clubId 없으면 요청 안 함
- 조기 리턴(if !clubId) 아래로 이동하고, 데이터/트래킹 훅들을 상단으로 배치해 훅 규칙 위반 방지
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
frontend/src/hooks/queries/application/useGetApplication.ts (1)
4-11: 훅 시그니처를 선택적 파라미터로 완화하고 제네릭 타입 지정 통일 제안현재
useGetApplication(clubId: string)에 호출부에서clubId!단언이 필요합니다. 이미enabled: !!clubId로 런타임 가드가 있으므로, 시그니처를clubId?: string로 완화하면 호출부 단언을 제거할 수 있고,useGetClubDetail과 함께 일관성 있는 사용 패턴을 갖출 수 있습니다. 또한 반환 타입 제네릭을 명시해 타입 안정성을 높일 것을 권장합니다.아래와 같은 변경을 제안합니다.
-import { useQuery } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; +// import type { ApplicationForm } from '@/types/application'; -export const useGetApplication = (clubId: string) => { - return useQuery({ +export const useGetApplication = (clubId?: string) => { + return useQuery/*<ApplicationForm>*/({ queryKey: ['applicationForm', clubId], - queryFn: () => getApplication(clubId), + // enabled가 false일 때 실행되지 않으므로 단언 사용이 안전합니다. + queryFn: () => getApplication(clubId as string), retry: false, enabled: !!clubId, }); };참고:
ApplicationForm타입이 존재한다면 제네릭을 해제하지 말고 실제 타입을 지정해 주세요.frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (1)
24-31: 비필수 단언 제거를 위한 훅 시그니처 정렬 제안
useGetClubDetail(clubId!),useGetApplication(clubId!)처럼 non-null 단언에 의존하고 있습니다. 두 훅 모두 내부에서enabled: !!clubId로 안전장치를 두었으므로, 훅 시그니처를clubId?: string로 변경하면 이 컴포넌트의 단언을 제거할 수 있어 가독성과 안정성이 개선됩니다.변경 예시:
- 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);훅 정의부 변경은 각각 파일에서 다음과 같이 진행해 주세요.
- frontend/src/hooks/queries/club/useGetClubDetail.ts:
useGetClubDetail = (clubId?: string) => useQuery<ClubDetail>({ ..., enabled: !!clubId })- frontend/src/hooks/queries/application/useGetApplication.ts: 위 파일 코멘트 참고
📜 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 (2)
frontend/src/hooks/queries/application/useGetApplication.ts(1 hunks)frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx(2 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/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/ApplicationFormPage/ApplicationFormPage.tsx
🧠 Learnings (2)
📓 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')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-03-19T05:18:07.818Z
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')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Applied to files:
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx
🧬 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 (2)
frontend/src/hooks/queries/application/useGetApplication.ts (1)
9-10: enabled 게이트 추가 적절 — 불필요한 초기 요청 방지
enabled: !!clubId로 파라미터가 없을 때 쿼리 실행을 막아 중복/불필요 요청을 예방합니다. 목적에 잘 부합합니다.frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (1)
6-6: 페이지 뷰 트래킹 훅 도입 적절도입 및 import 위치가 최상단이라 훅 규칙을 준수합니다. 이전 커밋에서 지적되었던 early return 이후 호출 이슈도 해소되었습니다.
- ApplicationFormPage: useTrackPageView에 clubName 폴백 적용
#️⃣연관된 이슈
📝작업 내용
useTrackPageView에서 clubName을 의존성배열에서 제거 -> 중복 트래킹 방지중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit