Skip to content

[analytics] 총동연 페이지, 지원서 페이지 방문 및 체류시간 로깅#684

Merged
seongwon030 merged 8 commits intomainfrom
develop-fe
Aug 20, 2025
Merged

[analytics] 총동연 페이지, 지원서 페이지 방문 및 체류시간 로깅#684
seongwon030 merged 8 commits intomainfrom
develop-fe

Conversation

@seongwon030
Copy link
Member

@seongwon030 seongwon030 commented Aug 19, 2025

#️⃣연관된 이슈

ex) #이슈번호, #이슈번호

📝작업 내용

이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지/동영상 첨부 가능)

중점적으로 리뷰받고 싶은 부분(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

논의하고 싶은 부분(선택)

논의하고 싶은 부분이 있다면 작성해주세요.

🫡 참고사항

Summary by CodeRabbit

  • New Features
    • 페이지 방문 추적 추가: 신청서 페이지와 동아리연합회 페이지에서 페이지 뷰 트래킹을 도입했습니다.
  • Bug Fixes
    • 클럽 ID가 없을 때 신청서 데이터 요청을 막아 불필요한 에러와 화면 깜빡임을 방지했습니다.
    • 신청서 페이지의 로딩/에러 처리 흐름을 정리해 일관성과 안정성을 높였습니다.
  • Performance
    • 페이지 뷰 트래킹의 불필요한 재실행을 줄여 오버헤드를 감소시켰습니다.
    • 데이터 선요청으로 초기 렌더링 과정을 간소화했습니다.

seongwon030 and others added 8 commits August 19, 2025 16:20
…ation-MOA-171

[feature] 총동연 페이지 방문 및 체류시간을 로깅한다
- clubId 없으면 요청 안 함
- 조기 리턴(if !clubId) 아래로 이동하고, 데이터/트래킹 훅들을 상단으로 배치해 훅 규칙 위반 방지
- ApplicationFormPage: useTrackPageView에 clubName 폴백 적용
…ation-MOA-172

[feature] 동아리 지원서 페이지 방문, 체류시간 로깅한다
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 19, 2025

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid regex pattern for base branch. Received: "**" at "reviews.auto_review.base_branches[0]"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

애플리케이션/클럽 페이지에 페이지뷰 트래킹 훅을 도입하고, ApplicationFormPage에서 데이터 패칭을 상단으로 통합했습니다. useGetApplication 쿼리에 clubId 기반 enabled 게이트를 추가했으며, useTrackPageView 훅의 의존성 배열에서 clubName을 제거했습니다.

Changes

Cohort / File(s) Summary
쿼리 조건 게이팅
frontend/src/hooks/queries/application/useGetApplication.ts
useQueryenabled: !!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
Loading
sequenceDiagram
  autonumber
  actor User
  participant CUP as ClubUnionPage
  participant Track as useTrackPageView

  User->>CUP: 방문 (/club-union)
  CUP->>Track: trackPageView('ClubUnionPage')
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

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.

❤️ Share
🪧 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 @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in 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 ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere 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.

@vercel
Copy link

vercel bot commented Aug 19, 2025

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

Project Deployment Preview Comments Updated (UTC)
moadong Ready Ready Preview Comment Aug 19, 2025 3:30pm

@seongwon030 seongwon030 added the 💻 FE Frontend label Aug 19, 2025
Copy link
Contributor

@lepitaaar lepitaaar left a comment

Choose a reason for hiding this comment

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

로깅추가 수고하셨습니다

Copy link
Contributor

@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: 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);

별도 참고: useGetClubDetailenabled: !!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.

📥 Commits

Reviewing files that changed from the base of the PR and between ecba609 and d8b3de5.

📒 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.ts
  • frontend/src/hooks/useTrackPageView.ts
  • frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx
  • frontend/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.tsx
  • 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')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
🧬 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은 최신 클럽명 포함)이 맞는지 확인 부탁드립니다.

@seongwon030 seongwon030 merged commit e75a911 into main Aug 20, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FE Frontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments