[feature] 웹뷰 감지 로직 추가 및 웹뷰용 UI 조건부 처리#828
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | 변경 요약 |
|---|---|
웹뷰 감지 유틸frontend/src/utils/isInAppWebView.ts,frontend/src/utils/isInAppWebView.test.ts |
navigator.userAgent에서 MoadongApp 토큰을 검사하는 isInAppWebView() 함수 추가 및 해당 함수 단위테스트 추가 |
ClubDetailPage — 네비게이션 숨김frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx |
isInAppWebView 임포트 후 앱 웹뷰일 때 BackNavigationBar를 렌더링하지 않도록 조건부 처리 (RecommendedClubs는 주석 상태 유지) |
InfoTabs — 스타일 프로퍼티 추가frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.styles.ts,frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsx |
InfoTabWrapper에 isInAppWebView?: boolean prop 추가, 웹뷰일 땐 top: 0px 아니면 top: 70px 적용; InfoTabs에서 isInAppWebView() 결과를 전달 |
페이지 레벨 Header 조건부 렌더링frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx,frontend/src/pages/IntroducePage/IntroducePage.tsx |
isInAppWebView()를 사용해 앱 웹뷰 환경이면 Header를 렌더링하지 않도록 변경 |
Sequence Diagram(s)
sequenceDiagram
participant Browser
participant isInApp as isInAppWebView()
participant Page as Page Component
participant Header as Header / BackNavigationBar
participant InfoTabs as InfoTabs / InfoTabWrapper
Browser->>isInApp: 요청 (navigator.userAgent)
isInApp-->>Page: true / false
alt isInApp == true
Page--xHeader: Header / BackNavigationBar 렌더링 생략
Page->>InfoTabs: <isInAppWebView: true>
InfoTabs->>InfoTabs: top = 0px
else isInApp == false
Page->>Header: Header / BackNavigationBar 렌더링
Page->>InfoTabs: <isInAppWebView: false>
InfoTabs->>InfoTabs: top = 70px
end
Estimated code review effort
🎯 2 (Simple) | ⏱️ ~10 분
- 주의 필요 영역:
isInAppWebView()의 userAgent 매칭이 여러 앱 버전/케이스(대소문자, 접두/접미 토큰 등)에서 정확히 동작하는지 확인- InfoTabs의 top 변경으로 인한 레이아웃/오버랩 문제(특히 모바일 및 다양한 뷰포트) 검증
- ClubDetailPage에서 RecommendedClubs 주석 상태가 의도된 것인지 확인
Possibly related issues
- MOA-282: 웹뷰용 상세페이지 상단 헤더 비노출 처리 — 이 PR은 웹뷰 감지 및 상세페이지/관련 페이지에서 헤더를 숨기는 목적과 일치합니다.
Possibly related PRs
- PR
#819— ClubDetailPage 관련 동일 파일을 변경하는 PR로 RecommendedClubs import/usage에 코드 레벨 충돌 가능성 있음 - PR
#813— ClubDetailPage에서 RecommendedClubs 처리를 수정한 PR로 중복/상충 변경 가능성 있음 - PR
#663— ClubUnionPage의 Header 처리/통합과 관련된 PR로 Header 조건부 렌더링 변경과 직접적 관계 있음
Suggested reviewers
- seongwon030
- lepitaaar
- suhyun113
Pre-merge checks and finishing touches
✅ Passed checks (5 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | ✅ Passed | PR 제목은 웹뷰 감지 로직 추가와 웹뷰용 UI 조건부 처리라는 변경 사항의 핵심을 명확하게 설명하고 있습니다. |
| Linked Issues check | ✅ Passed | PR은 연관 이슈 MOA-282의 요구사항인 웹뷰 환경에서 헤더 비노출 처리를 구현했으며, isInAppWebView 유틸리티를 통해 웹뷰 감지 로직을 추가했습니다. |
| Out of Scope Changes check | ✅ Passed | 모든 변경사항은 웹뷰 감지 및 UI 조건부 처리라는 명확한 범위 내에서 이루어졌으며, 무관한 변경사항은 없습니다. |
| 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/#777-hide-header-webview-MOA-282
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: 2
🧹 Nitpick comments (2)
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (1)
17-17: 중복 훅 호출을 제거하고 props로 전달하세요.
useIsWebView훅이ClubDetailPage와InfoTabs컴포넌트에서 각각 호출되고 있습니다. 같은 값을 중복 계산하는 것보다, 부모 컴포넌트에서 한 번만 호출하고 자식에게 props로 전달하는 것이 더 효율적입니다.Line 61의
InfoTabs컴포넌트에isWebView를 prop으로 전달하도록 수정하세요:- <InfoTabs onTabClick={scrollToSection} /> + <InfoTabs onTabClick={scrollToSection} isWebView={isWebView} />그리고
InfoTabs.tsx에서 훅 호출을 제거하고 props를 받도록 수정하세요 (해당 파일의 별도 코멘트 참조).Also applies to: 23-23
frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsx (1)
5-5: 중복 훅 호출을 제거하고 props로 받으세요.
useIsWebView훅을 이 컴포넌트와 부모 컴포넌트(ClubDetailPage) 양쪽에서 호출하고 있습니다. 부모에서 계산한 값을 props로 받는 것이 더 효율적입니다.다음 diff를 적용하여 props로 받도록 수정하세요:
import { useState } from 'react'; import * as Styled from './InfoTabs.styles'; import useMixpanelTrack from '@/hooks/useMixpanelTrack'; import { EVENT_NAME } from '@/constants/eventName'; -import useIsWebView from '@/hooks/useIsWebview'; const tabLabels = ['모집정보', '동아리정보', '소개글', '활동사진']; -const InfoTabs = ({ onTabClick }: { onTabClick: (index: number) => void }) => { +const InfoTabs = ({ + onTabClick, + isWebView +}: { + onTabClick: (index: number) => void; + isWebView: boolean; +}) => { const [activeTab, setActiveTab] = useState(0); const trackEvent = useMixpanelTrack(); - const { isWebView } = useIsWebView();Also applies to: 12-12
📜 Review details
Configuration used: CodeRabbit 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 selected for processing (4)
frontend/src/hooks/useIsWebview.ts(1 hunks)frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx(2 hunks)frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.styles.ts(1 hunks)frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.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/useIsWebview.tsfrontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.styles.tsfrontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.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/InfoTabs/InfoTabs.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
🧠 Learnings (4)
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions and hooks.
Applied to files:
frontend/src/hooks/useIsWebview.ts
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
Applied to files:
frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsx
📚 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/ClubDetailPage/ClubDetailPage.tsx
📚 Learning: 2025-07-19T05:05:10.196Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 548
File: frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx:17-57
Timestamp: 2025-07-19T05:05:10.196Z
Learning: ClubDetailPage.tsx에서 notJoinedClubNames 배열의 하드코딩은 의도적인 설계 결정입니다. 개발자가 명시적으로 하드코딩을 선택했으므로 이에 대한 리팩토링 제안을 하지 않아야 합니다.
Applied to files:
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
🔇 Additional comments (4)
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (1)
49-49: LGTM!WebView 환경에서 BackNavigationBar를 숨기는 로직이 PR 목적에 부합하며 올바르게 구현되었습니다.
frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsx (1)
25-25: LGTM!
isWebViewprop을 styled component에 올바르게 전달하고 있습니다.frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.styles.ts (1)
3-3: LGTM!
isWebViewoptional prop의 타입 정의가 올바릅니다.frontend/src/hooks/useIsWebview.ts (1)
1-18: 성능 최적화: 결과값을 메모이제이션하세요.userAgent 체크는 모든 렌더링마다 재계산되고 있습니다. 동일한 환경에서는 결과가 변하지 않으므로
useMemo를 사용하여 최적화해야 합니다. 또한 SSR 환경에서navigator가 정의되지 않을 수 있으므로 안전성 체크도 필요합니다.다음 diff를 적용하여 개선하세요:
+import { useMemo } from 'react'; + const useIsWebView = () => { - const userAgent = navigator.userAgent; - - // Android WebView - const isAndroidWebView = /Android/.test(userAgent) && /wv/.test(userAgent); - - // iOS WebView - const isIOSWebView = - /iPhone|iPad|iPod/.test(userAgent) && - /AppleWebKit/.test(userAgent) && - !/Safari/.test(userAgent); - - return { - isWebView: isAndroidWebView || isIOSWebView, - isIOSWebView, - isAndroidWebView, - }; + return useMemo(() => { + if (typeof navigator === 'undefined') { + return { + isWebView: false, + isIOSWebView: false, + isAndroidWebView: false, + }; + } + + const userAgent = navigator.userAgent; + + // Android WebView + const isAndroidWebView = /Android/.test(userAgent) && /wv/.test(userAgent); + + // iOS WebView + const isIOSWebView = + /iPhone|iPad|iPod/.test(userAgent) && + /AppleWebKit/.test(userAgent) && + !/Safari/.test(userAgent); + + return { + isWebView: isAndroidWebView || isIOSWebView, + isIOSWebView, + isAndroidWebView, + }; + }, []); };⛔ Skipped due to learnings
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 호환성 문제를 제기하지 않아야 합니다.
| display: none; | ||
| position: sticky; | ||
| top: 70px; | ||
| top: ${({ isWebView }) => (isWebView ? '0px' : '70px')}; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
매직 넘버를 명명된 상수로 교체하세요.
0px와 70px 값을 직접 사용하는 대신 명명된 상수로 교체하여 의미를 명확히 하세요.
As per coding guidelines.
파일 상단에 상수를 정의하고 사용하도록 수정하세요:
import styled from 'styled-components';
+const TAB_TOP_OFFSET_WITH_HEADER = '70px';
+const TAB_TOP_OFFSET_WEBVIEW = '0px';
+
export const InfoTabWrapper = styled.div<{ isWebView?: boolean }>`
display: none;
position: sticky;
- top: ${({ isWebView }) => (isWebView ? '0px' : '70px')};
+ top: ${({ isWebView }) => (isWebView ? TAB_TOP_OFFSET_WEBVIEW : TAB_TOP_OFFSET_WITH_HEADER)};
z-index: 1;📝 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.
| top: ${({ isWebView }) => (isWebView ? '0px' : '70px')}; | |
| import styled from 'styled-components'; | |
| const TAB_TOP_OFFSET_WITH_HEADER = '70px'; | |
| const TAB_TOP_OFFSET_WEBVIEW = '0px'; | |
| export const InfoTabWrapper = styled.div<{ isWebView?: boolean }>` | |
| display: none; | |
| position: sticky; | |
| top: ${({ isWebView }) => (isWebView ? TAB_TOP_OFFSET_WEBVIEW : TAB_TOP_OFFSET_WITH_HEADER)}; | |
| z-index: 1; |
🤖 Prompt for AI Agents
In frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.styles.ts
around line 6, the inline magic numbers '0px' and '70px' should be replaced with
named constants: define meaningful constants at the top of the file (e.g.,
WEBVIEW_TOP_OFFSET and DEFAULT_TOP_OFFSET) and use them in the styled component
expression instead of the literal strings to clarify intent and follow coding
guidelines.
…thub.com/Moadong/moadong into feature/#777-hide-header-webview-MOA-282
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
frontend/src/hooks/useIsWebview.ts (1)
11-14: 이전 리뷰에서 지적된 iOS WebView 감지 로직 미개선이 iOS WebView 감지 로직은 이전 리뷰에서 이미 신뢰할 수 없는 방식으로 지적되었습니다.
!/Safari/.test(userAgent)검사는 최신 WKWebView에서 false negative를 발생시킬 수 있습니다.이전 리뷰의 권장사항을 적용해주세요:
- 앱 수준에서
customUserAgent설정 또는 커스텀 HTTP 헤더 사용- 명시적인 토큰 감지 추가 (예:
/\bWKWebView\b/i또는 앱별 토큰)
🧹 Nitpick comments (1)
frontend/src/hooks/useIsWebview.ts (1)
1-33: 정규식 패턴을 named constants로 추출하세요코딩 가이드라인에 따라 인라인 정규식을 명명된 상수로 추출하면 가독성과 유지보수성이 향상됩니다.
Based on coding guidelines
다음과 같이 상수를 파일 상단에 정의하세요:
// WebView 감지용 정규식 패턴 const REACT_NATIVE_WEBVIEW_PATTERN = /MoadongApp/; const ANDROID_WEBVIEW_PATTERN = /wv/; const ANDROID_PLATFORM_PATTERN = /Android/; const IOS_DEVICE_PATTERN = /iPhone|iPad|iPod/; const APPLE_WEBKIT_PATTERN = /AppleWebKit/; const SAFARI_PATTERN = /Safari/; const useIsWebView = () => { const result = useMemo(() => { const userAgent = navigator.userAgent; // React Native WebView (MoadongApp 커스텀) const isReactNativeWebView = REACT_NATIVE_WEBVIEW_PATTERN.test(userAgent); // Android Native WebView const isAndroidWebView = ANDROID_PLATFORM_PATTERN.test(userAgent) && ANDROID_WEBVIEW_PATTERN.test(userAgent); // iOS Native WebView const isIOSWebView = IOS_DEVICE_PATTERN.test(userAgent) && APPLE_WEBKIT_PATTERN.test(userAgent) && !SAFARI_PATTERN.test(userAgent); const isWebView = isReactNativeWebView || isAndroidWebView || isIOSWebView; return { isWebView, isReactNativeWebView, isAndroidWebView, isIOSWebView, }; }, []); // ... rest of the code };
📜 Review details
Configuration used: CodeRabbit 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 selected for processing (1)
frontend/src/hooks/useIsWebview.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
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/useIsWebview.ts
🧠 Learnings (2)
📓 Common learnings
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')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions and hooks.
Applied to files:
frontend/src/hooks/useIsWebview.ts
lepitaaar
left a comment
There was a problem hiding this comment.
user-agent를이용해 모바일에서 다른 화면 보여주는거 신기하네요!
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.styles.ts (1)
3-6: 매직 넘버를 명명된 상수로 교체하세요.이전 리뷰에서 이미 지적된 것처럼,
'0px'와'70px'값을 명명된 상수로 교체하여 의미를 명확히 하세요.파일 상단에 상수를 정의하고 사용하세요:
import styled from 'styled-components'; +const TAB_TOP_OFFSET_WITH_HEADER = '70px'; +const TAB_TOP_OFFSET_WEBVIEW = '0px'; + -export const InfoTabWrapper = styled.div<{ isInAppWebView?: boolean }>` +export const InfoTabWrapper = styled.div<{ isInAppWebView?: boolean }>` display: none; position: sticky; - top: ${({ isInAppWebView }) => (isInAppWebView ? '0px' : '70px')}; + top: ${({ isInAppWebView }) => (isInAppWebView ? TAB_TOP_OFFSET_WEBVIEW : TAB_TOP_OFFSET_WITH_HEADER)}; z-index: 1;As per coding guidelines.
🧹 Nitpick comments (2)
frontend/src/pages/IntroducePage/IntroducePage.tsx (1)
11-17: 조건부 헤더 렌더링 패턴의 중복을 고려하세요.IntroducePage, ClubUnionPage에서 동일한 조건부 헤더 렌더링 패턴(
{!isInAppWebView() && <Header />})이 반복됩니다. 현재 구현은 명확하고 간단하지만, 향후 로직이 복잡해질 경우를 대비해 재사용 가능한ConditionalHeader컴포넌트로 추출하는 것을 고려해보세요.예시:
// components/common/ConditionalHeader/ConditionalHeader.tsx import Header from '@/components/common/Header/Header'; import isInAppWebView from '@/utils/isInAppWebView'; const ConditionalHeader = () => { if (isInAppWebView()) return null; return <Header />; }; export default ConditionalHeader;사용:
-import Header from '@/components/common/Header/Header'; +import ConditionalHeader from '@/components/common/ConditionalHeader/ConditionalHeader'; -import isInAppWebView from '@/utils/isInAppWebView'; const IntroducePage = () => { return ( <> <Styled.IntroducePageHeader> - {!isInAppWebView() && <Header />} + <ConditionalHeader /> </Styled.IntroducePageHeader>frontend/src/utils/isInAppWebView.ts (1)
1-1: 함수명이 더 구체적이면 좋겠습니다.함수명이
isInAppWebView로 되어 있지만, 실제로는 'MoadongApp' 특정 UserAgent를 검사합니다. 향후 다른 인앱 웹뷰 환경을 지원할 가능성이 있다면,isInMoadongApp또는isMoadongAppWebView같이 더 구체적인 이름을 사용하는 것이 명확할 수 있습니다.예시:
-const isInAppWebView = () => /MoadongApp/.test(navigator.userAgent); +const isMoadongAppWebView = () => /MoadongApp/.test(navigator.userAgent); -export default isInAppWebView; +export default isMoadongAppWebView;또는 현재 이름을 유지하되, 주석을 추가하여 의도를 명확히 할 수 있습니다:
+/** + * MoadongApp의 커스텀 UserAgent를 감지하여 React Native 웹뷰 환경인지 확인합니다. + */ const isInAppWebView = () => /MoadongApp/.test(navigator.userAgent);
📜 Review details
Configuration used: CodeRabbit 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 selected for processing (6)
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx(2 hunks)frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.styles.ts(1 hunks)frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsx(2 hunks)frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx(1 hunks)frontend/src/pages/IntroducePage/IntroducePage.tsx(1 hunks)frontend/src/utils/isInAppWebView.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
- frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsx
🧰 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/utils/isInAppWebView.tsfrontend/src/pages/IntroducePage/IntroducePage.tsxfrontend/src/pages/ClubUnionPage/ClubUnionPage.tsxfrontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.styles.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/IntroducePage/IntroducePage.tsxfrontend/src/pages/ClubUnionPage/ClubUnionPage.tsx
🧠 Learnings (5)
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
Applied to files:
frontend/src/pages/IntroducePage/IntroducePage.tsxfrontend/src/pages/ClubUnionPage/ClubUnionPage.tsx
📚 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/ClubUnionPage/ClubUnionPage.tsx
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Applied to files:
frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.styles.ts
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.{ts,tsx} : Define constants near related logic or ensure names link them clearly.
Applied to files:
frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.styles.ts
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Colocate simple, localized logic or use inline definitions to reduce context switching.
Applied to files:
frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.styles.ts
⏰ 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
🔇 Additional comments (2)
frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx (1)
7-14: 일관된 구현입니다.IntroducePage와 동일한 패턴으로 구현되어 일관성이 유지되고 있습니다. 앞서 언급한 것처럼, 향후 이 패턴이 더 많은 페이지에서 반복될 경우 재사용 가능한 컴포넌트로 추출하는 것을 고려하세요.
frontend/src/utils/isInAppWebView.ts (1)
1-1: 이 리뷰 의견은 프로젝트 아키텍처에 맞지 않습니다.retrieved learnings에 따르면, moadong 프로젝트는 Next.js가 아닌 순수 React + react-router-dom 기반의 CSR(Client-Side Rendering) SPA입니다. SSR 환경을 고려할 필요가 없으므로,
navigator객체 접근 안전성 관련 지적은 타당하지 않습니다.
isInAppWebView()함수는 현재 상태 그대로 문제없습니다.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
frontend/src/utils/isInAppWebView.test.ts (2)
6-13: 속성 디스크립터 일관성 개선을 고려하세요.
afterEach에서writable: false로 설정하고 있지만, 테스트 케이스(17-20줄, 31-34줄)에서는writable속성을 명시하지 않고 있습니다. 원래 userAgent의 속성 디스크립터와 일치하지 않을 수 있습니다.다음과 같이 개선할 수 있습니다:
afterEach(() => { - // 각 테스트 후 userAgent 원복 Object.defineProperty(navigator, 'userAgent', { value: originalUserAgent, - writable: false, configurable: true, }); });그리고 테스트 케이스에서도 일관되게
writable을 명시하지 않는 것이 좋습니다.
15-41: 엣지 케이스 테스트 추가를 고려하세요.현재 테스트는 기본적인 양성/음성 케이스를 잘 다루고 있지만, 다음과 같은 엣지 케이스 테스트를 추가하면 더욱 견고해질 수 있습니다:
- 대소문자 구분 테스트 (
"moadongapp"vs"MoadongApp")- 부분 문자열 매칭 (
"MoadongAppV2"같은 변형)- 빈 문자열이나 특수 문자가 포함된 경우
예시:
test('userAgent에 "MoadongApp"의 대소문자 변형이 있을 때', () => { Object.defineProperty(navigator, 'userAgent', { value: 'Mozilla/5.0 moadongapp', configurable: true, }); const result = isInAppWebView(); // 구현체에 따라 true 또는 false 예상 expect(result).toBe(false); // 대소문자 구분 시 });
📜 Review details
Configuration used: CodeRabbit 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 selected for processing (1)
frontend/src/utils/isInAppWebView.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
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/utils/isInAppWebView.test.ts
🔇 Additional comments (1)
frontend/src/utils/isInAppWebView.test.ts (1)
1-42: 전체적으로 잘 작성된 테스트입니다.테스트 구조가 명확하고, Given-When-Then 주석을 통해 각 테스트의 의도를 잘 표현하고 있습니다.
afterEach를 통한 정리 로직도 적절하게 구현되어 있으며, 핵심 기능을 충분히 검증하고 있습니다.
#️⃣연관된 이슈
📝작업 내용
1. isInAppWebView 유틸리티 함수 구현 및 테스트 코드 추가
2. 클럽 상세페이지 웹뷰 최적화
sticky top위치를 0px로 조정하여 빈 공간 제거3. 소개페이지, 총동연 소개 페이지 웹뷰 대응
Summary by CodeRabbit
업데이트 내용
새로운 기능
스타일
테스트