-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: 변경된 밸런스게임 조회 api 파라미터 수정 #264
base: dev
Are you sure you want to change the base?
Conversation
…ateDropdown.tsx Co-authored-by: Banana_milk <[email protected]>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
개요Walkthrough이번 풀 리퀘스트는 애플리케이션의 모바일 반응형 디자인을 강화하고, 다양한 컴포넌트의 스타일과 기능을 개선하는 광범위한 변경 사항을 포함하고 있습니다. 주요 초점은 모바일 뷰포트에 최적화된 레이아웃, 새로운 모바일 컴포넌트 추가, 그리고 기존 컴포넌트의 반응형 스타일링입니다. Changes
시퀀스 다이어그램sequenceDiagram
participant User
participant App
participant MobileHook
participant Components
User->>App: 페이지 로드
App->>MobileHook: useIsMobile() 호출
MobileHook-->>App: 모바일 여부 반환
App->>Components: 모바일/데스크톱 렌더링 결정
Components->>User: 적절한 UI 렌더링
연결된 이슈에 대한 평가
시 (Poem)
Tip CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 23
🧹 Nitpick comments (40)
src/components/molecules/CategoryBox/CategoryBox.tsx (1)
11-24
: 코드 중복을 줄일 수 있는 여지가 있습니다.
CategoryButton
에isMobile
prop을 전달하는 부분에서 중복이 발생합니다. 다음과 같은 개선을 고려해보세요:- <CategoryButton isMobile imageType="PickVote" label="톡&픽 플레이스" /> + <CategoryButton + {...(isMobile && { isMobile })} + imageType="PickVote" + label="톡&픽 플레이스" + />또는 모바일 버전용 버튼들을 별도의 컴포넌트로 추출하는 것도 고려해볼 수 있습니다:
const MobileCategoryButtons = ({ handleService }: { handleService: () => void }) => ( <> <Link to="/talkpickplace"> <CategoryButton isMobile imageType="PickVote" label="톡&픽 플레이스" /> </Link> <CategoryButton isMobile imageType="RandomGame" label="랜덤 밸런스 게임" onClick={handleService} /> </> );src/components/molecules/SearchTagBar/SearchTagBar.tsx (1)
23-29
: 조건부 렌더링 로직을 개선할 수 있습니다.현재 구현은 작동하지만, 다음과 같은 개선사항을 제안드립니다:
- SearchBar 컴포넌트로 전달되는 isMobile prop의 영향이 명확하지 않습니다. 주석이나 문서화가 필요해 보입니다.
- 조건부 렌더링 로직을 더 간단하게 만들 수 있습니다.
다음과 같이 개선해 보시는 것은 어떨까요?:
- return isMobile ? ( - <SearchBar - isMobile - onInputChange={handleInputChange} - onSearchClick={handleSearchClick} - value={inputValue} - /> - ) : ( + const commonSearchBarProps = { + onInputChange: handleInputChange, + onSearchClick: handleSearchClick, + value: inputValue, + }; + + return ( <div css={S.searchBarContainerStyling}> - <SearchBar - onInputChange={handleInputChange} - onSearchClick={handleSearchClick} - value={inputValue} - /> + <SearchBar {...commonSearchBarProps} isMobile={isMobile} /> + {!isMobile && ( <div css={S.btnWrapperStyling}> {KEYWORD_TAG.map((keyWord) => ( <Button key={keyWord} size="large" variant="outlinePrimary"> #{keyWord} </Button> ))} </div> + )} </div> );src/components/mobile/atoms/DraftSaveButton/DraftSaveButton.tsx (1)
8-14
: 접근성 개선을 위한 제안사항이 있습니다.버튼 컴포넌트의 기본 구현은 잘 되어있으나, 다음과 같은 접근성 속성들을 추가하면 좋을 것 같습니다:
<button css={S.buttonStyle} type="button" onClick={onClick} + aria-label="임시저장" + role="button" > 임시저장 </button>src/components/atoms/Bookmark/Bookmark.style.ts (1)
14-17
: CSS 변수 사용을 통한 유지보수성 개선을 제안드립니다.현재 하드코딩된 픽셀값들을 CSS 변수로 관리하면 일관성 있는 유지보수가 가능할 것 같습니다.
+const styles = { + bookmark: { + desktop: { + size: '30px', + }, + mobile: { + size: '19px', + }, + }, +} as const; export const bookmarkButton = css` display: flex; align-items: center; justify-content: center; - width: 30px; - height: 30px; + width: ${styles.bookmark.desktop.size}; + height: ${styles.bookmark.desktop.size}; flex-shrink: 0; background-color: transparent; border: none; cursor: pointer; padding: 0; @media (max-width: 430px) { - width: 19px; - height: 19px; + width: ${styles.bookmark.mobile.size}; + height: ${styles.bookmark.mobile.size}; } `;src/hooks/common/useIsMobile.ts (1)
14-16
: 이벤트 리스너 최적화 필요리사이즈 이벤트가 너무 자주 발생하면 성능에 영향을 줄 수 있습니다.
디바운스를 적용하는 것을 추천드립니다:
+import { debounce } from 'lodash'; + useEffect(() => { - checkIsMobile(); - window.addEventListener('resize', checkIsMobile); + const debouncedCheck = debounce(checkIsMobile, 250); + checkIsMobile(); + window.addEventListener('resize', debouncedCheck); - return () => window.removeEventListener('resize', checkIsMobile); + return () => { + window.removeEventListener('resize', debouncedCheck); + debouncedCheck.cancel(); + }; }, []);src/stories/mobile/atoms/DraftSaveButton.stories.tsx (2)
7-23
: 스토리 구성 개선 필요스토리북 설정에 다음 사항들이 보완되면 좋을 것 같습니다:
- 모바일 뷰포트 설정이 없습니다
- 버튼의 다양한 상태를 보여주는 스토리가 없습니다
다음과 같이 개선하는 것을 추천드립니다:
const meta: Meta<typeof DraftSaveButton> = { title: 'mobile/atoms/DraftSaveButton', component: DraftSaveButton, parameters: { layout: 'centered', + viewport: { + defaultViewport: 'mobile', + }, }, argTypes: { - onClick: { action: '클릭 이벤트 발생' }, + onClick: { + action: '임시저장 버튼 클릭', + description: '클릭 시 임시저장 로직이 실행됩니다', + }, }, decorators: [ (Story) => ( <BrowserRouter> <Story /> </BrowserRouter> ), ], };
28-28
: 스토리 변형 추가 필요기본 스토리만으로는 컴포넌트의 다양한 상태를 테스트하기 어렵습니다.
다음과 같은 추가 스토리를 작성하는 것을 추천드립니다:
export const Loading: Story = { args: { isLoading: true, }, }; export const Disabled: Story = { args: { disabled: true, }, }; export const WithCustomText: Story = { args: { children: '커스텀 텍스트', }, };src/stories/mobile/atoms/MobileToggleGroup.stories.tsx (1)
29-29
: 스토리 구현을 보완하는 것이 좋습니다.현재 Default 스토리가 비어있습니다. 컴포넌트의 다양한 상태와 사용 사례를 보여주는 스토리를 추가하면 좋겠습니다.
export const Default: Story = { args: { selectedValue: { field: 'views', order: 'desc' }, }, }; export const WithDifferentSelection: Story = { args: { selectedValue: { field: 'latest', order: 'asc' }, }, };src/components/mobile/atoms/FloatingButton/FloatingButton.style.ts (2)
5-23
: 전반적인 스타일 구조가 잘 설계되었습니다.스타일 구조와 반응형 디자인이 잘 구현되어 있습니다. 다만, transition 속성을 더 구체적으로 지정하면 좋을 것 같습니다.
다음과 같이 수정을 제안드립니다:
- transition: 'all 0.3s ease-in', + transition: 'box-shadow 0.3s ease-in',
12-19
: 매직 넘버를 상수로 분리하는 것을 고려해보세요.현재 하드코딩된 수치들(padding, gap, width 등)을 의미 있는 상수로 분리하면 유지보수성이 향상될 것 같습니다.
다음과 같은 방식을 제안드립니다:
const FLOATING_BUTTON = { PADDING: { LEFT: '21px', RIGHT: '5px', }, GAP: '13px', WIDTH: { TALKPICK: '102px', GAME: '139px', }, MARGIN: { TALKPICK: '37px', }, } as const;src/components/atoms/Chips/Chips.style.ts (1)
15-15
: 브레이크포인트 값을 상수로 분리하는 것을 추천드립니다.여러 곳에서 사용되는 미디어 쿼리 브레이크포인트 값(430px)을 상수로 분리하면 유지보수성이 향상될 것 같습니다.
다음과 같은 방식을 제안드립니다:
// src/styles/breakpoints.ts export const BREAKPOINTS = { MOBILE: '430px', } as const; // 사용 예시 import { BREAKPOINTS } from '@/styles/breakpoints'; `@media (max-width: ${BREAKPOINTS.MOBILE})`Also applies to: 26-26
src/stories/mobile/atoms/MobileSideMenu.stories.tsx (1)
1-2
: ESLint 비활성화 주석 개선 필요전역 ESLint 비활성화 주석 대신 특정 라인에 대한 비활성화를 고려해주세요. 현재 방식은 파일 전체에 영향을 미치므로 코드 품질 관리에 어려움이 있을 수 있습니다.
-/* eslint-disable no-console */ -/* eslint-disable @typescript-eslint/no-unused-vars */ +// eslint-disable-next-line no-console, @typescript-eslint/no-unused-varssrc/stories/mobile/molecules/FloatingMenuButton.stories.tsx (1)
15-25
: Provider 중첩 구조 개선 제안현재 Provider 중첩이 다소 복잡해 보입니다. 테스트 환경 설정을 위한 별도의 래퍼 컴포넌트를 만들어 사용하는 것을 고려해보세요.
const TestWrapper = ({ children }: { children: React.ReactNode }) => ( <Provider store={store}> <ReactQueryProvider> <Router>{children}</Router> </ReactQueryProvider> </Provider> ); // decorators에서 사용 decorators: [(Story) => <TestWrapper><Story /></TestWrapper>]src/components/mobile/atoms/MobileToggleGroup/MobileToggleGroup.style.ts (2)
10-21
: 클릭된 토글의 스타일 구현에 대한 제안현재 구현은 잘 작동하지만, 다음과 같은 개선을 고려해보세요:
- isOpen 상태에 따른 스타일 변화를 더 명확하게 구분
- 반복되는 스타일 값들을 상수로 분리
다음과 같이 개선해 보세요:
+const TOGGLE_PADDING = '4px 8px 4px 9px'; +const BORDER_RADIUS = '6px'; export const clickedToggleStyle = (isOpen: boolean) => css(typo.Mobile.Text.SemiBold_12, { color: color.MAIN, backgroundColor: color.WT_VIOLET, display: 'flex', alignItems: 'center', gap: '8px', - padding: '4px 8px 4px 9px', + padding: TOGGLE_PADDING, borderBottom: isOpen ? `0.6px solid ${color.SKYBLUE}` : undefined, - borderRadius: isOpen ? undefined : '6px', + borderRadius: isOpen ? undefined : BORDER_RADIUS, cursor: 'pointer', });
23-33
: 미클릭 토글의 hover 효과 개선 제안현재 hover 효과가 즉시 적용되는데, 부드러운 전환을 위해 배경색 변경에만 transition을 적용하는 것이 좋습니다.
다음과 같이 수정해보세요:
export const unClickedToggleStyle = css(typo.Mobile.Text.SemiBold_12, { color: color.GY[1], backgroundColor: color.WT, padding: '4px 26px 4px 9px', - transition: 'all .1s ease-in', + transition: 'background-color .1s ease-in', borderRadius: '0 0 6px 6px', cursor: 'pointer', '&:hover': { backgroundColor: color.WT_VIOLET, }, });src/pages/LandingPage/LandingPage.style.ts (1)
32-37
: 검색 박스 스타일의 모바일 대응 필요모바일 환경에서 마진과 갭이 너무 크게 설정되어 있습니다. 화면 크기에 따른 반응형 조정이 필요합니다.
다음과 같이 수정해보세요:
export const searchBoxStyle = css({ margin: '30px 0', gap: '14px', display: 'flex', flexDirection: 'column', + '@media (max-width: 768px)': { + margin: '20px 0', + gap: '10px', + }, });src/components/atoms/CategoryButton/CategoryButton.tsx (1)
27-32
: 모바일 조건부 렌더링 로직이 개선될 수 있습니다.현재 구현은 작동하지만, 반복되는 삼항 연산자 패턴을 개선할 수 있습니다.
다음과 같이 리팩토링하는 것을 고려해보세요:
- ImageComponent = isMobile ? PickVoteSmall : PickVote; + const components = { + PickVote: { default: PickVote, mobile: PickVoteSmall }, + RandomGame: { default: RandomGame, mobile: RandomGameSmall }, + TodayPick: { default: TodayPick, mobile: TodayPick } + }; + ImageComponent = components[imageType][isMobile ? 'mobile' : 'default'];src/components/atoms/Bookmark/Bookmark.tsx (1)
27-32
: renderIcon 함수 구현이 개선될 수 있습니다.현재 구현도 잘 작동하지만, 조건부 렌더링을 더 간결하게 만들 수 있습니다.
다음과 같은 리팩토링을 제안합니다:
const renderIcon = () => { - if (isPressed) { - return isMobile ? <BookmarkPRSmall /> : <BookmarkPR css={S.icon} />; - } - return isMobile ? <BookmarkDFSmall /> : <BookmarkDF css={S.icon} />; + const Icon = isPressed + ? (isMobile ? BookmarkPRSmall : BookmarkPR) + : (isMobile ? BookmarkDFSmall : BookmarkDF); + return <Icon css={!isMobile ? S.icon : undefined} />; };src/components/organisms/BalanceGameList/BalanceGameList.style.ts (1)
6-12
: 미디어 쿼리 브레이크포인트를 상수로 분리하는 것을 추천드립니다.여러 곳에서 반복되는
430px
브레이크포인트를 상수로 분리하면 유지보수가 더욱 용이해질 것 같습니다.다음과 같이 변경하는 것을 제안드립니다:
+ const MOBILE_BREAKPOINT = '430px'; export const containerStyle = css({ width: '1158px', - '@media (max-width: 430px)': { + `@media (max-width: ${MOBILE_BREAKPOINT})`: { maxWidth: '335px', // ... }, });Also applies to: 20-25, 32-34, 41-43, 51-53
src/components/molecules/TopBanner/TodayTalkPickBanner/TodayTalkPickBanner.tsx (1)
5-6
: 모바일 대응이 적절하게 구현되었습니다.
useIsMobile
훅을 사용하여 디바이스에 따라 적절한 크기의 아이콘을 렌더링하는 방식이 좋습니다.단,
Check
와CheckSmall
컴포넌트의 props 타입을 명시하면 더 좋을 것 같습니다:import { Check, CheckSmall } from '@/assets'; type CheckProps = { // 공통 props 정의 };Also applies to: 20-20, 35-35
src/components/organisms/Header/Header.style.ts (1)
5-22
: z-index 값을 상수로 관리하는 것을 추천드립니다.고정 헤더의
z-index: 1000
값을 상수로 분리하여 관리하면 다른 컴포넌트들과의 레이어 관계를 더 명확하게 관리할 수 있을 것 같습니다.다음과 같이 변경하는 것을 제안드립니다:
const Z_INDICES = { HEADER: 1000, MODAL: 1100, // 다른 z-index 값들... } as const; export const containerStyle = css` // ... z-index: ${Z_INDICES.HEADER}; `;src/stories/mobile/atoms/FloatingButton.stories.tsx (1)
1-1
: 콘솔 로그 제거 필요프로덕션 코드에서는
console.log
문을 제거하는 것이 좋습니다. 디버깅 목적으로 사용된 콘솔 로그가 남아있습니다.다음과 같이 수정하는 것을 제안합니다:
- /* eslint-disable no-console */ - onClick={() => console.log('TalkPick button clicked')} + onClick={() => {}}- onClick={() => console.log('Game button clicked')} + onClick={() => {}}Also applies to: 34-34, 42-42
src/components/molecules/TopBanner/TopBanner.style.ts (1)
8-10
: 스타일 상수 추출 권장반복되는 미디어 쿼리 브레이크포인트와 매직 넘버(예: 430px, 227px 등)를 상수로 추출하면 유지보수가 더 용이해질 것 같습니다.
다음과 같은 방식으로 상수를 추출하는 것을 제안합니다:
const BREAKPOINTS = { MOBILE: '430px', } as const; const DIMENSIONS = { MOBILE: { BANNER_HEIGHT: '227px', DOT_SIZE: '4px', DOT_GAP: '5px', ACTIVE_DOT_WIDTH: '32px', }, } as const;Also applies to: 31-34, 46-50, 57-61
src/stories/molecules/CategoryBar.stories.tsx (1)
21-21
: 스토리 문서화 개선 필요
activeTab
의 빈 문자열 처리에 대한 설명이 부족합니다. 다음 사항들의 추가를 제안드립니다:
- 빈 문자열이 의미하는 상태 설명
- API 파라미터와의 연관성 명시
- 각 탭 상태별 동작 방식 설명
// Story 설명 예시 /** * activeTab이 빈 문자열('')일 경우: * - 인기 탭으로 간주 * - API 호출 시 tagName 파라미터 제외 * * activeTab이 특정 값일 경우: * - 해당 태그로 필터링 * - API 호출 시 tagName 파라미터 포함 */Also applies to: 55-55
src/stories/atoms/CategoryButton.stories.tsx (1)
52-74
: Mobile 스토리의 구현 방식 개선이 필요합니다.Mobile 스토리에서 다음과 같은 개선사항을 제안드립니다:
- TodayPick 케이스가 누락되어 있습니다.
- 모바일 환경에서의 레이아웃 테스트를 위해 스타일 속성을 추가하는 것이 좋습니다.
다음과 같이 수정하는 것을 제안드립니다:
export const Mobile: Story = { parameters: { viewport: { defaultViewport: 'mobile', }, }, render: () => ( <ul css={[storyContainer, { padding: '0 16px' }]}> <li css={storyInnerContainer}> <h3>모바일 톡&픽 플레이스</h3> <CategoryButton imageType="PickVote" label="톡&픽 플레이스" isMobile /> </li> <li css={storyInnerContainer}> <h3>모바일 오늘의 톡픽 모음</h3> + <CategoryButton imageType="TodayPick" label="오늘의 톡픽 모음.zip" isMobile /> </li> <li css={storyInnerContainer}> <h3>모바일 랜덤 밸런스 게임</h3> <CategoryButton imageType="RandomGame" label="랜덤 밸런스 게임" isMobile /> </li> </ul> ), };
src/components/molecules/CategoryBar/CategoryBar.tsx (1)
Line range hint
28-40
: getBadgeText 함수의 타입 안전성 개선이 필요합니다.getBadgeText 함수에서 다음과 같은 개선이 필요합니다:
- 파라미터 타입을 CategoryTabType을 사용하도록 변경
- 반환 타입을 명시적으로 지정
- const getBadgeText = (tab: '' | '커플' | '취향' | '월드컵') => { + const getBadgeText = (tab: CategoryTabType): string => {src/components/mobile/atoms/MobileToggleGroup/MobileToggleGroup.tsx (2)
51-72
: 접근성 개선이 필요합니다.토글 그룹의 접근성 향상을 위해 다음 사항들을 추가해주세요:
- ARIA 속성 추가
- 키보드 네비게이션 지원
return ( - <div css={S.toggleGroupStyle}> + <div css={S.toggleGroupStyle} role="group" aria-label="정렬 옵션"> <button css={S.clickedToggleStyle(isOpen)} type="button" + aria-expanded={isOpen} + aria-haspopup="true" onClick={handleMenuClick} + onKeyDown={(e) => { + if (e.key === 'Escape') setIsOpen(false); + }} >
10-19
: 상수 값의 분리가 필요합니다.defaultItems를 별도의 상수 파일로 분리하여 재사용성을 높이는 것이 좋습니다.
+ // src/constants/toggle.ts + export const TOGGLE_ITEMS: ToggleGroupItem[] = [ + { + label: '인기순', + value: { field: 'views', order: 'desc' }, + }, + { + label: '최신순', + value: { field: 'createdAt', order: 'desc' }, + }, + ]; - const defaultItems: ToggleGroupItem[] = [ - { - label: '인기순', - value: { field: 'views', order: 'desc' }, - }, - { - label: '최신순', - value: { field: 'createdAt', order: 'desc' }, - }, - ]; + import { TOGGLE_ITEMS } from '@/constants/toggle'; + const defaultItems = TOGGLE_ITEMS;src/components/molecules/ContentsButton/ContentsButton.style.ts (2)
30-36
: 크기 값을 상수로 분리하는 것이 좋겠습니다.하드코딩된 크기 값들을 상수로 분리하면 유지보수가 더 용이해질 것 같습니다.
다음과 같이 개선해보세요:
const SIZES = { EXTRA_SMALL: { WIDTH: '162px', HEIGHT: '121px', INFO_HEIGHT: '40px', LABEL_MAX_WIDTH: '141px', IMAGE_HEIGHT: '81px', }, // ... 다른 사이즈들도 동일하게 적용 } as const; const sizeStyles = { extraSmall: SIZES.EXTRA_SMALL, // ... 나머지 사이즈 };
68-69
: 모바일 반응형 스타일이 잘 구현되었습니다.미디어 쿼리를 통한 모바일 대응이 적절하게 구현되었습니다. 다만, 브레이크포인트 값(430px)을 상수로 분리하면 더 좋을 것 같습니다.
브레이크포인트를 다음과 같이 분리해보세요:
const BREAKPOINTS = { MOBILE: '430px', } as const; // 사용 예시 @media (max-width: ${BREAKPOINTS.MOBILE}) { // ... }Also applies to: 115-117, 124-126
src/components/atoms/BalanceGameCategoryButton/BalanceGameCategoryButton.style.ts (2)
15-17
: 미디어 쿼리 상수화 및 일관성 개선 필요미디어 쿼리에서 반복적으로 사용되는
430px
값과 여러 간격 값들을 상수로 분리하여 관리하면 유지보수성이 향상될 것 같습니다.다음과 같은 개선을 제안드립니다:
+ const BREAKPOINTS = { + MOBILE: '430px', + }; + + const SPACING = { + MOBILE: { + MARGIN_TOP: '2px', + MARGIN_BOTTOM: '2px', + MARGIN_LEFT: '2px', + }, + }; + export const buttonStyle = css({ // ... - '@media (max-width: 430px)': { + [`@media (max-width: ${BREAKPOINTS.MOBILE})`]: { width: '84px', }, });Also applies to: 26-28, 33-35, 41-43, 49-51, 60-62, 71-73
77-85
: border-radius 값의 일관성 확보 필요
inactiveLineStyle
함수에서 하드코딩된 border-radius 값들을 상수로 분리하면 스타일의 일관성을 더 쉽게 유지할 수 있을 것 같습니다.다음과 같은 개선을 제안드립니다:
+ const BORDER_RADIUS = { + DESKTOP: { + LEFT: '10px 0 0 10px', + RIGHT: '0 10px 10px 0', + }, + MOBILE: { + LEFT: '3px 0 0 3px', + RIGHT: '0 3px 3px 0', + }, + }; export const inactiveLineStyle = (label: string) => { let baseBorderRadius = '0'; let mobileBorderRadius = '0'; if (label === '인기') { - baseBorderRadius = '10px 0 0 10px'; - mobileBorderRadius = '3px 0 0 3px'; + baseBorderRadius = BORDER_RADIUS.DESKTOP.LEFT; + mobileBorderRadius = BORDER_RADIUS.MOBILE.LEFT; } else if (label === '월드컵') { - baseBorderRadius = '0 10px 10px 0'; - mobileBorderRadius = '0 3px 3px 0'; + baseBorderRadius = BORDER_RADIUS.DESKTOP.RIGHT; + mobileBorderRadius = BORDER_RADIUS.MOBILE.RIGHT; }Also applies to: 92-96
src/components/organisms/BalanceGameList/BalanceGameList.tsx (2)
20-24
: 타입 정의 개선 필요activeTab의 타입과 관련된 문자열 리터럴을 상수로 분리하여 재사용성과 유지보수성을 높일 수 있습니다.
다음과 같은 개선을 제안드립니다:
export const TAB_TYPES = { EMPTY: '', COUPLE: '커플', PREFERENCE: '취향', WORLDCUP: '월드컵', } as const; type TabType = typeof TAB_TYPES[keyof typeof TAB_TYPES]; export interface ContentListProps { // ... activeTab: TabType; setActiveTab: React.Dispatch<React.SetStateAction<TabType>>; // ... }
64-74
: 조건부 렌더링 로직 개선 필요삼항 연산자를 사용한 조건부 렌더링이 가독성을 떨어뜨립니다. 컴포넌트를 분리하거나 더 명확한 방식으로 처리하면 좋을 것 같습니다.
다음과 같은 개선을 제안드립니다:
const ToggleGroupComponent = { mobile: MobileToggleGroup, desktop: ToggleGroup, }; const Component = isMobile ? ToggleGroupComponent.mobile : ToggleGroupComponent.desktop; return ( <Component selectedValue={selectedValue} onClick={isMobile ? handleToggleChange : setSelectedValue} /> );src/pages/LandingPage/LandingPage.tsx (1)
62-80
: 모바일 뷰의 구조적 개선이 필요합니다.모바일 뷰에서 많은 props를 전달하고 있습니다. 컴포넌트의 책임을 줄이고 유지보수성을 높이기 위해 다음과 같은 개선을 제안합니다.
- <BalanceGameList - isMobile - contents={contents} - selectedValue={selectedValue} - setSelectedValue={setSelectedValue} - activeTab={activeTab} - setActiveTab={setActiveTab} - /> + <BalanceGameList + isMobile + contents={contents} + selectedValue={selectedValue} + onValueChange={setSelectedValue} + activeTab={activeTab} + onTabChange={setActiveTab} + />src/components/atoms/Button/Button.style.ts (1)
207-213
: circle 버튼의 medium 사이즈 스타일 개선이 필요합니다.SVG 아이콘의 크기가 너무 작아 보입니다. 가독성을 위해 크기를 약간 키우는 것을 제안합니다.
medium: css({ padding: '8.5px', '& svg': { - width: '10.5px', - height: '10.5px', + width: '12px', + height: '12px', }, }),src/components/organisms/Header/Header.tsx (1)
175-198
: 데스크톱 뷰의 중첩된 컨테이너를 단순화해주세요.불필요하게 중첩된
rightContainerStyle
이 있습니다. 구조를 단순화하여 가독성을 개선할 수 있습니다.- <div css={S.rightContainerStyle}> <button type="button" onClick={handleLoginButton} css={S.LoginButtonStyle} > {accessToken ? '로그아웃' : '로그인'} </button> <div css={S.notificationStyle}> {accessToken ? ( <ProfileIcon interaction="custom" imgUrl={member?.profileImgUrl ?? DefaultProfile} onClick={handleProfileIcon} /> ) : ( <ProfileIcon interaction="default" onClick={handleProfileIcon} /> )} </div> - </div>src/styles/typo.ts (2)
193-198
: 일관성 있는 스타일 속성을 유지하세요.
SemiBold_12
와Medium_12
에서letterSpacing
속성이 일관되지 않게 적용되어 있습니다. 또한 다른 스타일들과 달리lineHeight
가1.3
으로 설정되어 있습니다.SemiBold_12: { fontFamily: 'Pretendard', fontSize: '12px', fontWeight: 600, - lineHeight: '1.3', + lineHeight: '1.6', + letterSpacing: `${12 * -0.05}px`, },Also applies to: 218-223
Line range hint
232-235
: 기술 부채 해결 계획을 수립하세요.이전 타이포 타입에 대한 TODO 주석이 있습니다. 기술 부채를 해결하기 위한 이슈를 생성하고 계획을 수립하는 것이 좋습니다.
이전 타이포 타입 정리를 위한 GitHub 이슈를 생성하시겠습니까?
src/assets/index.ts (1)
Line range hint
104-127
: 이전 SVG 정리가 필요합니다.TODO 주석으로 표시된 이전 SVG 파일들의 정리가 필요합니다. 현재 사용 중인 컴포넌트들과의 연관성을 확인하고 정리하는 것이 좋습니다.
이전 SVG 정리를 위한 GitHub 이슈를 생성하시겠습니까?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (18)
package-lock.json
is excluded by!**/package-lock.json
src/assets/svg/bookmark-df-sm.svg
is excluded by!**/*.svg
src/assets/svg/bookmark-pr-sm.svg
is excluded by!**/*.svg
src/assets/svg/check-small.svg
is excluded by!**/*.svg
src/assets/svg/circle-close.svg
is excluded by!**/*.svg
src/assets/svg/circle-game.svg
is excluded by!**/*.svg
src/assets/svg/circle-pencil.svg
is excluded by!**/*.svg
src/assets/svg/circle-talkpick.svg
is excluded by!**/*.svg
src/assets/svg/couple-small.svg
is excluded by!**/*.svg
src/assets/svg/list.svg
is excluded by!**/*.svg
src/assets/svg/logo-small.svg
is excluded by!**/*.svg
src/assets/svg/pick-vote-small.svg
is excluded by!**/*.svg
src/assets/svg/popular-small.svg
is excluded by!**/*.svg
src/assets/svg/random-game-small.svg
is excluded by!**/*.svg
src/assets/svg/taste-small.svg
is excluded by!**/*.svg
src/assets/svg/triangle-down.svg
is excluded by!**/*.svg
src/assets/svg/triangle-up.svg
is excluded by!**/*.svg
src/assets/svg/worldcup-small.svg
is excluded by!**/*.svg
📒 Files selected for processing (53)
.storybook/main.ts
(1 hunks).storybook/preview.ts
(2 hunks)package.json
(1 hunks)public/index.html
(1 hunks)src/api/game.ts
(1 hunks)src/assets/index.ts
(1 hunks)src/components/atoms/BalanceGameCategoryButton/BalanceGameCategoryButton.style.ts
(5 hunks)src/components/atoms/Bookmark/Bookmark.style.ts
(1 hunks)src/components/atoms/Bookmark/Bookmark.tsx
(3 hunks)src/components/atoms/Button/Button.style.ts
(5 hunks)src/components/atoms/Button/Button.tsx
(1 hunks)src/components/atoms/CategoryButton/CategoryButton.style.ts
(1 hunks)src/components/atoms/CategoryButton/CategoryButton.tsx
(1 hunks)src/components/atoms/Chips/Chips.style.ts
(1 hunks)src/components/atoms/SearchBar/SearchBar.style.ts
(2 hunks)src/components/atoms/SearchBar/SearchBar.tsx
(1 hunks)src/components/mobile/atoms/DraftSaveButton/DraftSaveButton.style.ts
(1 hunks)src/components/mobile/atoms/DraftSaveButton/DraftSaveButton.tsx
(1 hunks)src/components/mobile/atoms/FloatingButton/FloatingButton.style.ts
(1 hunks)src/components/mobile/atoms/FloatingButton/FloatingButton.tsx
(1 hunks)src/components/mobile/atoms/MobileSideMenu/MobileSideMenu.style.ts
(1 hunks)src/components/mobile/atoms/MobileSideMenu/MobileSideMenu.tsx
(1 hunks)src/components/mobile/atoms/MobileToggleGroup/MobileToggleGroup.style.ts
(1 hunks)src/components/mobile/atoms/MobileToggleGroup/MobileToggleGroup.tsx
(1 hunks)src/components/mobile/molecules/FloatingMenuButton/FloatingMenuButton.style.ts
(1 hunks)src/components/mobile/molecules/FloatingMenuButton/FloatingMenuButton.tsx
(1 hunks)src/components/molecules/CategoryBar/CategoryBar.style.ts
(1 hunks)src/components/molecules/CategoryBar/CategoryBar.tsx
(2 hunks)src/components/molecules/CategoryBox/CategoryBox.style.ts
(1 hunks)src/components/molecules/CategoryBox/CategoryBox.tsx
(1 hunks)src/components/molecules/ContentsButton/ContentsButton.style.ts
(3 hunks)src/components/molecules/ContentsButton/ContentsButton.tsx
(1 hunks)src/components/molecules/SearchTagBar/SearchTagBar.tsx
(2 hunks)src/components/molecules/TopBanner/TodayTalkPickBanner/TodayTalkPickBanner.style.ts
(3 hunks)src/components/molecules/TopBanner/TodayTalkPickBanner/TodayTalkPickBanner.tsx
(3 hunks)src/components/molecules/TopBanner/TopBanner.style.ts
(2 hunks)src/components/organisms/BalanceGameList/BalanceGameList.style.ts
(1 hunks)src/components/organisms/BalanceGameList/BalanceGameList.tsx
(5 hunks)src/components/organisms/Header/Header.style.ts
(1 hunks)src/components/organisms/Header/Header.tsx
(4 hunks)src/hooks/common/useIsMobile.ts
(1 hunks)src/layout/layout.tsx
(2 hunks)src/pages/LandingPage/LandingPage.style.ts
(2 hunks)src/pages/LandingPage/LandingPage.tsx
(2 hunks)src/stories/atoms/Bookmark.stories.tsx
(3 hunks)src/stories/atoms/CategoryButton.stories.tsx
(2 hunks)src/stories/mobile/atoms/DraftSaveButton.stories.tsx
(1 hunks)src/stories/mobile/atoms/FloatingButton.stories.tsx
(1 hunks)src/stories/mobile/atoms/MobileSideMenu.stories.tsx
(1 hunks)src/stories/mobile/atoms/MobileToggleGroup.stories.tsx
(1 hunks)src/stories/mobile/molecules/FloatingMenuButton.stories.tsx
(1 hunks)src/stories/molecules/CategoryBar.stories.tsx
(2 hunks)src/styles/typo.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- public/index.html
- src/components/mobile/atoms/DraftSaveButton/DraftSaveButton.style.ts
- src/components/mobile/molecules/FloatingMenuButton/FloatingMenuButton.style.ts
🧰 Additional context used
🪛 eslint
src/api/game.ts
[error] 64-64: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
[error] 81-81: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
🔇 Additional comments (34)
src/components/molecules/CategoryBox/CategoryBox.style.ts (1)
5-11
: 반응형 디자인 구현이 잘 되었습니다!
모바일 환경을 고려한 스타일링이 적절하게 구현되었습니다:
- 중앙 정렬을 위한
alignItems
와justifyContent
속성 추가 - 모바일 화면에서의 간격 조정을 위한 미디어 쿼리 적용
- 컨테이너의 전체 너비 활용
src/components/molecules/CategoryBox/CategoryBox.tsx (1)
7-7
: Props 인터페이스 정의가 명확합니다.
모바일 대응을 위한 isMobile
옵션 프롭이 적절하게 추가되었습니다.
src/components/molecules/SearchTagBar/SearchTagBar.tsx (2)
9-9
: 인터페이스 정의가 적절합니다!
optional 플래그(?)를 사용하여 isMobile prop을 선택적으로 만든 것이 좋습니다. 이는 기존 코드와의 호환성을 유지하면서도 모바일 대응이 가능하게 합니다.
12-12
: 컴포넌트 시그니처가 잘 구성되었습니다!
isMobile의 기본값을 false로 설정한 것이 적절합니다. 이는 기존 데스크톱 동작을 기본값으로 유지하면서 모바일 지원을 추가하는 좋은 접근 방식입니다.
src/components/molecules/CategoryBar/CategoryBar.style.ts (1)
7-9
: 모바일 반응형 스타일링이 적절히 구현되었습니다!
미디어 쿼리를 통한 모바일 최적화가 잘 되어있으며, 430px 브레이크포인트는 모바일 디바이스에 적합합니다.
src/components/atoms/SearchBar/SearchBar.style.ts (2)
15-19
: 모바일 반응형 스타일링이 적절히 구현되었습니다.
미디어 쿼리를 통한 모바일 뷰포트 대응이 잘 되어있습니다. 430px 브레이크포인트는 일반적인 모바일 기기 너비에 적합합니다.
29-33
: 모바일 입력 필드 스타일링이 일관성있게 적용되었습니다.
typo.Mobile.Text.Medium_12
를 사용하여 모바일 타이포그래피 시스템을 잘 따르고 있습니다. 패딩과 높이 값이 모바일 환경에 적절합니다.
src/components/mobile/atoms/MobileSideMenu/MobileSideMenu.style.ts (1)
28-36
: 버튼 스타일링이 모바일 디자인 시스템을 잘 따르고 있습니다.
typo.Mobile.Text.Medium_12
를 사용한 타이포그래피와 터치 영역을 고려한 패딩 값이 적절합니다. 모바일 사용성을 잘 고려한 구현입니다.
src/stories/atoms/Bookmark.stories.tsx (1)
14-14
: prop 이름 변경이 일관성 있게 적용되었습니다!
bookmarkState
에서 bookmarked
로의 prop 이름 변경이 더 명확하고 의미론적으로 개선되었습니다. 모든 필요한 위치(argTypes, args, stories)에서 일관되게 변경되었습니다.
Also applies to: 17-17, 26-26, 35-35, 38-38
src/components/atoms/Chips/Chips.style.ts (1)
15-18
: 모바일 반응형 스타일이 적절하게 구현되었습니다.
타이포그래피와 패딩이 모바일 환경에 맞게 잘 조정되었습니다. 디자인 시스템의 typo.Mobile.Text
사용이 적절합니다.
src/stories/mobile/atoms/MobileSideMenu.stories.tsx (1)
8-27
: 컴포넌트 메타데이터 구성이 잘 되어있습니다.
스토리북 설정이 체계적으로 구성되어 있으며, 필요한 모든 props와 액션이 잘 정의되어 있습니다. BrowserRouter 데코레이터 사용으로 라우팅 기능도 적절히 테스트할 수 있습니다.
src/stories/mobile/molecules/FloatingMenuButton.stories.tsx (1)
26-26
: 타입 안전성이 잘 구현되어 있습니다.
satisfies
연산자를 사용하여 타입 안전성을 보장한 것이 좋습니다.
src/components/atoms/CategoryButton/CategoryButton.style.ts (2)
5-26
: 반응형 디자인 구현이 잘 되어있습니다.
모바일 환경을 고려한 스타일링이 적절히 구현되어 있으며, 디자인 시스템의 색상 상수(color.WT
)를 잘 활용하고 있습니다. 트랜지션 효과도 자연스럽게 적용되어 있습니다.
32-38
: 타이포그래피 시스템 활용이 훌륭합니다.
디자인 시스템의 타이포그래피를 스프레드 연산자로 확장하여 사용한 방식이 깔끔합니다. 모바일 환경에서의 폰트 크기 조정도 적절합니다.
src/components/mobile/atoms/MobileToggleGroup/MobileToggleGroup.style.ts (1)
5-8
: 토글 그룹 스타일 구현이 적절합니다.
flex 컨테이너를 사용하여 수직 방향으로 토글 아이템을 정렬하는 구현이 깔끔합니다.
src/components/mobile/atoms/FloatingButton/FloatingButton.tsx (1)
5-9
: Props 인터페이스 정의가 명확합니다.
ComponentPropsWithRef를 확장하여 버튼의 기본 속성들을 모두 지원하면서도, 필요한 커스텀 속성들을 추가한 것이 좋습니다.
src/components/atoms/CategoryButton/CategoryButton.tsx (1)
3-9
: 인터페이스 변경 사항이 적절해 보입니다.
모바일 대응을 위한 이미지 컴포넌트 추가와 isMobile
프로퍼티 추가가 잘 되어있습니다. 선택적 프로퍼티(?
)로 설정한 것도 적절합니다.
Also applies to: 15-15
src/components/atoms/Bookmark/Bookmark.tsx (1)
2-8
: 모바일 아이콘 임포트가 잘 구성되어 있습니다.
모바일용 북마크 아이콘 컴포넌트들이 깔끔하게 구성되어 있습니다.
src/components/organisms/BalanceGameList/BalanceGameList.style.ts (1)
15-25
: 모바일 타이포그래피 스타일이 적절하게 적용되었습니다.
typo.Mobile.Text.Bold_16
을 사용하여 모바일 환경에 맞는 타이포그래피를 적용한 것이 좋습니다.
src/components/organisms/Header/Header.style.ts (1)
44-51
: 모바일 레이아웃 대응이 잘 구현되었습니다.
rightContainerStyle
에서 모바일 환경일 때 flex: 1
과 justify-content: flex-end
를 사용하여 레이아웃을 적절하게 조정한 것이 좋습니다.
src/stories/mobile/atoms/FloatingButton.stories.tsx (1)
8-21
: 스토리 구성이 잘 되어있습니다!
메타 설정과 데코레이터 구성이 적절하며, 라우팅 컨텍스트를 위한 BrowserRouter
래퍼 사용이 좋습니다.
src/components/molecules/TopBanner/TopBanner.style.ts (1)
38-38
: CSS 리셋 속성 사용이 적절합니다
all: 'unset'
과 display: 'inline-block'
을 함께 사용하여 스타일 초기화와 레이아웃 동작을 명확하게 정의한 것이 좋습니다.
Also applies to: 45-45
src/components/atoms/SearchBar/SearchBar.tsx (1)
13-13
: 타입 안전성이 잘 구현되어 있습니다
isMobile
prop의 타입 정의와 기본값 설정이 명확하게 되어있어 좋습니다.
Also applies to: 17-17
src/layout/layout.tsx (2)
6-6
: 모바일 대응을 위한 Footer 조건부 렌더링이 적절히 구현되었습니다.
useIsMobile 훅을 사용하여 모바일 환경에서 Footer를 숨기는 방식이 깔끔하게 구현되었습니다.
Also applies to: 10-10, 24-24
17-19
: 모바일 뷰포트에서의 패딩 조정이 일관되게 적용되었습니다.
Layout과 LayoutNoSearch 컴포넌트 모두에서 모바일 환경(430px 이하)에 대한 패딩 조정이 동일하게 적용되어 일관성이 잘 유지되었습니다.
Also applies to: 40-42
src/components/molecules/ContentsButton/ContentsButton.tsx (1)
14-14
: size 타입에 'extraSmall' 옵션이 추가된 것을 확인해주세요.
스타일 파일에서 'extraSmall' 사이즈에 대한 스타일이 적절히 정의되어 있는지 확인이 필요합니다.
✅ Verification successful
'extraSmall' 사이즈에 대한 스타일이 올바르게 정의되어 있음을 확인했습니다.
스타일 파일에서 'extraSmall' 사이즈에 대해 다음과 같은 스타일 속성들이 명확하게 정의되어 있습니다:
- width: 162px
- height: 121px
- infoHeight: 40px
- labelMaxWidth: 141px
- imageHeight: 81px
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# ContentsButton.style.ts 파일에서 extraSmall 스타일 정의 확인
rg "extraSmall" -A 5 "src/components/molecules/ContentsButton/ContentsButton.style.ts"
Length of output: 218
.storybook/main.ts (1)
14-14
: Storybook viewport 애드온 추가가 적절합니다.
모바일 반응형 디자인 테스트를 위한 @storybook/addon-viewport 추가는 적절한 변경사항입니다. 다만, preview 파일에서 적절한 설정이 되어있는지 확인이 필요합니다.
✅ Verification successful
Viewport 애드온이 올바르게 구성되어 있습니다
preview.ts 파일에서 viewport 설정이 적절하게 구현되어 있습니다:
- INITIAL_VIEWPORTS가 정상적으로 임포트되어 있음
- 커스텀 모바일 뷰포트(430px)가 추가되어 있음
- parameters에서 viewport 설정이 올바르게 구성됨
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# preview 파일에서 viewport 설정 확인
rg "viewport" ".storybook/preview.ts"
Length of output: 134
Script:
#!/bin/bash
# preview 파일의 viewport 설정 전체 내용 확인
rg -A 10 "viewport: {" ".storybook/preview.ts"
Length of output: 132
Script:
#!/bin/bash
# preview 파일의 전체 내용 확인
cat ".storybook/preview.ts"
Length of output: 627
src/stories/atoms/CategoryButton.stories.tsx (1)
11-11
: 모바일 지원을 위한 설정이 적절히 추가되었습니다.
스토리북에 모바일 지원을 위한 viewport 설정과 isMobile 속성이 잘 구현되어 있습니다.
Also applies to: 19-20, 24-25
src/api/game.ts (1)
72-76
: API 파라미터 처리가 개선되었습니다.
고정 파라미터와 조건부 파라미터를 분리하여 처리하는 방식이 깔끔합니다. 특히 인기 게임 조회 시 불필요한 tagName 파라미터를 제외하는 로직이 잘 구현되었습니다.
Also applies to: 89-93
src/components/mobile/molecules/FloatingMenuButton/FloatingMenuButton.tsx (1)
13-77
: 컴포넌트 구현이 잘 되었습니다.
- 인증 상태에 따른 네비게이션 처리가 적절합니다.
- 이벤트 리스너의 정리(cleanup)가 잘 구현되었습니다.
- 상태 관리와 이벤트 처리가 깔끔합니다.
src/pages/LandingPage/LandingPage.tsx (1)
25-27
: activeTab 상태 타입 변경이 적절합니다.
빈 문자열을 허용하도록 상태 타입을 변경한 것은 PR 목표와 일치하며, 인기 게임 조회 시 태그 파라미터를 제외하는 요구사항을 잘 반영하고 있습니다.
src/components/atoms/Button/Button.style.ts (1)
173-181
: outlineShadow 버튼의 small 사이즈 스타일이 적절합니다.
모바일 환경을 고려한 작은 크기의 버튼 스타일이 잘 정의되어 있습니다. SVG 아이콘의 크기도 적절하게 조정되었습니다.
src/components/organisms/Header/Header.tsx (1)
35-37
: 메뉴 토글 함수가 간단명료합니다.
상태 업데이트 로직이 함수형 업데이트를 사용하여 안전하게 구현되었습니다.
src/assets/index.ts (1)
86-102
: 모바일 대응을 위한 에셋 추가가 잘 되었습니다.
새로운 모바일용 아이콘들이 일관된 명명 규칙을 따라 잘 추가되었습니다.
const mobile = window.matchMedia('(max-width: 430px)').matches; | ||
setIsMobile( | ||
mobile || ('ontouchstart' in window && window.innerWidth <= 430), | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
모바일 기기 감지 로직 개선 필요
현재 구현은 다음과 같은 잠재적 문제가 있습니다:
- 430px의 고정된 브레이크포인트는 유지보수성이 떨어집니다
- 터치 기능이 있는 노트북에서 잘못된 판단을 할 수 있습니다
다음과 같이 개선하는 것을 추천드립니다:
+const MOBILE_BREAKPOINT = 430; // 상수로 분리
+
const checkIsMobile = () => {
- const mobile = window.matchMedia('(max-width: 430px)').matches;
+ const mobile = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`).matches;
setIsMobile(
- mobile || ('ontouchstart' in window && window.innerWidth <= 430),
+ mobile || (window.navigator.maxTouchPoints > 0 && window.innerWidth <= MOBILE_BREAKPOINT),
);
};
📝 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 mobile = window.matchMedia('(max-width: 430px)').matches; | |
setIsMobile( | |
mobile || ('ontouchstart' in window && window.innerWidth <= 430), | |
); | |
const MOBILE_BREAKPOINT = 430; // 상수로 분리 | |
const mobile = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`).matches; | |
setIsMobile( | |
mobile || (window.navigator.maxTouchPoints > 0 && window.innerWidth <= MOBILE_BREAKPOINT), | |
); |
const customViewports = { | ||
mobile: { | ||
name: 'mobile', | ||
styles: { | ||
width: '430px', | ||
height: '400px', | ||
}, | ||
type: 'mobile', | ||
}, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
모바일 뷰포트 설정 개선 필요
현재 구성은 단일 모바일 해상도만 지원하고 있습니다. 다양한 모바일 기기를 지원하기 위해 추가 뷰포트 설정이 필요합니다.
다음과 같이 개선하는 것을 추천드립니다:
const customViewports = {
mobile: {
name: 'mobile',
styles: {
width: '430px',
- height: '400px',
+ height: '932px',
},
type: 'mobile',
},
+ mobileSM: {
+ name: 'mobile-small',
+ styles: {
+ width: '360px',
+ height: '780px',
+ },
+ type: 'mobile',
+ },
+ mobileXL: {
+ name: 'mobile-large',
+ styles: {
+ width: '480px',
+ height: '1040px',
+ },
+ type: 'mobile',
+ },
};
📝 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 customViewports = { | |
mobile: { | |
name: 'mobile', | |
styles: { | |
width: '430px', | |
height: '400px', | |
}, | |
type: 'mobile', | |
}, | |
}; | |
const customViewports = { | |
mobile: { | |
name: 'mobile', | |
styles: { | |
width: '430px', | |
height: '932px', | |
}, | |
type: 'mobile', | |
}, | |
mobileSM: { | |
name: 'mobile-small', | |
styles: { | |
width: '360px', | |
height: '780px', | |
}, | |
type: 'mobile', | |
}, | |
mobileXL: { | |
name: 'mobile-large', | |
styles: { | |
width: '480px', | |
height: '1040px', | |
}, | |
type: 'mobile', | |
}, | |
}; |
/* eslint-disable no-console */ | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
ESLint 비활성화 주석을 제거하는 것이 좋습니다.
ESLint 규칙을 비활성화하는 대신 코드를 수정하여 린트 규칙을 준수하는 것이 좋습니다. 특히 no-console
과 @typescript-eslint/no-unused-vars
규칙은 중요한 코드 품질 지표입니다.
-/* eslint-disable no-console */
-/* eslint-disable @typescript-eslint/no-unused-vars */
export const buttonGroupStyle = css({ | ||
position: 'fixed', | ||
top: '55px', | ||
right: 0, | ||
width: '134px', | ||
height: '100%', | ||
backgroundColor: color.WT, | ||
boxShadow: '0px 0 5px rgba(0, 0, 0, 0.1)', | ||
zIndex: 1100, | ||
display: 'flex', | ||
flexDirection: 'column', | ||
justifyContent: 'space-between', | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
z-index 값의 일관성 있는 관리가 필요합니다
현재 코드베이스에서 z-index 값들이 다음과 같이 산발적으로 사용되고 있습니다:
- Header: 1000
- MobileSideMenu: 1100
- CommentsSection: 1, 10
- ImageBoxButton: 1, 2
z-index 값의 체계적인 관리를 위해 다음 작업이 필요합니다:
- constants 파일에 z-index 값을 상수로 정의
- 컴포넌트 계층 구조에 따른 z-index 값 체계 수립
- 모바일 메뉴가 Header보다 높은 z-index를 가져야 하는 이유 문서화
iOS Safari에서의 fixed positioning 관련 스크롤 이슈는 QA 단계에서 반드시 확인이 필요합니다.
🔗 Analysis chain
z-index 값에 대한 문서화가 필요합니다.
z-index: 1100
의 값이 임의로 설정된 것으로 보입니다. z-index 값의 체계와 의미를 문서화하고, 전역 상수로 관리하는 것이 좋습니다.
또한 fixed positioning을 사용할 때 iOS Safari에서 스크롤 이슈가 발생할 수 있으니 테스트가 필요합니다.
Let me search for more z-index values to get a complete picture.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# z-index 상수 정의 여부 확인
rg -g '*.{ts,tsx}' 'z-index.*[0-9]{4}'
Length of output: 105
Script:
#!/bin/bash
# Search for all z-index values in the codebase
rg -g '*.{ts,tsx}' 'z-index:' -A 1
Length of output: 752
'@media (max-width: 430px)': { | ||
width: '164px', | ||
height: '70px', | ||
padding: '5px 50px 14px', | ||
borderRadius: '7px', | ||
border: '0.35px solid #dedede', | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
미디어 쿼리 브레이크포인트 상수화 필요
미디어 쿼리의 브레이크포인트(430px)가 하드코딩되어 있습니다. 이를 중앙 관리되는 상수로 분리하여 일관성을 유지하는 것이 좋습니다.
// src/styles/breakpoints.ts
export const BREAKPOINTS = {
MOBILE: '430px',
TABLET: '768px',
// ... 기타 브레이크포인트
} as const;
// 사용 예시
'@media (max-width: ${BREAKPOINTS.MOBILE})': {
// ... 스타일
}
@@ -46,6 +46,7 @@ | |||
"@storybook/addon-interactions": "^8.1.9", | |||
"@storybook/addon-links": "^8.1.9", | |||
"@storybook/addon-onboarding": "^8.1.9", | |||
"@storybook/addon-viewport": "^8.4.7", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Storybook 의존성 버전 불일치
@storybook/addon-viewport
의 버전(^8.4.7)이 다른 Storybook 의존성들의 버전(^8.1.9)과 일치하지 않습니다. 호환성 문제를 방지하기 위해 버전을 일치시키는 것이 좋습니다.
다음과 같이 수정을 제안드립니다:
- "@storybook/addon-viewport": "^8.4.7",
+ "@storybook/addon-viewport": "^8.1.9",
📝 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.
"@storybook/addon-viewport": "^8.4.7", | |
"@storybook/addon-viewport": "^8.1.9", |
/* eslint-disable no-alert */ | ||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
ESLint 비활성화 주석 범위 축소 필요
파일 전체에 대한 ESLint 규칙 비활성화보다는 특정 라인에만 적용하는 것이 더 안전합니다.
다음과 같이 수정을 제안드립니다:
- /* eslint-disable no-alert */
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
그리고 필요한 라인에만 규칙을 비활성화:
// alert 사용하는 라인에만 적용
// eslint-disable-next-line no-alert
alert(ERROR.GAME.NOT_EXIST);
<div css={S.contentWrapStyle}> | ||
<SearchTagBar onSearch={handleSearch} /> | ||
<div css={S.categoryBoxStyle}> | ||
<CategoryBox handleService={handleService} /> | ||
</div> | ||
<BalanceGameList | ||
contents={contents} | ||
selectedValue={selectedValue} | ||
setSelectedValue={setSelectedValue} | ||
activeTab={activeTab} | ||
setActiveTab={setActiveTab} | ||
/> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
데스크톱 뷰와 모바일 뷰의 중복 코드를 제거해주세요.
BalanceGameList
컴포넌트에 전달되는 props가 중복되어 있습니다. 공통 props를 추출하여 코드 중복을 줄이는 것이 좋겠습니다.
+ const commonBalanceGameProps = {
+ contents,
+ selectedValue,
+ setSelectedValue,
+ activeTab,
+ setActiveTab,
+ };
{isMobile ? (
<BalanceGameList
isMobile
+ {...commonBalanceGameProps}
- contents={contents}
- selectedValue={selectedValue}
- setSelectedValue={setSelectedValue}
- activeTab={activeTab}
- setActiveTab={setActiveTab}
/>
) : (
<BalanceGameList
+ {...commonBalanceGameProps}
- contents={contents}
- selectedValue={selectedValue}
- setSelectedValue={setSelectedValue}
- activeTab={activeTab}
- setActiveTab={setActiveTab}
/>
)}
Committable suggestion skipped: line range outside the PR's diff.
{isMobile ? ( | ||
<> | ||
<button | ||
type="button" | ||
aria-label="headerList" | ||
onClick={handleMenuToggle} | ||
css={S.listButtonStyle} | ||
> | ||
<ListIcon /> | ||
</button> | ||
{isMenuOpen && ( | ||
<MobileSideMenu | ||
isOpen={isMenuOpen} | ||
setIsOpen={setIsMenuOpen} | ||
accessToken={accessToken} | ||
handleLoginButton={handleLoginButton} | ||
/> | ||
) : ( | ||
<ProfileIcon interaction="default" onClick={handleProfileIcon} /> | ||
)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
모바일 메뉴 접근성 개선이 필요합니다.
모바일 메뉴 버튼과 사이드 메뉴의 접근성을 개선하기 위해 다음 사항들을 제안합니다:
<button
type="button"
aria-label="headerList"
+ aria-expanded={isMenuOpen}
+ aria-controls="mobile-side-menu"
onClick={handleMenuToggle}
css={S.listButtonStyle}
>
<ListIcon />
</button>
{isMenuOpen && (
<MobileSideMenu
+ id="mobile-side-menu"
+ role="dialog"
+ aria-modal="true"
isOpen={isMenuOpen}
setIsOpen={setIsMenuOpen}
accessToken={accessToken}
handleLoginButton={handleLoginButton}
/>
)}
📝 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.
{isMobile ? ( | |
<> | |
<button | |
type="button" | |
aria-label="headerList" | |
onClick={handleMenuToggle} | |
css={S.listButtonStyle} | |
> | |
<ListIcon /> | |
</button> | |
{isMenuOpen && ( | |
<MobileSideMenu | |
isOpen={isMenuOpen} | |
setIsOpen={setIsMenuOpen} | |
accessToken={accessToken} | |
handleLoginButton={handleLoginButton} | |
/> | |
) : ( | |
<ProfileIcon interaction="default" onClick={handleProfileIcon} /> | |
)} | |
{isMobile ? ( | |
<> | |
<button | |
type="button" | |
aria-label="headerList" | |
aria-expanded={isMenuOpen} | |
aria-controls="mobile-side-menu" | |
onClick={handleMenuToggle} | |
css={S.listButtonStyle} | |
> | |
<ListIcon /> | |
</button> | |
{isMenuOpen && ( | |
<MobileSideMenu | |
id="mobile-side-menu" | |
role="dialog" | |
aria-modal="true" | |
isOpen={isMenuOpen} | |
setIsOpen={setIsMenuOpen} | |
accessToken={accessToken} | |
handleLoginButton={handleLoginButton} | |
/> | |
)} |
Medium_16: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '16px', | ||
lineHeight: '1.6', | ||
letterSpacing: `${16 * -0.05}px`, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
중복된 스타일 정의를 통합하세요.
Mobile.Main.Medium_16
와 Mobile.Text.Medium_16
이 동일한 스타일을 정의하고 있습니다. 코드 중복을 피하기 위해 하나의 정의로 통합하는 것이 좋습니다.
Mobile: {
Main: {
- Medium_16: {
- fontFamily: 'Pretendard',
- fontSize: '16px',
- lineHeight: '1.6',
- letterSpacing: `${16 * -0.05}px`,
- },
// ... other styles
},
Text: {
// ... other styles
Medium_16: {
fontFamily: 'Pretendard',
fontSize: '16px',
+ fontWeight: 500,
lineHeight: '1.6',
letterSpacing: `${16 * -0.05}px`,
},
},
}
Also applies to: 212-217
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/pages/LandingPage/LandingPage.tsx
(2 hunks)
🧰 Additional context used
🪛 eslint
src/pages/LandingPage/LandingPage.tsx
[error] 31-31: React Hook "useBestGameList" is called conditionally. React Hooks must be called in the exact same order in every component render.
(react-hooks/rules-of-hooks)
[error] 36-36: React Hook "useLatestGameList" is called conditionally. React Hooks must be called in the exact same order in every component render.
(react-hooks/rules-of-hooks)
🔇 Additional comments (3)
src/pages/LandingPage/LandingPage.tsx (3)
13-14
: 모바일 대응을 위한 import 추가가 적절합니다.
모바일 기능 구현을 위한 컴포넌트와 훅의 추가가 잘 되어있습니다.
Also applies to: 18-18
64-97
: 모바일/데스크톱 뷰의 중복 코드가 있습니다.
이전 리뷰에서 지적된 BalanceGameList
컴포넌트의 props 중복 문제가 아직 해결되지 않았습니다.
25-27
: activeTab 상태 변경이 요구사항과 일치합니다.
빈 문자열을 포함하도록 타입을 변경한 것이 인기 게임 조회 시 태그 파라미터를 제외하는 요구사항과 잘 부합합니다.
다음 스크립트로 관련 컴포넌트들의 타입 정의를 확인해보세요:
✅ Verification successful
activeTab 타입 변경이 일관되게 적용되었습니다.
검색 결과를 통해 다음과 같이 확인되었습니다:
- CategoryBar 컴포넌트 (
src/components/molecules/CategoryBar/CategoryBar.tsx
) - BalanceGameList 컴포넌트 (
src/components/organisms/BalanceGameList/BalanceGameList.tsx
) - LandingPage 컴포넌트 (
src/pages/LandingPage/LandingPage.tsx
)
모든 관련 컴포넌트에서 activeTab 타입이 '' | '커플' | '취향' | '월드컵'
으로 일관되게 정의되어 있어 타입 변경이 올바르게 적용되었습니다.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# activeTab 타입이 일관되게 적용되었는지 확인
ast-grep --pattern 'type $_ = {
$$$
activeTab: $_;
$$$
}'
Length of output: 65
Script:
#!/bin/bash
# activeTab 관련 타입과 사용처를 검색
rg "activeTab.*['\"]커플['\"].*['\"]취향['\"].*['\"]월드컵['\"]" -A 2 -B 2
# activeTab 상태 관리 코드 검색
ast-grep --pattern 'const [$_, $_] = useState<$_>($$$)'
# 관련 Props 타입 정의 검색
rg "interface.*Props" -A 5 -B 1
Length of output: 72387
const { bestGames } = | ||
activeTab === '' || selectedValue.field === 'views' | ||
? useBestGameList(activeTab) | ||
: { bestGames: undefined }; | ||
|
||
const { bestGames } = useBestGameList(activeTab); | ||
const { latestGames } = useLatestGameList(activeTab); | ||
const { latestGames } = | ||
activeTab !== '' && selectedValue.field !== 'views' | ||
? useLatestGameList(activeTab) | ||
: { latestGames: undefined }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hook 호출 규칙 위반을 수정해주세요.
현재 구현은 React의 Hook 규칙을 위반하고 있습니다. Hook은 조건부로 호출될 수 없습니다.
다음과 같이 수정을 제안드립니다:
- const { bestGames } =
- activeTab === '' || selectedValue.field === 'views'
- ? useBestGameList(activeTab)
- : { bestGames: undefined };
- const { latestGames } =
- activeTab !== '' && selectedValue.field !== 'views'
- ? useLatestGameList(activeTab)
- : { latestGames: undefined };
+ const { bestGames } = useBestGameList(activeTab);
+ const { latestGames } = useLatestGameList(activeTab);
+
+ const filteredBestGames = (activeTab === '' || selectedValue.field === 'views')
+ ? bestGames
+ : undefined;
+
+ const filteredLatestGames = (activeTab !== '' && selectedValue.field !== 'views')
+ ? latestGames
+ : undefined;
+
- const contents = bestGames || latestGames || [];
+ const contents = filteredBestGames || filteredLatestGames || [];
📝 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 { bestGames } = | |
activeTab === '' || selectedValue.field === 'views' | |
? useBestGameList(activeTab) | |
: { bestGames: undefined }; | |
const { bestGames } = useBestGameList(activeTab); | |
const { latestGames } = useLatestGameList(activeTab); | |
const { latestGames } = | |
activeTab !== '' && selectedValue.field !== 'views' | |
? useLatestGameList(activeTab) | |
: { latestGames: undefined }; | |
const { bestGames } = useBestGameList(activeTab); | |
const { latestGames } = useLatestGameList(activeTab); | |
const filteredBestGames = (activeTab === '' || selectedValue.field === 'views') | |
? bestGames | |
: undefined; | |
const filteredLatestGames = (activeTab !== '' && selectedValue.field !== 'views') | |
? latestGames | |
: undefined; | |
const contents = filteredBestGames || filteredLatestGames || []; |
🧰 Tools
🪛 eslint
[error] 31-31: React Hook "useBestGameList" is called conditionally. React Hooks must be called in the exact same order in every component render.
(react-hooks/rules-of-hooks)
[error] 36-36: React Hook "useLatestGameList" is called conditionally. React Hooks must be called in the exact same order in every component render.
(react-hooks/rules-of-hooks)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
밸런스 게임 조회 api 수정 수고 많으셨습니다!!! 🙌🙌
|
||
export interface CategoryBarProps { | ||
activeTab: '인기' | '커플' | '취향' | '월드컵'; | ||
activeTab: '' | '커플' | '취향' | '월드컵'; | ||
setActiveTab: React.Dispatch< | ||
React.SetStateAction<'인기' | '커플' | '취향' | '월드컵'> | ||
React.SetStateAction<'' | '커플' | '취향' | '월드컵'> | ||
>; | ||
isMobile?: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
activeTab에 빈 문자열을 넣는 것 보단 '인기' 문자열을 넣고 tagName을 받는 함수에서 tagName이 인기가 아닐 경우에만 params에 tagName이 추가되도록 하는 건 어떨까요??? 4가지의 탭을 제어하는 컴포넌트이다보니 빈 문자열보단 각 태그의 이름이 들어가야 한다고 생각해요!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오오!! 좋은 생각인거 같습니다🤭 확실히 코드적으로도 직관적인거 같아요👏
const { bestGames } = | ||
activeTab === '' || selectedValue.field === 'views' | ||
? useBestGameList(activeTab) | ||
: { bestGames: undefined }; | ||
|
||
const { bestGames } = useBestGameList(activeTab); | ||
const { latestGames } = useLatestGameList(activeTab); | ||
const { latestGames } = | ||
activeTab !== '' && selectedValue.field !== 'views' | ||
? useLatestGameList(activeTab) | ||
: { latestGames: undefined }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 훅을 조건부로 호출하는 방식 대신에 react-query 훅 내부에 enabled 옵션을 통해 특정 조건을 만족할 경우 수행하도록 수행하는 것도 좋아보입니다 :)
💡 작업 내용
💡 자세한 설명
✅ getBestGames, getLatestGames
📗 참고 자료 (선택)
📢 리뷰 요구 사항 (선택)
🚩 후속 작업 (선택)
✅ 셀프 체크리스트
closes #263
Summary by CodeRabbit
신규 기능
@storybook/addon-viewport
추가.FloatingButton
,DraftSaveButton
,MobileSideMenu
,MobileToggleGroup
와 같은 새로운 모바일 컴포넌트 추가.스타일 변경
LandingPage
및TodayTalkPickBanner
와 같은 컴포넌트의 모바일 반응형 스타일 수정.문서화
Bookmark
,CategoryButton
,MobileSideMenu
,FloatingButton
의 스토리 업데이트, 모바일 뷰에 대한 새로운 스토리 추가.