Skip to content

Conversation

@seongwon030
Copy link
Member

@seongwon030 seongwon030 commented Dec 17, 2025

#️⃣연관된 이슈

ex) #937

📝작업 내용

앱 릴리즈 배너 클릭 시 사용자의 OS에 따라 적절한 스토어로 이동하도록 구현했습니다.

1. 앱 릴리즈 배너 추가

  • 앱 다운로드 안내 배너 이미지 추가 (desktop/mobile)
  • bannerData.ts에 배너 데이터 등록
  • 패치노트 배너는 제거

2. OS별 스토어 분기 처리

  • user-agent 기반으로 iOS/Android 판별하는 유틸함수 추가
  • 외부에서 사용되지 않을 것 같아 Banner.tsx내부에 두었습니다.
  • iOS: App Store (itms-apps://)
  • Android: Google Play Store (https://play.google.com/...)

3. useNavigator 보안 강화

  • 외부 URL 판별 시 허용 프로토콜 화이트리스트 적용
  • javascript://, data:// 등 위험한 프로토콜 차단
  • 허용: http://, https://, itms-apps://

4. 테스트 추가

  • 빈 URL 처리
  • 악성 링크 차단
  • 외부 URL 이동
  • 내부 페이지 이동

5. 로깅 추가

  • 배너 클릭 시 id, name, alt 로깅
  • alt에 배너 이름 명시

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

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

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

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

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

🫡 참고사항

Summary by CodeRabbit

  • 새로운 기능

    • 플랫폼별 앱 스토어 링크 지원 추가 (iOS/macOS 및 Android)
    • 배너 클릭 시 앱 다운로드 배너 이벤트 추가
  • 개선사항

    • 외부 URL 감지 범위 확대 — http/https 및 앱 스토어 스킴 지원
    • 위험 프로토콜(js/data/vbscript) 차단으로 보안 강화
    • 외부 링크 이동 방식 조정 — 현재 창에서 이동하도록 변경
  • 업데이트

    • 배너 콘텐츠를 앱 릴리스용으로 교체(이미지·링크 갱신)
  • 테스트

    • 링크 처리 동작을 검증하는 통합 테스트 추가

✏️ Tip: You can customize this high-level summary in your review settings.

- 패치노트 배너 제거
- APP_STORE_LINKS 상수 추가 (iOS, Android, default)
- getAppStoreLink 함수로 user-agent 기반 OS 판별
- iOS: itms-apps 스키마로 App Store 연결
- Android/기타: Google Play Store 연결
@seongwon030 seongwon030 self-assigned this Dec 17, 2025
@seongwon030 seongwon030 added ✨ Feature 기능 개발 💻 FE Frontend labels Dec 17, 2025
@vercel
Copy link

vercel bot commented Dec 17, 2025

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

Project Deployment Review Updated (UTC)
moadong Ready Ready Preview, Comment Dec 17, 2025 9:35am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 17, 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.
  • You can also validate your configuration using the online YAML validator.
  • 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

위험한 스킴(javascript:, data:, vbscript:)을 차단하고 외부 URL 판별을 확장(http(s), itms-apps)해 외부 링크는 window.location.href로 이동하도록 useNavigator를 재작성했습니다. Banner 클릭은 APP_STORE_LINK을 플랫폼별 스토어 URL로 해석해 트래킹 후 이동하며, 패치노트 배너는 앱 출시 배너로 교체되었습니다.

Changes

Cohort / File(s) Summary
외부 URL 탐지 및 네비게이션 훅
frontend/src/hooks/useNavigator.ts
위험한 프로토콜(javascript:, data:, vbscript:) 차단; 외부 링크 판별을 http/https/itms-apps 등으로 확장; 외부 링크는 내부 라우터 대신 window.location.href로 이동하도록 변경.
훅 단위테스트 추가
frontend/src/hooks/__tests__/useNavigator.test.ts
빈/공백 링크, 위험 스킴(대소문자 포함), 외부 링크(itms-apps 포함), 내부 절대/상대 경로 시나리오를 검증하는 테스트 추가. useNavigatewindow.location 모킹/복원 포함.
배너 클릭 처리 및 앱스토어 토큰 해석
frontend/src/pages/MainPage/components/Banner/Banner.tsx
APP_STORE_LINKS 맵과 getAppStoreLink() 추가. handleBannerClickAPP_STORE_LINK일 때 플랫폼별 스토어 URL을 해석해 트래킹(APP_DOWNLOAD_BANNER_CLICKED) 후 이동; 일반 링크는 BANNER_CLICKED 트래킹 후 이동.
배너 데이터 업데이트(콘텐츠 전환)
frontend/src/pages/MainPage/components/Banner/bannerData.ts
PatchNote 배너 항목 제거, 앱 출시 배너(app-release-december-2025)로 교체(데스크톱/모바일 이미지 및 linkTo: APP_STORE_LINK).
이벤트 상수 추가
frontend/src/constants/eventName.ts
USER_EVENTBANNER_CLICKEDAPP_DOWNLOAD_BANNER_CLICKED 키 추가.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Banner
    participant useNavigator
    participant Router as React-Router
    participant Window as window.location

    User->>Banner: 배너 클릭 (bannerId, bannerName, linkTo)
    Banner->>useNavigator: handleBannerClick(linkTo, bannerId, bannerName)
    alt linkTo == APP_STORE_LINK
        useNavigator->>useNavigator: getAppStoreLink(userAgent)
        useNavigator->>Window: window.location.href = appStoreUrl
        useNavigator->>Banner: track(APP_DOWNLOAD_BANNER_CLICKED, ...)
    else linkTo matches external (http/https/itms-apps)
        useNavigator->>Window: window.location.href = url
        useNavigator->>Banner: track(BANNER_CLICKED, ...)
    else internal path
        useNavigator->>Router: navigate(path)
        useNavigator->>Banner: (no window.location change)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 분

  • 주의할 파일/영역:
    • useNavigator.ts의 스킴 차단 정규식(대소문자 및 인코딩 엣지케이스)
    • getAppStoreLink()의 userAgent 기반 플랫폼 판별 정확성
    • Banner의 트래킹 호출 인자와 이벤트 이름(USER_EVENT) 사용 일관성
    • 테스트에서 window.location 모킹/복원으로 인한 사이드 이펙트

Possibly related issues

Possibly related PRs

Suggested reviewers

  • oesnuj
  • lepitaaar
  • seongje973

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title '[feature] 앱 출시 배너를 추가한다' clearly summarizes the main objective: adding an app release banner feature.
Linked Issues check ✅ Passed All coding requirements from MOA-427 are met: patch note banner removed, app release banner added with platform-specific store links, security enhancements to useNavigator implemented, and comprehensive tests added.
Out of Scope Changes check ✅ Passed All changes directly support the linked issue objectives: banner replacement, app store navigation, security improvements, and test coverage are all within scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/#937-add-app-banner-MOA-427

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@seongwon030 seongwon030 linked an issue Dec 17, 2025 that may be closed by this pull request
2 tasks
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: 3

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 2978c99 and 57db215.

⛔ Files ignored due to path filters (2)
  • frontend/src/assets/images/banners/banner_desktop4.png is excluded by !**/*.png
  • frontend/src/assets/images/banners/banner_mobile4.png is excluded by !**/*.png
📒 Files selected for processing (3)
  • frontend/src/hooks/useNavigator.ts (1 hunks)
  • frontend/src/pages/MainPage/components/Banner/Banner.tsx (2 hunks)
  • frontend/src/pages/MainPage/components/Banner/bannerData.ts (2 hunks)
🧰 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 with if/else or 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/pages/MainPage/components/Banner/Banner.tsx
  • frontend/src/pages/MainPage/components/Banner/bannerData.ts
  • frontend/src/hooks/useNavigator.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/Banner/Banner.tsx
frontend/**/*.{ts,tsx}

📄 CodeRabbit inference engine (frontend/.cursorrules)

Use consistent return types for similar functions/hooks

Files:

  • frontend/src/pages/MainPage/components/Banner/Banner.tsx
  • frontend/src/pages/MainPage/components/Banner/bannerData.ts
  • frontend/src/hooks/useNavigator.ts
🔇 Additional comments (2)
frontend/src/pages/MainPage/components/Banner/bannerData.ts (1)

5-6: LGTM!

앱 출시 배너로의 전환이 올바르게 구현되었습니다. 이미지 임포트와 배너 데이터 구조가 기존 패턴을 잘 따르고 있으며, APP_STORE_LINK 토큰은 Banner 컴포넌트에서 플랫폼별 스토어 링크로 적절히 해석됩니다.

Also applies to: 32-36

frontend/src/pages/MainPage/components/Banner/Banner.tsx (1)

48-57: LGTM!

handleBannerClick 함수는 undefined URL에 대한 적절한 가드를 포함하고 있으며, APP_STORE_LINK 토큰을 플랫폼별 스토어 링크로 올바르게 해석한 후 네비게이션을 수행합니다. 로직이 명확하고 안전합니다.

- javascript, data, vbscript 프로토콜 차단으로 XSS 방지
- 허용 프로토콜 화이트리스트 방식으로 변경
- 빈 URL 처리 테스트
- 악성 프로토콜 차단 테스트 (javascript, data, vbscript)
- 외부 URL 이동 테스트 (https, http, itms-apps)
- 내부 페이지 이동 테스트
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

🧹 Nitpick comments (1)
frontend/src/hooks/useNavigator.ts (1)

12-13: 위험 프로토콜 목록에 추가 프로토콜을 포함하는 것을 권장합니다.

현재 javascript:, data:, vbscript: 프로토콜만 차단하고 있습니다. file:blob: 프로토콜도 보안 위험이 될 수 있으므로 차단 목록에 추가하는 것이 좋습니다.

다음 diff를 적용하여 차단 목록을 확장하세요:

-const isDangerousProtocol = /^(javascript|data|vbscript):/i.test(trimmedUrl);
+const isDangerousProtocol = /^(javascript|data|vbscript|file|blob):/i.test(trimmedUrl);
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 9e174c3 and 6203c5d.

📒 Files selected for processing (2)
  • frontend/src/hooks/__tests__/useNavigator.test.ts (1 hunks)
  • frontend/src/hooks/useNavigator.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
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 with if/else or 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/hooks/useNavigator.ts
  • frontend/src/hooks/__tests__/useNavigator.test.ts
frontend/**/*.{ts,tsx}

📄 CodeRabbit inference engine (frontend/.cursorrules)

Use consistent return types for similar functions/hooks

Files:

  • frontend/src/hooks/useNavigator.ts
  • frontend/src/hooks/__tests__/useNavigator.test.ts
🧠 Learnings (1)
📚 Learning: 2025-07-20T11:48:50.207Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 541
File: frontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.tsx:41-43
Timestamp: 2025-07-20T11:48:50.207Z
Learning: moadong 프로젝트는 Next.js가 아닌 순수 React + react-router-dom 기반의 CSR(Client-Side Rendering) SPA이므로, window 객체 사용에 대한 SSR 호환성 문제를 제기하지 않아야 합니다.

Applied to files:

  • frontend/src/hooks/__tests__/useNavigator.test.ts
🔇 Additional comments (3)
frontend/src/hooks/useNavigator.ts (2)

15-15: LGTM! 이전 리뷰의 보안 우려사항이 해결되었습니다.

외부 URL 탐지 로직이 올바르게 구현되었으며, 위험한 프로토콜 차단(12-13줄)이 추가되어 이전 리뷰에서 제기된 보안 문제가 해결되었습니다.


18-18: 외부 링크 탐색 방식 변경을 확인해주세요.

외부 URL 탐색 방식이 window.open(trimmedUrl, '_blank', 'noopener,noreferrer')에서 window.location.href = trimmedUrl로 변경되었습니다. 이는 외부 링크가 새 탭이 아닌 현재 탭에서 열리도록 변경하는 것으로, 사용자 경험에 큰 영향을 미칩니다.

  • 이전: 외부 링크를 새 탭에서 열어 현재 페이지 유지
  • 현재: 외부 링크로 현재 탭에서 이동하여 현재 페이지 이탈

앱 스토어 링크의 경우 의도적인 변경일 수 있으나, 다른 외부 링크의 경우 사용자가 새 탭에서 열리기를 기대할 수 있습니다. 이 변경이 의도된 것인지 확인해주세요.

필요한 경우 다음과 같이 URL 유형에 따라 다르게 처리할 수 있습니다:

if (isExternalUrl) {
  // 앱 스토어 링크는 현재 탭에서, 다른 외부 링크는 새 탭에서
  if (/^itms-apps:/.test(trimmedUrl)) {
    window.location.href = trimmedUrl;
  } else {
    window.open(trimmedUrl, '_blank', 'noopener,noreferrer');
  }
} else {
  navigate(trimmedUrl);
}
frontend/src/hooks/__tests__/useNavigator.test.ts (1)

1-126: LGTM! 포괄적이고 잘 구성된 테스트 스uite입니다.

테스트가 다음을 철저하게 검증합니다:

  • 빈 링크 및 공백 처리
  • 위험한 프로토콜 차단 (대소문자 변형 포함)
  • 외부 URL 탐색 (https, http, itms-apps)
  • 내부 경로 탐색 (절대/상대 경로)

테스트 구조가 명확하고, mock 관리가 적절하며, 구현과 완벽하게 일치합니다.

Based on learnings, moadong 프로젝트는 CSR SPA이므로 window 객체 사용에 대한 SSR 호환성 문제가 없습니다.

- Given-When-Then 패턴 주석 추가
- renderHook을 beforeEach로 공통화하여 중복 제거
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

🧹 Nitpick comments (1)
frontend/src/hooks/__tests__/useNavigator.test.ts (1)

54-90: 악성 링크 차단 테스트가 잘 구현되어 있습니다.

javascript:, data:, vbscript: 스킴에 대한 차단과 대소문자 구분 없는 검증이 포함되어 있어 일반적인 XSS 공격 벡터를 효과적으로 커버하고 있습니다.

선택적 개선 사항: 추가 보안 테스트 케이스를 고려해볼 수 있습니다:

  • URL 인코딩된 스킴 (javascript%3Aalert)
  • 공백이 포함된 스킴 우회 시도
  • 다른 잠재적 위험 스킴 (file:, blob:)

현재 구현으로도 주요 공격 벡터는 충분히 방어됩니다.

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 6203c5d and 54ea58e.

📒 Files selected for processing (1)
  • frontend/src/hooks/__tests__/useNavigator.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
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 with if/else or 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/hooks/__tests__/useNavigator.test.ts
frontend/**/*.{ts,tsx}

📄 CodeRabbit inference engine (frontend/.cursorrules)

Use consistent return types for similar functions/hooks

Files:

  • frontend/src/hooks/__tests__/useNavigator.test.ts
🧠 Learnings (1)
📚 Learning: 2025-07-20T11:48:50.207Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 541
File: frontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.tsx:41-43
Timestamp: 2025-07-20T11:48:50.207Z
Learning: moadong 프로젝트는 Next.js가 아닌 순수 React + react-router-dom 기반의 CSR(Client-Side Rendering) SPA이므로, window 객체 사용에 대한 SSR 호환성 문제를 제기하지 않아야 합니다.

Applied to files:

  • frontend/src/hooks/__tests__/useNavigator.test.ts
🔇 Additional comments (4)
frontend/src/hooks/__tests__/useNavigator.test.ts (4)

1-32: 테스트 설정이 올바르게 구성되어 있습니다.

Mock 설정, window.location 백업/복원, 그리고 각 테스트 간 초기화 로직이 적절하게 구현되었습니다. Object.defineProperty를 사용한 window.location 모킹은 Jest에서 표준적인 패턴입니다.


34-52: 빈 링크 처리 테스트가 올바릅니다.

빈 문자열과 공백만 있는 경우 모두 내비게이션이 발생하지 않음을 검증하고 있습니다. 적절한 경계값 테스트입니다.


92-119: 외부 링크 처리 테스트가 정확합니다.

https, http, itms-apps 프로토콜에 대해 window.location.href를 통한 이동과 useNavigate가 호출되지 않음을 모두 검증하고 있습니다. 앱 스토어 딥링크 지원이라는 PR 목표와도 일치합니다.


121-139: 내부 페이지 내비게이션 테스트가 올바릅니다.

절대 경로와 상대 경로 모두 useNavigate를 통해 처리되고 window.location.href가 변경되지 않음을 검증하여 SPA 내비게이션 동작이 올바르게 유지됨을 보장합니다.

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.

빠른추가 수고하셨습니다~

- eventName에 BANNER_CLICKED, APP_DOWNLOAD_BANNER_CLICKED 이벤트 추가
- 배너 클릭 시 bannerId, bannerName, linkTo/platform 로깅
- user-agent 기반 iOS/Android 스토어 링크 분기
- 앱 다운로드 배너 데이터 추가
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

♻️ Duplicate comments (1)
frontend/src/pages/MainPage/components/Banner/Banner.tsx (1)

21-31: iPadOS 감지 로직에 잠재적 문제가 있습니다.

이전 리뷰에서 언급된 것처럼, 최신 iPadOS는 데스크탑 모드에서 macOS와 동일한 user-agent를 전송합니다. macintosh 매칭만으로는 Mac과 iPad를 구분할 수 없어 Mac 사용자가 iOS 앱스토어로 리다이렉트될 수 있습니다.

navigator.maxTouchPoints를 활용하여 개선할 수 있습니다:

 const getAppStoreLink = (): string => {
   const userAgent = navigator.userAgent.toLowerCase();
   
-  if (/iphone|ipad|ipod|macintosh/.test(userAgent)) {
+  // iPad/iPhone/iPod 명시적 감지
+  if (/iphone|ipad|ipod/.test(userAgent)) {
+    return APP_STORE_LINKS.ios;
+  }
+  // macOS Safari에서 iPadOS 감지 (maxTouchPoints > 1)
+  if (/macintosh/.test(userAgent) && navigator.maxTouchPoints > 1) {
     return APP_STORE_LINKS.ios;
   }
   if (/android/.test(userAgent)) {
     return APP_STORE_LINKS.android;
   }
   return APP_STORE_LINKS.default;
 };
🧹 Nitpick comments (2)
frontend/src/pages/MainPage/components/Banner/bannerData.ts (1)

17-23: 매직 스트링 'APP_STORE_LINK'를 상수로 추출하는 것을 권장합니다.

linkTo: 'APP_STORE_LINK'가 Banner.tsx에서 특별히 처리되는 매직 스트링입니다. 명시적인 상수로 추출하면 유지보수성이 향상됩니다.

+// 상수 파일 또는 이 파일 상단에 추가
+export const APP_STORE_LINK_TOKEN = 'APP_STORE_LINK';

// bannerData.ts에서
-    linkTo: 'APP_STORE_LINK',
+    linkTo: APP_STORE_LINK_TOKEN,
frontend/src/pages/MainPage/components/Banner/Banner.tsx (1)

51-59: 플랫폼 감지 로직이 중복되어 있습니다.

라인 24와 56에서 동일한 iOS 감지 로직이 반복됩니다. 또한 getAppStoreLink()와 플랫폼 추적의 로직이 분리되어 있어 향후 불일치가 발생할 수 있습니다.

플랫폼 감지를 통합하는 것을 권장합니다:

+const getPlatform = (): 'ios' | 'android' => {
+  const userAgent = navigator.userAgent.toLowerCase();
+  if (/iphone|ipad|ipod/.test(userAgent)) return 'ios';
+  if (/macintosh/.test(userAgent) && navigator.maxTouchPoints > 1) return 'ios';
+  return 'android';
+};
+
 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;
+  return APP_STORE_LINKS[getPlatform()];
 };

 // handleBannerClick 내부
-      platform: /iphone|ipad|ipod|macintosh/.test(navigator.userAgent.toLowerCase()) ? 'ios' : 'android',
+      platform: getPlatform(),
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 54ea58e and eee9400.

📒 Files selected for processing (3)
  • frontend/src/constants/eventName.ts (1 hunks)
  • frontend/src/pages/MainPage/components/Banner/Banner.tsx (3 hunks)
  • frontend/src/pages/MainPage/components/Banner/bannerData.ts (2 hunks)
🧰 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 with if/else or 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/constants/eventName.ts
  • frontend/src/pages/MainPage/components/Banner/bannerData.ts
  • frontend/src/pages/MainPage/components/Banner/Banner.tsx
frontend/**/*.{ts,tsx}

📄 CodeRabbit inference engine (frontend/.cursorrules)

Use consistent return types for similar functions/hooks

Files:

  • frontend/src/constants/eventName.ts
  • frontend/src/pages/MainPage/components/Banner/bannerData.ts
  • frontend/src/pages/MainPage/components/Banner/Banner.tsx
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/Banner/Banner.tsx
🧬 Code graph analysis (1)
frontend/src/pages/MainPage/components/Banner/Banner.tsx (1)
frontend/src/constants/eventName.ts (1)
  • USER_EVENT (1-36)
🔇 Additional comments (2)
frontend/src/pages/MainPage/components/Banner/Banner.tsx (1)

89-101: 배너 클릭 핸들러 통합이 잘 되어 있습니다.

handleBannerClick에 배너 메타데이터(bannerId, bannerName, linkTo)를 전달하는 방식이 깔끔하고, 이벤트 추적과 내비게이션 로직이 적절히 분리되어 있습니다.

frontend/src/constants/eventName.ts (1)

5-9: 새 배너 이벤트 상수가 적절하게 추가되었습니다.

새 이벤트 상수들(BANNER_CLICKED, APP_DOWNLOAD_BANNER_CLICKED)이 기존 네이밍 컨벤션을 잘 따르고 있습니다.

참고: 라인 8-9의 불필요한 공백을 정리할 수 있습니다.

- 악성 링크 테스트 4개 → 1개 (parameterized)
- 외부 링크 테스트 3개 → 1개 (parameterized)
@seongwon030 seongwon030 merged commit d14a323 into develop-fe Dec 17, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FE Frontend ✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature] MOA-427 패치노트 배너를 앱 출시 배너로 변경한다

3 participants