Conversation
- eventName 제거, 'categoryButton Clicked'로 통일 - category_id/category_name 속성으로 카테고리 정보 전달 - useMixpanelTrack로 공통 속성 자동 포함
- 이벤트명 상수화: CategoryButton Clicked, SearchBox Clicked, Photo Navigation 등 - 여러 컴포넌트에서 하드코딩된 이벤트명을 EVENT_NAME 상수로 치환 - 트래킹 네이밍 일관성 확보 및 오타/변경 비용 감소
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
Warning
|
| Cohort / File(s) | Change Summary |
|---|---|
공통 이벤트 상수 추가frontend/src/constants/eventName.ts |
EVENT_NAME 상수 객체 신규 추가 및 export. 여러 이벤트 키를 표준화된 문자열로 매핑. |
이벤트명 상수화 (검색/포토/공유/탭/뒤로가기/지원/상태/헤더/모바일메뉴)frontend/src/components/common/SearchBox/SearchBox.tsx, frontend/src/hooks/PhotoList/usePhotoNavigation.ts, frontend/src/pages/ClubDetailPage/components/BackNavigationBar/BackNavigationBar.tsx, frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx, frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsx, frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx, frontend/src/pages/MainPage/components/StatusRadioButton/StatusRadioButton.tsx, frontend/src/services/header/useHeaderService.ts, frontend/src/services/header/useMobileMenu.ts |
각 파일의 trackEvent 호출에서 하드코딩된 문자열을 EVENT_NAME의 대응 키로 교체. 로직/제어 흐름 변화 없음. |
카테고리 버튼 로깅 리팩터링frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx |
Mixpanel 직접 호출 제거 → useMixpanelTrack 훅 사용. EVENT_NAME 사용. 이벤트 페이로드에서 timestamp, url 제거. 카테고리 데이터에서 eventName 필드 제거. UI 로직 변경 없음. |
SNS 링크 컴포넌트 인터페이스 및 이벤트 상수화frontend/src/pages/ClubDetailPage/components/SnsLinkIcons/SnsLinkIcons.tsx |
EVENT_NAME 사용으로 이벤트명 상수화. props 인터페이스에 선택적 clubName?: string 추가. 기존 페이로드 형태 유지. |
Sequence Diagram(s)
sequenceDiagram
actor User
participant UI as CategoryButtonList
participant Hook as useMixpanelTrack (trackEvent)
participant Analytics as Analytics/Mixpanel
User->>UI: 카테고리 버튼 클릭
UI->>Hook: trackEvent(EVENT_NAME.CATEGORY_BUTTON_CLICKED, {category_id, category_name})
Hook->>Analytics: track(eventName, payload)
sequenceDiagram
actor User
participant Comp as 다양한 컴포넌트(검색/포토/탭/뒤로가기/공유...)
participant Hook as useMixpanelTrack (trackEvent)
participant Analytics as Analytics/Mixpanel
User->>Comp: UI 상호작용
Comp->>Hook: trackEvent(EVENT_NAME.XYZ, payload)
Hook->>Analytics: track(eventName, payload)
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
Assessment against linked issues
| Objective | Addressed | Explanation |
|---|---|---|
| 공통 이벤트 상수 도입 (MOA-148) | ✅ |
Assessment against linked issues: Out-of-scope changes
| Code Change | Explanation |
|---|---|
| Mixpanel 직접 호출 제거 및 페이로드에서 timestamp/url 제거 (frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx) | 공통 이벤트 상수 도입 범위를 넘어 로깅 방식과 페이로드를 변경함. 이슈 요구사항에 해당 변경이 명시되어 있지 않음. |
| SnsLinkIcons에 선택적 prop clubName 추가 (frontend/src/pages/ClubDetailPage/components/SnsLinkIcons/SnsLinkIcons.tsx) | 이벤트 상수화와 직접적 연관이 불명확함. 기능 표면을 확장하는 변경으로, 이슈 범위에 명시되지 않음. |
Possibly related PRs
- [feature] sns링크버튼에 믹스패널 이벤트를 추가한다 #614: 동일 SnsLinkIcons에서 Mixpanel 트래킹과 clubName prop 변경과 연관.
- [fix] 질문 제목 줄바꿈, 설명 커서 노출, 객관식 선택 해제 등 지원서 폼 UI/UX 개선 #573: ClubApplyButton 수정과 코드 레벨 중복 영역 존재.
- [feature] 믹스패널 track 2차 추가 및 기타 작업 #283: 동일 추적 지점(검색/포토/탭 등)에서 트래킹 호출 변경과 연관.
Suggested reviewers
- Zepelown
- lepitaaar
- oesnuj
✨ Finishing Touches
- 📝 Generate Docstrings
🧪 Generate unit tests
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
refactor/#640-use-common-event-constants-MOA-148
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
🧹 Nitpick comments (8)
frontend/src/constants/eventName.ts (1)
1-17: 이벤트 명명 일관성 점검 및 타입 세이프티 보강 제안값(문자열)들이 언어/형식이 섞여 있어 분석 대시보드에 혼선을 줄 수 있습니다. 특히:
- Line 4: 값이 'Photo Navigation'으로 끝에 'Clicked'가 누락되어 키와 불일치합니다.
- Line 15-16: 'Mobile Menubar delete Button Clicked'는 문법/대소문자 표기가 어색합니다(예: Menubar/delete/case).
- Line 8-9: 한국어/영어 혼용(‘공유하기 버튼 클릭’, ‘sns링크 버튼 클릭’) 및 ‘sns’의 소문자 표기 불일치.
운영 중인 리포트/세그먼트 의존성이 크므로 값 변경 전 데이터팀/PM과 합의가 필요합니다. 합의된다면 아래와 같이 표준화(영문 + Title Case + “… Button Clicked” 일관)하는 것을 권장합니다.
다음 최소 정정 예시(diff):
PHOTO_NAVIGATION_CLICKED: 'Photo Navigation' as const, + PHOTO_NAVIGATION_CLICKED: 'Photo Navigation Clicked' as const, - SHARE_BUTTON_CLICKED: '공유하기 버튼 클릭' as const, + SHARE_BUTTON_CLICKED: 'Share Button Clicked' as const, - SNS_LINK_CLICKED: 'sns링크 버튼 클릭' as const, + SNS_LINK_CLICKED: 'SNS Link Button Clicked' as const, - MOBILE_MENU_DELETE_BUTTON_CLICKED: - 'Mobile Menubar delete Button Clicked' as const, + MOBILE_MENU_DELETE_BUTTON_CLICKED: + 'Mobile Menu Delete Button Clicked' as const,또한 trackEvent 인자에 대한 타입 안전성을 높이기 위해 이벤트명 유니온 타입을 노출해 주세요(외부 추가 코드):
// 추가 제안: constants 파일 하단에 타입을 노출 export type EventName = (typeof EVENT_NAME)[keyof typeof EVENT_NAME];그 후 useMixpanelTrack의 시그니처를 trackEvent(name: EventName, props?: Record<string, unknown>)로 강화하면 오타/미정의 이벤트 방지에 효과적입니다.
frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx (1)
47-48: 이벤트 페이로드에 clubId 추가 제안현재 clubName만 전송합니다. 동일 이름 동아리 혹은 이름 변경 상황까지 대비하려면 clubId도 함께 로깅하는 것이 좋습니다.
- trackEvent(EVENT_NAME.SHARE_BUTTON_CLICKED, { clubName: clubDetail.name }); + trackEvent(EVENT_NAME.SHARE_BUTTON_CLICKED, { + clubId: clubDetail.id, + clubName: clubDetail.name, + });frontend/src/components/common/SearchBox/SearchBox.tsx (1)
26-38: 빈 검색어 로깅 방지 가드 추가 제안공백만 입력된 경우에도 이벤트가 집계될 수 있습니다. 노이즈를 줄이기 위해 trim 체크를 권장합니다.
const handleSearch = () => { + if (!inputValue.trim()) { + inputRef.current?.blur(); + return; + } redirectToHome(); setKeyword(inputValue); setSelectedCategory('all'); setIsSearching(true); inputRef.current?.blur(); trackEvent(EVENT_NAME.SEARCH_BOX_CLICKED, { - inputValue: inputValue, + inputValue: inputValue.trim(), page: window.location.pathname, }); };frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx (1)
49-50: 이벤트 페이로드 보강 제안지원 버튼 클릭은 중요 전환 지표입니다. clubId/clubName 등을 함께 로깅하면 추후 퍼널 분석/이슈 트레이싱이 수월합니다.
- trackEvent(EVENT_NAME.CLUB_APPLY_BUTTON_CLICKED); + trackEvent(EVENT_NAME.CLUB_APPLY_BUTTON_CLICKED, { + clubId, + clubName: clubDetail.name, + });frontend/src/pages/MainPage/components/StatusRadioButton/StatusRadioButton.tsx (1)
19-21: 이벤트 페이로드 키 네이밍 컨벤션 정리 제안현재 'new_status'는 snake_case, 다른 곳은 camelCase(inputValue 등)로 혼재합니다. 분석 스키마 일관성을 위해 키 네이밍 통일이 필요합니다(예: status 혹은 newStatus).
추가로 onChange가 boolean을 받으나 의미적으로 'OPEN' | 'ALL' 두 상태입니다. 도메인 친화적 타입으로 교체하면 가독성과 오용 방지에 유리합니다:
- 타입: type RecruitmentFilter = 'OPEN' | 'ALL'
- onChange: (selected: RecruitmentFilter) => void
- trackEvent 페이로드: { status: selected }
frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx (2)
36-39: 이벤트 페이로드 키 네이밍 스타일(스네이크 vs 카멜) 일관성 확인 요청여기서는
category_id,category_name(snake_case)을 사용하지만, 다른 파일(SnsLinkIcons 등)에서는clubName(camelCase)을 사용하고 있습니다. 이벤트 스키마가 일관되지 않으면 분석/대시보드에서 조인/필터 시 혼선이 생깁니다. 팀 컨벤션(예: 전역 camelCase 또는 snake_case)을 정해 일괄 적용하는 것을 권장합니다. 또한 이 PR에서 timestamp/url을 페이로드에서 제거했는데, 기존 리포트/대시보드 의존성이 없는지 확인 부탁드립니다.선호 컨벤션이 camelCase라면 이 파일은 아래처럼 맞출 수 있습니다:
- trackEvent(EVENT_NAME.CATEGORY_BUTTON_CLICKED, { - category_id: category.id, - category_name: category.name, - }); + trackEvent(EVENT_NAME.CATEGORY_BUTTON_CLICKED, { + categoryId: category.id, + categoryName: category.name, + });
21-27: 카테고리 상수 타입 안정성 강화 제안카테고리 id/name이 매직 스트링으로 흩어질 가능성을 줄이기 위해, 상수 배열을
as const로 고정하고 그로부터 타입을 유도하면 오탈자를 컴파일 타임에 잡을 수 있습니다.예시(선택 적용, 변경 라인 범위 외 참고용):
const clubCategories = [ { id: 'all', name: '전체', icon: iconAll }, { id: '봉사', name: '봉사', icon: iconVolunteer }, { id: '종교', name: '종교', icon: iconReligion }, { id: '취미교양', name: '취미교양', icon: iconHobby }, { id: '학술', name: '학술', icon: iconStudy }, { id: '운동', name: '운동', icon: iconSport }, { id: '공연', name: '공연', icon: iconPerformance }, ] as const; type Category = typeof clubCategories[number];또는
id만 별도 유니온 타입으로 분리해 재사용하는 것도 좋습니다.frontend/src/pages/ClubDetailPage/components/SnsLinkIcons/SnsLinkIcons.tsx (1)
30-34: undefined 속성 전송 방지로 데이터 청결도 개선 제안
clubName이 선택값이라면,undefined를 이벤트 페이로드에 포함하지 않도록 조건부로 스프레드하는 것이 분석 측면에서 안전합니다.- trackEvent(EVENT_NAME.SNS_LINK_CLICKED, { - platform, - clubName, - }) + trackEvent(EVENT_NAME.SNS_LINK_CLICKED, { + platform, + ...(clubName ? { clubName } : {}), + })또한, 본 PR 전반의 이벤트 페이로드 키 네이밍(예: camelCase vs snake_case)이 일관적인지 최종 점검 부탁드립니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
frontend/src/components/common/SearchBox/SearchBox.tsx(2 hunks)frontend/src/constants/eventName.ts(1 hunks)frontend/src/hooks/PhotoList/usePhotoNavigation.ts(2 hunks)frontend/src/pages/ClubDetailPage/components/BackNavigationBar/BackNavigationBar.tsx(1 hunks)frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx(2 hunks)frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsx(3 hunks)frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx(2 hunks)frontend/src/pages/ClubDetailPage/components/SnsLinkIcons/SnsLinkIcons.tsx(2 hunks)frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx(2 hunks)frontend/src/pages/MainPage/components/StatusRadioButton/StatusRadioButton.tsx(2 hunks)frontend/src/services/header/useHeaderService.ts(2 hunks)frontend/src/services/header/useMobileMenu.ts(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/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/ClubDetailPage/components/BackNavigationBar/BackNavigationBar.tsxfrontend/src/services/header/useMobileMenu.tsfrontend/src/components/common/SearchBox/SearchBox.tsxfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsxfrontend/src/services/header/useHeaderService.tsfrontend/src/hooks/PhotoList/usePhotoNavigation.tsfrontend/src/constants/eventName.tsfrontend/src/pages/MainPage/components/StatusRadioButton/StatusRadioButton.tsxfrontend/src/pages/ClubDetailPage/components/SnsLinkIcons/SnsLinkIcons.tsxfrontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.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/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/ClubDetailPage/components/BackNavigationBar/BackNavigationBar.tsxfrontend/src/components/common/SearchBox/SearchBox.tsxfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsxfrontend/src/pages/MainPage/components/StatusRadioButton/StatusRadioButton.tsxfrontend/src/pages/ClubDetailPage/components/SnsLinkIcons/SnsLinkIcons.tsxfrontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx
🧬 Code Graph Analysis (1)
frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx (2)
frontend/src/context/SearchContext.tsx (1)
useSearch(39-45)frontend/src/context/CategoryContext.tsx (1)
useCategory(11-18)
🔇 Additional comments (8)
frontend/src/pages/ClubDetailPage/components/BackNavigationBar/BackNavigationBar.tsx (1)
5-13: 하드코딩 제거 및 공통 상수 적용 적절Mixpanel 이벤트명을 상수로 치환하여 일관성과 유지보수성이 좋아졌습니다. 호출 시점(네비게이션 이전)도 적절합니다.
frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsx (1)
16-20: 공통 상수 적용 적절탭 클릭 이벤트명을 상수로 치환한 변경이 타 파일들과 일관적입니다. 페이로드(tabName, tabIndex)도 충분합니다.
frontend/src/components/common/SearchBox/SearchBox.tsx (1)
34-37: 대시보드 및 리포트 의존성 수동 검증 필요코드 검색 결과, 기존 이벤트명
'Search Executed'는 더 이상 사용되지 않고 있으며, 새 이벤트명'SearchBox Clicked'만 다음 위치에 정의되어 있습니다:
- frontend/src/constants/eventName.ts:
SEARCH_BOX_CLICKED: 'SearchBox Clicked'내부 코드에는 더 이상
'Search Executed'참조가 없으므로 빌드나 런타임 오류는 없을 것으로 보입니다.
그러나 Analytics 파이프라인·대시보드·BI 리포트 등 외부 시스템이 여전히 이전 이벤트명을 기반으로 집계 중일 수 있으니, 아래 항목을 반드시 확인해 주세요:
- 이벤트 트래킹 로그 플랫폼에서
SearchBox Clicked이벤트가 정상 수집되는지- 기존에
Search Executed를 사용하던 대시보드나 리포트에 데이터 공백이 발생하지 않는지- 마케팅/운영팀 문서나 알림 채널에서 이벤트명 변경 사항을 공유했는지
frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx (1)
1-1: EVENT_NAME + 트래킹 훅으로 중앙화: 좋습니다하드코딩된 이벤트명을 제거하고 훅 기반으로 일관화한 방향이 명확하고 유지보수성에 도움이 됩니다.
Also applies to: 12-12, 33-33
frontend/src/pages/ClubDetailPage/components/SnsLinkIcons/SnsLinkIcons.tsx (1)
6-6: EVENT_NAME 사용으로 문자열 분산 제거: 좋습니다하드코딩된 이벤트명을 상수로 대체해 표준화가 잘 이뤄졌습니다.
frontend/src/hooks/PhotoList/usePhotoNavigation.ts (2)
3-3: 이벤트명 상수화 도입: 적절합니다
EVENT_NAME사용으로 이벤트 명칭이 중앙화되어 일관성이 높아졌습니다.
38-41: 트래킹 시점과 페이로드 구성 적절상태 업데이트 직후 인덱스를 포함해 트래킹하는 흐름이 자연스럽고, 이벤트명도 통일돼 있습니다.
Also applies to: 47-50
frontend/src/services/header/useHeaderService.ts (1)
4-4: 이벤트명 중앙화와 매핑 상수화: 잘 반영되었습니다디바이스 구분을 매핑 상수로 두고
keyof를 통해 타입을 좁힌 점이 명확하고 안전합니다. 개별 버튼 이벤트도EVENT_NAME로 일관화되어 있습니다.Also applies to: 7-9, 25-25, 29-29
| const closeMenu = useCallback(() => { | ||
| setIsMenuOpen(false); | ||
| trackEvent('Mobile Menubar delete Button Clicked'); | ||
| trackEvent(EVENT_NAME.MOBILE_MENU_DELETE_BUTTON_CLICKED); | ||
| }, []); |
There was a problem hiding this comment.
ESC로 닫을 때도 ‘삭제 버튼 클릭’ 이벤트가 찍히는 분석 오류
closeMenu가 어디에서 호출되든 동일하게 MOBILE_MENU_DELETE_BUTTON_CLICKED를 로깅합니다. 현재 ESC 키 핸들러에서도 closeMenu를 호출하고 있어, 버튼 클릭이 아님에도 ‘…Delete Button Clicked’가 집계되는 분석 오류가 발생합니다. 또한 useCallback의 의존성 배열에 trackEvent가 누락되었습니다.
권장 수정(diff 1/2: closeMenu에 source 구분 추가 및 의존성 보완):
- const closeMenu = useCallback(() => {
- setIsMenuOpen(false);
- trackEvent(EVENT_NAME.MOBILE_MENU_DELETE_BUTTON_CLICKED);
- }, []);
+ const closeMenu = useCallback((source: 'deleteButton' | 'esc' | 'other' = 'deleteButton') => {
+ setIsMenuOpen(false);
+ if (source === 'deleteButton') {
+ trackEvent(EVENT_NAME.MOBILE_MENU_DELETE_BUTTON_CLICKED);
+ }
+ }, [trackEvent]);권장 수정(diff 2/2: ESC 핸들러에서 source 전달):
- const handleEsc = (e: KeyboardEvent) => {
- if (e.key === 'Escape') closeMenu();
- };
+ const handleEsc = (e: KeyboardEvent) => {
+ if (e.key === 'Escape') closeMenu('esc');
+ };만약 “메뉴 닫힘” 자체를 집계하고 싶다면, EVENT_NAME에 별도의 ‘MOBILE_MENU_CLOSED’를 추가해 시맨틱을 분리하는 것도 고려해 주세요.
Also applies to: 23-29
🤖 Prompt for AI Agents
frontend/src/services/header/useMobileMenu.ts around lines 18-21 (also apply to
23-29): closeMenu currently always logs
EVENT_NAME.MOBILE_MENU_DELETE_BUTTON_CLICKED and omits trackEvent from its
dependency array, which causes ESC closes to be mis-attributed; change closeMenu
to accept a source parameter (e.g., 'button' | 'esc' | 'other') or emit a
semantic MOBILE_MENU_CLOSED event, then call trackEvent with the appropriate
event name and include the source in the payload; update the ESC key handler to
call closeMenu('esc') (or directly call trackEvent with source 'esc'); finally
add trackEvent to the useCallback dependency array so closures capture the
latest function.
#️⃣연관된 이슈
📝작업 내용
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit