Conversation
- Platform 타입 정의 ('iOS' | 'Android' | 'Other')
- getAppStoreLink: 사용자 플랫폼 감지 후 적절한 앱 스토어 링크 반환
- detectPlatform: iOS/Android/기타 플랫폼 감지
- APP_STORE_LINKS: 플랫폼별 앱 스토어 링크 상수
- 테스트 코드 작성 (12개 테스트, 100% 커버리지)
- APP_STORE_LINKS 상수 및 getAppStoreLink 함수 제거 - appStoreLink 유틸의 getAppStoreLink, detectPlatform 사용 - 플랫폼 감지 로직 간소화 및 코드 재사용성 향상
- 모바일에서만 표시되는 앱 출시 안내 팝업 구현 - useDevice 훅을 활용한 반응형 모바일 감지 - localStorage 기반 '다시 보지 않기' 기능 - 플랫폼별 앱 스토어 자동 연결 (iOS/Android) - Mixpanel 이벤트 트래킹 (팝업 표시, 닫기, 다운로드 클릭) - 이미지 클릭 시 앱 다운로드 페이지 이동 - 하단 버튼 그룹: 다시 보지 않기 / 닫기 - 배경 클릭으로 팝업 닫기 지원
- 사용자를 50/50 비율로 show_popup/no_popup 그룹에 랜덤 할당 - localStorage로 그룹 정보 영구 저장 (동일 사용자는 항상 같은 그룹) - show_popup 그룹만 팝업 표시, no_popup 그룹은 컨트롤 그룹 - 모든 Mixpanel 이벤트에 abTestGroup 속성 추가 - MAIN_POPUP_NOT_SHOWN 이벤트 추가 (컨트롤 그룹 트래킹) - PopupABTestGroup 타입 정의로 다른 A/B 테스트와 구분
- localStorage에 타임스탬프 저장하여 7일 후 자동 재표시 - isPopupHidden 함수로 날짜 기반 팝업 숨김 로직 구현 - POPUP_STORAGE_KEY를 'mainpage_popup_hidden_date'로 변경 - 테스트를 위해 상수 및 유틸 함수 export - 17개 테스트 케이스 작성 (모두 통과) - A/B 테스트 그룹 할당 테스트 - 7일 기반 팝업 숨김 로직 테스트 - 통합 시나리오 테스트 - 핵심 로직 90.9% 커버리지 달성
- handleClose에 action 파라미터 추가 - 닫기 방법(close_button, backdrop_click)을 파라미터로 전달 - handleBackdropClick에서 이벤트 중복 트래킹 제거 - 각 닫기 방법이 정확히 한 번씩만 트래킹되도록 개선
- 이미지 로딩 상태(imageLoaded) 추가 - Image 객체로 팝업 이미지 사전 로드 - 이미지 로드 완료 후에만 팝업 표시 - 버튼과 이미지가 동시에 렌더링되어 UX 개선
[feature] 메인화면에 앱 출시 팝업을 추가한다
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| 집단 / 파일(들) | 변경 요약 |
|---|---|
이벤트 추적 상수 frontend/src/constants/eventName.ts |
MAIN_POPUP_VIEWED, MAIN_POPUP_NOT_SHOWN, MAIN_POPUP_CLOSED, APP_DOWNLOAD_POPUP_CLICKED 네 개의 새로운 USER_EVENT 키 추가 |
팝업 컴포넌트 및 스타일 frontend/src/pages/MainPage/components/Popup/Popup.tsx, Popup.styles.ts, Popup.test.tsx |
Overlay, ModalContainer, Container, ImageWrapper, PopupImage, ButtonGroup, Button 등의 styled-components 추가; AB 테스트 로직(50/50 분할), localStorage 기반 7일 숨김 기능, 플랫폼별 앱 스토어 링크 열기, Mixpanel 이벤트 추적 구현; 팝업 열고 닫을 때 페이지 스크롤 잠금 관리; 유틸리티 함수(getABTestGroup, isPopupHidden) 및 상수(POPUP_STORAGE_KEY, AB_TEST_KEY, DAYS_TO_HIDE) 내보내기 |
앱 스토어 유틸리티 frontend/src/utils/appStoreLink.ts, appStoreLink.test.ts |
Platform 타입, APP_STORE_LINKS 상수, detectPlatform(), getAppStoreLink() 함수 추가로 userAgent 기반 iOS/Android/Other 플랫폼 감지 및 해당 스토어 링크 반환 |
메인 페이지 통합 frontend/src/pages/MainPage/MainPage.tsx, Banner.tsx |
MainPage에서 Popup 컴포넌트 임포트 및 렌더링; Banner에서 하드코딩된 플랫폼 감지 로직을 detectPlatform(), getAppStoreLink() 유틸리티로 대체 |
시퀀스 다이어그램
sequenceDiagram
participant User
participant MainPage
participant Popup
participant Storage as localStorage
participant Analytics as Analytics/Mixpanel
participant AppStore as App Store Link
User->>MainPage: 메인 페이지 방문
MainPage->>Popup: Popup 컴포넌트 마운트
rect rgb(200, 220, 240)
note over Popup: AB 테스트 그룹 결정
Popup->>Storage: AB 테스트 그룹 확인
alt 저장된 그룹 없음
Popup->>Storage: 새 그룹 생성 및 저장<br/>(show_popup 또는 no_popup, 50/50)
end
end
rect rgb(220, 240, 200)
note over Popup: 팝업 표시 여부 결정
Popup->>Storage: 마지막 숨김 날짜 확인
alt 7일 이내
Popup->>Analytics: MAIN_POPUP_NOT_SHOWN 이벤트 전송
Popup-->>User: 팝업 표시 안 함
else 7일 이상 또는 저장된 날짜 없음
Popup->>Analytics: MAIN_POPUP_VIEWED 이벤트 전송
Popup-->>User: 팝업 표시
end
end
rect rgb(240, 220, 200)
note over User,Analytics: 사용자 상호작용
alt 닫기 또는 배경 클릭
User->>Popup: 팝업 닫기
Popup->>Storage: 숨김 날짜 저장
Popup->>Analytics: MAIN_POPUP_CLOSED 이벤트 전송
else 다운로드 버튼 클릭
User->>Popup: 다운로드 클릭
Popup->>Popup: 플랫폼 감지
Popup->>AppStore: 플랫폼별 스토어 링크 요청
AppStore-->>Popup: 스토어 URL 반환
Popup->>Analytics: APP_DOWNLOAD_POPUP_CLICKED 이벤트<br/>(플랫폼, AB 그룹 포함)
Popup->>User: 앱 스토어 열기
end
end
Popup->>Popup: 페이지 스크롤 복원
예상 코드 리뷰 시간
🎯 4 (복잡함) | ⏱️ ~45분
관련 가능성 있는 이슈
- [feature] MOA-486 메인화면에 앱 출시 팝업을 추가한다 #1009: AB 테스트 로직이 포함된 앱 다운로드 팝업 기능이 이 이슈의 요구사항을 직접 구현합니다.
관련 가능성 있는 PR
- [feature] 메인화면에 앱 출시 팝업을 추가한다 #1010: 동일한 파일들(eventName.ts, MainPage, Popup 컴포넌트/스타일/테스트, appStoreLink 유틸리티)을 수정하며 동일한 앱 다운로드 팝업 기능을 구현합니다.
- [feature] 관리자페이지 믹스패널 로깅을 적용한다 #874: eventName.ts의 USER_EVENT export를 수정하는 점에서 연관됩니다.
- [release] FE v1.1.8 #939: 앱 스토어 링크 유틸리티와 배너 분석 이벤트 상수 추가에서 연관됩니다.
제안 라벨
AB TEST
제안 리뷰어
- lepitaaar
- suhyun113
- oesnuj
Pre-merge checks and finishing touches
✅ Passed checks (3 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | PR 제목은 '앱 출시 팝업 릴리즈'로 주요 변경사항(앱 다운로드 팝업 컴포넌트 추가)을 명확히 나타내고 있습니다. |
| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
✨ Finishing touches
- 📝 Generate docstrings
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.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In @frontend/src/pages/MainPage/components/Popup/Popup.styles.ts:
- Around line 34-35: Container uses border-radius: 10px while ButtonGroup
applies 16px on bottom corners and Container has overflow: hidden, so the
ButtonGroup's 16px corners will be clipped; make the radii consistent by either
changing ButtonGroup's bottom corner radius to 10px to match Container (symbols:
Container and ButtonGroup) or removing/moving overflow: hidden to an inner
wrapper so ButtonGroup can keep 16px without being clipped; apply the same
change to the other occurrence noted (lines referencing the second
Container/ButtonGroup pair).
In @frontend/src/pages/MainPage/components/Popup/Popup.tsx:
- Around line 34-36: The expression 1000 * 60 * 60 * 24 in the daysSinceHidden
calculation is a magic number; define a named constant (e.g., MS_PER_DAY or
MILLISECONDS_PER_DAY) near the top of the module and replace the inline
multiplication with that constant so the calculation in the daysSinceHidden
expression ((Date.now() - parseInt(hiddenDate)) / MS_PER_DAY) is clear and
self-documenting; keep DAYS_TO_HIDE as-is and ensure the new constant is
exported or scoped appropriately for testing if needed.
In @frontend/src/utils/appStoreLink.test.ts:
- Around line 139-142: The iOS App Store ID in APP_STORE_LINKS is incorrect (the
stored link in frontend/src/utils/appStoreLink.ts uses '675506285' but the test
expects '6755062085'); update the iOS URL value (APP_STORE_LINKS.ios) to use the
correct App Store ID '6755062085' so the link contains both 'itms-apps://' and
the expected 10-digit ID.
🧹 Nitpick comments (5)
frontend/src/utils/appStoreLink.ts (1)
11-33:getAppStoreLink와detectPlatform간 중복 로직이 있습니다.두 함수 모두 동일한 userAgent 파싱 로직과 정규식 패턴(
/iphone|ipad|ipod|macintosh/,/android/)을 사용하고 있습니다. 패턴 수정이 필요할 때 한 곳만 수정하고 다른 곳을 놓칠 위험이 있습니다.🔎 중복 제거를 위한 리팩토링 제안
+const IOS_PATTERN = /iphone|ipad|ipod|macintosh/; +const ANDROID_PATTERN = /android/; + export const getAppStoreLink = (): string => { - const userAgent = navigator.userAgent.toLowerCase(); - - if (/iphone|ipad|ipod|macintosh/.test(userAgent)) { - return APP_STORE_LINKS.ios; - } - if (/android/.test(userAgent)) { - return APP_STORE_LINKS.android; - } - return APP_STORE_LINKS.default; + const platform = detectPlatform(); + if (platform === 'iOS') return APP_STORE_LINKS.ios; + if (platform === 'Android') return APP_STORE_LINKS.android; + return APP_STORE_LINKS.default; }; export const detectPlatform = (): Platform => { const userAgent = navigator.userAgent.toLowerCase(); - if (/iphone|ipad|ipod|macintosh/.test(userAgent)) { + if (IOS_PATTERN.test(userAgent)) { return 'iOS'; } - if (/android/.test(userAgent)) { + if (ANDROID_PATTERN.test(userAgent)) { return 'Android'; } return 'Other'; };frontend/src/pages/MainPage/components/Popup/Popup.tsx (1)
123-128: 접근성 개선:role="dialog"속성 추가를 권장합니다.
aria-modal='true'가 설정되어 있지만, 스크린 리더 호환성을 위해role="dialog"와aria-labelledby또는aria-label을 함께 추가하면 좋습니다.🔎 접근성 속성 추가 제안
<Styled.Overlay isOpen={isOpen} onClick={handleBackdropClick} + role="dialog" aria-modal='true' + aria-label='모아동 앱 다운로드 팝업' >frontend/src/pages/MainPage/components/Popup/Popup.styles.ts (3)
2-2: 사용되지 않는Theme타입 import를 제거해 주세요.
Theme타입이 import되었지만 파일 내에서 사용되지 않습니다.-import { theme, Theme } from '@/styles/theme'; +import { theme } from '@/styles/theme';
16-26:ModalContainer의isOpenprop이 사용되지 않습니다.
isOpenprop이 타입에 선언되어 있지만 스타일에서 활용되지 않습니다. 의도된 트랜지션 효과가 있다면 구현하고, 그렇지 않다면 prop을 제거해 주세요.🔎 prop 제거 또는 트랜지션 구현 제안
옵션 1: prop 제거
-export const ModalContainer = styled.div<{ isOpen: boolean }>` +export const ModalContainer = styled.div`옵션 2: 트랜지션 효과 구현
export const ModalContainer = styled.div<{ isOpen: boolean }>` position: relative; z-index: ${Z_INDEX.modal}; max-width: 500px; width: 100%; max-height: 90vh; background: transparent; border-radius: 16px; overflow: visible; - transition: transform 0.2s ease; + transform: ${({ isOpen }) => (isOpen ? 'scale(1)' : 'scale(0.95)')}; + opacity: ${({ isOpen }) => (isOpen ? 1 : 0)}; + transition: transform 0.2s ease, opacity 0.2s ease; `;
33-34: 하드코딩된 색상을 테마 변수로 교체해 주세요.
#ffffff대신theme.colors.base.white를 사용하면 테마 일관성을 유지할 수 있습니다.- background-color: #ffffff; + background-color: ${theme.colors.base.white};
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
frontend/src/assets/images/popup/app-download.svgis excluded by!**/*.svg
📒 Files selected for processing (8)
frontend/src/constants/eventName.tsfrontend/src/pages/MainPage/MainPage.tsxfrontend/src/pages/MainPage/components/Banner/Banner.tsxfrontend/src/pages/MainPage/components/Popup/Popup.styles.tsfrontend/src/pages/MainPage/components/Popup/Popup.test.tsxfrontend/src/pages/MainPage/components/Popup/Popup.tsxfrontend/src/utils/appStoreLink.test.tsfrontend/src/utils/appStoreLink.ts
🧰 Additional context used
📓 Path-based instructions (3)
frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries withif/elseor IIFEs for readability
Assign complex boolean conditions to named variables for explicit meaning
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Use unique and descriptive names for custom wrappers/functions to avoid ambiguity
Define constants near related logic or ensure names link them clearly to avoid silent failures
Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Files:
frontend/src/utils/appStoreLink.test.tsfrontend/src/constants/eventName.tsfrontend/src/pages/MainPage/components/Popup/Popup.tsxfrontend/src/pages/MainPage/MainPage.tsxfrontend/src/pages/MainPage/components/Popup/Popup.test.tsxfrontend/src/utils/appStoreLink.tsfrontend/src/pages/MainPage/components/Banner/Banner.tsxfrontend/src/pages/MainPage/components/Popup/Popup.styles.ts
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/utils/appStoreLink.test.tsfrontend/src/constants/eventName.tsfrontend/src/pages/MainPage/components/Popup/Popup.tsxfrontend/src/pages/MainPage/MainPage.tsxfrontend/src/pages/MainPage/components/Popup/Popup.test.tsxfrontend/src/utils/appStoreLink.tsfrontend/src/pages/MainPage/components/Banner/Banner.tsxfrontend/src/pages/MainPage/components/Popup/Popup.styles.ts
frontend/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{tsx,jsx}: Abstract complex logic/interactions into dedicated 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 when using form libraries like react-hook-form
Use Component Composition instead of Props Drilling to reduce coupling
Files:
frontend/src/pages/MainPage/components/Popup/Popup.tsxfrontend/src/pages/MainPage/MainPage.tsxfrontend/src/pages/MainPage/components/Popup/Popup.test.tsxfrontend/src/pages/MainPage/components/Banner/Banner.tsx
🧠 Learnings (4)
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 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/MainPage/MainPage.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Define constants near related logic or ensure names link them clearly to avoid silent failures
Applied to files:
frontend/src/utils/appStoreLink.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Replace magic numbers with named constants for clarity
Applied to files:
frontend/src/utils/appStoreLink.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Abstract complex logic/interactions into dedicated components/HOCs
Applied to files:
frontend/src/pages/MainPage/components/Popup/Popup.styles.ts
🧬 Code graph analysis (4)
frontend/src/utils/appStoreLink.test.ts (1)
frontend/src/utils/appStoreLink.ts (3)
detectPlatform(23-33)getAppStoreLink(11-21)APP_STORE_LINKS(1-7)
frontend/src/pages/MainPage/components/Popup/Popup.tsx (2)
frontend/src/constants/eventName.ts (1)
USER_EVENT(1-44)frontend/src/utils/appStoreLink.ts (2)
getAppStoreLink(11-21)detectPlatform(23-33)
frontend/src/pages/MainPage/components/Banner/Banner.tsx (1)
frontend/src/utils/appStoreLink.ts (1)
detectPlatform(23-33)
frontend/src/pages/MainPage/components/Popup/Popup.styles.ts (2)
frontend/src/styles/zIndex.ts (1)
Z_INDEX(1-6)frontend/src/styles/theme/index.ts (1)
theme(4-7)
🔇 Additional comments (8)
frontend/src/constants/eventName.ts (1)
5-9: LGTM! 이벤트 상수가 잘 정의되었습니다.팝업 관련 이벤트 상수들이 기존 네이밍 컨벤션을 잘 따르고 있으며, 주석으로 명확하게 구분되어 있습니다. A/B 테스트와 팝업 분석에 필요한 모든 이벤트를 포함하고 있습니다.
frontend/src/pages/MainPage/MainPage.tsx (1)
11-11: LGTM! Popup 컴포넌트 통합이 깔끔합니다.Popup 컴포넌트를 최상단에 배치하여 오버레이/모달 동작을 보장하고 있습니다. Props drilling 없이 컴포넌트 조합 패턴을 잘 따르고 있습니다.
Also applies to: 55-55
frontend/src/utils/appStoreLink.test.ts (1)
1-153: 테스트 커버리지가 훌륭합니다.플랫폼 감지, 앱 스토어 링크 반환, 상수 검증, 통합 시나리오까지 포괄적으로 테스트하고 있습니다. Navigator mocking 패턴도 올바르게 구현되었습니다.
frontend/src/pages/MainPage/components/Banner/Banner.tsx (1)
11-11: LGTM! 앱 스토어 링크 로직을 잘 리팩토링했습니다.플랫폼 감지 및 링크 결정 로직을 중앙화된 유틸리티로 분리하여 단일 책임 원칙(SRP)과 DRY 원칙을 잘 따르고 있습니다. 테스트 가능성도 향상되었습니다.
Also applies to: 37-46
frontend/src/pages/MainPage/components/Popup/Popup.test.tsx (1)
1-156: LGTM! Popup 유틸리티 테스트가 매우 포괄적입니다.A/B 테스트 그룹 할당, 팝업 숨김 상태 관리, 시간 경계 조건, 통합 시나리오까지 모든 주요 기능을 테스트하고 있습니다. 특히:
- 50/50 분포 테스트는 통계적으로 적절한 허용 범위(40-60%)를 사용합니다
- 7일 경계 조건을 정확히 검증합니다
- localStorage 모킹 패턴이 올바릅니다
frontend/src/utils/appStoreLink.ts (1)
14-16:macintosh패턴 포함 여부를 확인해 주세요.macOS 데스크톱 Safari 사용자도 iOS로 감지되어
itms-apps://스킴 링크로 연결됩니다. 팝업이 모바일 전용이라면 문제없지만, 이 유틸리티가 다른 곳(예: Banner)에서도 사용되면 데스크톱 Mac 사용자에게 의도치 않은 동작이 발생할 수 있습니다.frontend/src/pages/MainPage/components/Popup/Popup.tsx (2)
45-50: 이미지 로드 실패 시 팝업이 깨진 이미지와 함께 표시될 수 있습니다.
onerror핸들러가imageLoaded를true로 설정하여 이미지 로드 실패 시에도 팝업이 표시됩니다. 의도된 동작이라면 괜찮지만, 이미지 로드 실패 시 팝업을 숨기려면 별도의 상태 관리가 필요합니다.
52-70: 트래킹 이벤트 중복 발생 우려는 실제로 발생하지 않습니다.
useMixpanelTrack훅에서trackEvent는useCallback으로 빈 의존성 배열[]과 함께 메모이제이션되어 있어, 함수 참조가 렌더링 간에 안정적으로 유지됩니다. 따라서 effect가trackEvent변경으로 인해 재실행되지 않으며, 중복 추적 이벤트 발생 위험은 없습니다.Likely an incorrect or invalid review comment.
| border-radius: 10px; | ||
| `; |
There was a problem hiding this comment.
Container와 ButtonGroup의 border-radius 불일치를 확인해 주세요.
Container는 border-radius: 10px를 사용하고, ButtonGroup은 하단 모서리에 16px를 사용합니다. Container에 overflow: hidden이 적용되어 있어 ButtonGroup의 16px radius가 10px로 잘릴 수 있습니다.
🔎 border-radius 통일 제안
export const Container = styled.div`
display: flex;
flex-direction: column;
width: 100%;
overflow: hidden;
background-color: #ffffff;
- border-radius: 10px;
+ border-radius: 16px;
`;Also applies to: 60-61
🤖 Prompt for AI Agents
In @frontend/src/pages/MainPage/components/Popup/Popup.styles.ts around lines 34
- 35, Container uses border-radius: 10px while ButtonGroup applies 16px on
bottom corners and Container has overflow: hidden, so the ButtonGroup's 16px
corners will be clipped; make the radii consistent by either changing
ButtonGroup's bottom corner radius to 10px to match Container (symbols:
Container and ButtonGroup) or removing/moving overflow: hidden to an inner
wrapper so ButtonGroup can keep 16px without being clipped; apply the same
change to the other occurrence noted (lines referencing the second
Container/ButtonGroup pair).
| const daysSinceHidden = | ||
| (Date.now() - parseInt(hiddenDate)) / (1000 * 60 * 60 * 24); | ||
| return daysSinceHidden < DAYS_TO_HIDE; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
매직 넘버를 명명된 상수로 추출해 주세요.
1000 * 60 * 60 * 24의 의미가 명확하지 않습니다. 코딩 가이드라인에 따라 명명된 상수로 정의하는 것이 좋습니다.
🔎 상수 추출 제안
export const POPUP_STORAGE_KEY = 'mainpage_popup_hidden_date';
export const AB_TEST_KEY = 'mainpage_popup_ab_group';
export const DAYS_TO_HIDE = 7;
const AB_TEST_SPLIT_RATIO = 0.5;
+const MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24;
// ...
export const isPopupHidden = (): boolean => {
const hiddenDate = localStorage.getItem(POPUP_STORAGE_KEY);
if (!hiddenDate) return false;
const daysSinceHidden =
- (Date.now() - parseInt(hiddenDate)) / (1000 * 60 * 60 * 24);
+ (Date.now() - parseInt(hiddenDate)) / MILLISECONDS_PER_DAY;
return daysSinceHidden < DAYS_TO_HIDE;
};Based on learnings, 매직 넘버를 명명된 상수로 대체하여 명확성을 높여야 합니다.
📝 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.
| const daysSinceHidden = | |
| (Date.now() - parseInt(hiddenDate)) / (1000 * 60 * 60 * 24); | |
| return daysSinceHidden < DAYS_TO_HIDE; | |
| const daysSinceHidden = | |
| (Date.now() - parseInt(hiddenDate)) / MILLISECONDS_PER_DAY; | |
| return daysSinceHidden < DAYS_TO_HIDE; |
🤖 Prompt for AI Agents
In @frontend/src/pages/MainPage/components/Popup/Popup.tsx around lines 34 - 36,
The expression 1000 * 60 * 60 * 24 in the daysSinceHidden calculation is a magic
number; define a named constant (e.g., MS_PER_DAY or MILLISECONDS_PER_DAY) near
the top of the module and replace the inline multiplication with that constant
so the calculation in the daysSinceHidden expression ((Date.now() -
parseInt(hiddenDate)) / MS_PER_DAY) is clear and self-documenting; keep
DAYS_TO_HIDE as-is and ensure the new constant is exported or scoped
appropriately for testing if needed.
| it('iOS 앱 스토어 링크가 올바른 형식이다', () => { | ||
| expect(APP_STORE_LINKS.ios).toContain('itms-apps://'); | ||
| expect(APP_STORE_LINKS.ios).toContain('6755062085'); | ||
| }); |
There was a problem hiding this comment.
앱 스토어 ID에 오타가 있습니다.
Line 141에서 기대하는 앱 ID가 '6755062085' (10자리)인데, 실제 frontend/src/utils/appStoreLink.ts의 iOS 링크는 'itms-apps://itunes.apple.com/app/675506285' (9자리)입니다. '0'이 하나 더 들어가 있어 테스트가 실패할 것입니다.
🔎 수정 제안
it('iOS 앱 스토어 링크가 올바른 형식이다', () => {
expect(APP_STORE_LINKS.ios).toContain('itms-apps://');
- expect(APP_STORE_LINKS.ios).toContain('6755062085');
+ expect(APP_STORE_LINKS.ios).toContain('675506285');
});🤖 Prompt for AI Agents
In @frontend/src/utils/appStoreLink.test.ts around lines 139 - 142, The iOS App
Store ID in APP_STORE_LINKS is incorrect (the stored link in
frontend/src/utils/appStoreLink.ts uses '675506285' but the test expects
'6755062085'); update the iOS URL value (APP_STORE_LINKS.ios) to use the correct
App Store ID '6755062085' so the link contains both 'itms-apps://' and the
expected 10-digit ID.
#️⃣연관된 이슈
📝작업 내용
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
릴리스 노트
New Features
Tests
✏️ Tip: You can customize this high-level summary in your review settings.