-
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
feat: 메인 페이지 모바일 UI 구현 #255
base: dev
Are you sure you want to change the base?
Conversation
Walkthrough이 변경 사항은 다양한 컴포넌트와 스타일 파일에서 모바일 UI를 개선하기 위한 여러 가지 수정 및 추가를 포함합니다. 주요 변경 사항으로는 Changes
Assessment against linked issues
Suggested labels
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: 24
🧹 Outside diff range and nitpick comments (39)
src/pages/LandingPage/LandingPage.style.ts (1)
12-14
: 반응형 전환 효과 추가 검토패딩값이 데스크톱에서 모바일로 전환될 때 급격하게 변경됩니다. 부드러운 사용자 경험을 위해 transition 속성 추가를 고려해보세요.
export const contentWrapStyle = css({ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '100%', background: color.WT, padding: '113px 388px 95px 374px', + transition: 'padding 0.3s ease-in-out', '@media (max-width: 430px)': { padding: '18px 20px', }, });
src/components/organisms/Header/Header.style.ts (2)
5-22
: 브레이크포인트 표준화 및 전환 개선 필요현재 구현된 미디어 쿼리에 대해 다음 사항들을 고려해주세요:
- 430px의 브레이크포인트가 매우 구체적입니다. 재사용 가능한 상수로 정의하는 것이 좋습니다.
- 패딩이 200px에서 20px로 급격하게 변경되어 레이아웃 시프트가 발생할 수 있습니다.
다음과 같이 개선해보세요:
+import { BREAKPOINTS } from '@/styles/breakpoints'; export const containerStyle = css` width: 100%; padding: 0 200px; height: 100px; - @media (max-width: 430px) { + @media (max-width: ${BREAKPOINTS.MOBILE}) { padding: 0 20px; height: 55px; }
24-31
: 로고 스타일 구현이 적절합니다Flex를 활용한 모바일 환경에서의 로고 중앙 정렬이 잘 구현되었습니다. 다만, 앞서 언급된 브레이크포인트 표준화가 여기에도 적용되면 좋겠습니다.
src/components/organisms/Header/Header.tsx (3)
Line range hint
6-24
: 주석 처리된 코드 정리가 필요합니다.사용하지 않는 주석 처리된 코드(Button, Notification 등)가 남아있습니다. 코드의 가독성과 유지보수를 위해 불필요한 주석은 제거하는 것이 좋습니다.
91-97
: 로그인/로그아웃 처리 시 에러 핸들링이 필요합니다.현재 로그인/로그아웃 처리 시 발생할 수 있는 에러에 대한 처리가 없습니다. 사용자 경험 향상을 위해 에러 처리를 추가하는 것이 좋습니다.
다음과 같은 개선을 제안드립니다:
const handleLoginButton = () => { if (accessToken) { - logout.mutate(); + try { + logout.mutate(); + } catch (error) { + console.error('로그아웃 중 오류가 발생했습니다:', error); + // 사용자에게 에러 메시지 표시 + } setIsMenuOpen(false); } else { navigate('/login'); setIsMenuOpen(false); } };
151-201
: 접근성과 구조적 개선이 필요한 부분이 있습니다.
- 모바일 메뉴 토글 버튼에 대한 접근성이 개선되었지만, 추가적인 ARIA 속성이 도움될 수 있습니다.
- 중첩된 div 구조가 복잡합니다.
다음과 같은 개선을 제안드립니다:
- <button - type="button" - aria-label="headerList" - onClick={handleMenuToggle} - css={S.listButtonStyle} - > + <button + type="button" + aria-label="메뉴 열기" + aria-expanded={isMenuOpen} + aria-controls="mobile-menu" + onClick={handleMenuToggle} + css={S.listButtonStyle} + > <ListIcon /> </button> {isMenuOpen && ( <MobileSideMenu + id="mobile-menu" isOpen={isMenuOpen} setIsOpen={setIsMenuOpen} accessToken={accessToken} handleLoginButton={handleLoginButton} /> )}src/pages/LandingPage/LandingPage.tsx (1)
70-103
: 중복된 코드를 리팩토링하여 유지보수성을 향상시킬 수 있습니다.모바일과 데스크톱의 렌더링 부분에서 코드 중복이 발생하고 있습니다. 공통된 부분을 별도의 컴포넌트나 함수로 분리하면 코드의 가독성과 유지보수성이 향상될 수 있습니다.
src/hooks/common/useIsMobile.ts (2)
14-16
: 성능 최적화 및 상수 정의 필요매직 넘버를 피하고 성능을 개선하기 위한 수정이 필요합니다.
+const MOBILE_MAX_WIDTH = 768; + function useIsMobile() { const [isMobile, setIsMobile] = useState<boolean>(false); + const mobileMediaQuery = useMemo( + () => window.matchMedia(`(max-width: ${MOBILE_MAX_WIDTH}px)`), + [] + );
19-21
: 이벤트 리스너 최적화 필요리사이즈 이벤트에 디바운스 처리가 필요합니다.
+import { debounce } from 'lodash'; + +const debouncedCheckMobile = debounce(checkIsMobile, 250); - window.addEventListener('resize', checkIsMobile); + window.addEventListener('resize', debouncedCheckMobile); - return () => window.removeEventListener('resize', checkIsMobile); + return () => { + window.removeEventListener('resize', debouncedCheckMobile); + debouncedCheckMobile.cancel(); + };src/components/atoms/SearchBar/SearchBar.style.ts (1)
29-33
: 접근성 및 일관성 개선 필요모바일 입력 필드의 높이가 터치 타겟 최소 크기에 미달하며, 패딩값이 일관적이지 않습니다.
export const mobileInputStyling = css(typo.Mobile.Text.Medium_12, { width: '100%', - height: '36px', + height: MOBILE_MIN_TOUCH_TARGET, - padding: '0 5px', + padding: '0 10px', });src/components/mobile/MobileCreateDropdown/MobileCreateDropdown.style.ts (2)
2-2
: 사용하지 않는 import 제거 필요주석 처리된 import문은 제거하는 것이 좋습니다.
-// import color from '@/styles/color';
32-40
: 터치 영역 및 위치 조정 필요버튼의 터치 영역이 작고, 절대 위치 지정으로 인한 레이아웃 깨짐이 발생할 수 있습니다.
+const MOBILE_TOUCH_TARGET = '60px'; export const dropdownButtonStyling = css({ cursor: 'pointer', - zIndex: 20, + zIndex: Z_INDEX.dropdown, - width: '50px', - height: '50px', + width: MOBILE_TOUCH_TARGET, + height: MOBILE_TOUCH_TARGET, position: 'absolute', bottom: '10px', right: '20px', + '@media (max-width: 430px)': { + bottom: '20px', + right: '20px', + }, });src/components/mobile/MobileSideMenu/MobileSideMenu.style.ts (1)
28-36
: 접근성 개선을 위한 제안모바일 환경의 사용성 향상을 위해 다음 사항들을 고려해주세요:
- 버튼의 패딩(10.5px)이 터치 영역으로는 다소 작을 수 있습니다.
- 터치 피드백을 위한 active 상태 스타일 추가가 필요합니다.
export const buttonStyle = css(typo.Mobile.Text.Medium_12, { outline: 'none', cursor: 'pointer', backgroundColor: 'transparent', - padding: '10.5px 60px 10.5px 24px', + padding: '16px 60px 16px 24px', textAlign: 'left', color: color.BK, border: 'none', + '&:active': { + backgroundColor: color.GR100, + }, });src/stories/mobile/MobileSideMenu.stories.tsx (1)
16-19
: 스토리북 테스트 케이스 보완 필요다음 테스트 케이스들의 추가를 고려해주세요:
isOpen: false
상태accessToken: undefined
또는null
상태- 실제 동작하는
handleLoginButton
구현예시 구현:
export const Closed: Story = { args: { ...Default.args, isOpen: false, }, }; export const LoggedOut: Story = { args: { ...Default.args, accessToken: undefined, }, };src/components/atoms/Chips/Chips.style.ts (2)
15-18
: 미디어 쿼리 브레이크포인트 표준화 필요현재 하드코딩된 430px 브레이크포인트를 상수나 테마 설정으로 분리하는 것이 좋습니다. 이는 유지보수성을 높이고 일관된 반응형 디자인을 보장할 수 있습니다.
+ // constants/breakpoints.ts 파일 생성 + export const BREAKPOINTS = { + MOBILE: '430px', + } as const; '@media (max-width: ${BREAKPOINTS.MOBILE})': { ...typo.Mobile.Text.SemiBold_10, padding: '4px 9px', },
26-29
: 중복된 미디어 쿼리 로직 개선 필요동일한 브레이크포인트를 사용하는 미디어 쿼리가 여러 곳에서 반복되고 있습니다. 재사용 가능한 믹스인이나 유틸리티 함수로 분리하는 것이 좋습니다.
+ // styles/mixins.ts + export const mobileStyling = (styles: CSSObject) => css({ + [`@media (max-width: ${BREAKPOINTS.MOBILE})`]: styles, + }); // 사용 예시 outline: css({ padding: '10px 18px', borderRadius: '12px', ...mobileStyling({ padding: '4px 9px', borderRadius: '7.2px', }), }),src/stories/mobile/MobileCreateButton.stories.tsx (1)
28-29
: 스토리북 인터랙션 테스트 개선 필요단순 console.log 대신 Storybook의 Actions를 활용하여 더 의미 있는 인터랙션 테스트를 구현하는 것이 좋습니다.
export const TalkPick: Story = { args: { imageType: 'talkpick', label: '톡픽', - onClick: () => console.log('TalkPick button clicked'), + onClick: action('톡픽 버튼 클릭됨'), }, };Also applies to: 36-37
src/components/mobile/MobileToggleGroup/MobileToggleGroup.style.ts (2)
18-19
: border 스타일 로직 개선 필요
isOpen
상태에 따른borderRadius
및borderBottom
스타일 적용이 다소 복잡합니다. 다음과 같이 개선하는 것을 고려해보세요:- borderBottom: isOpen ? `0.6px solid ${color.SKYBLUE}` : undefined, - borderRadius: isOpen ? undefined : '6px', + ...(!isOpen && { borderRadius: '6px' }), + ...(isOpen && { borderBottom: `0.6px solid ${color.SKYBLUE}` }),
27-27
: 트랜지션 지속 시간 조정 권장현재 트랜지션 시간이 0.1초로 설정되어 있어 사용자가 변화를 인지하기 어려울 수 있습니다. 부드러운 사용자 경험을 위해 0.2초 이상으로 조정을 권장드립니다.
- transition: 'all .1s ease-in', + transition: 'all .2s ease-in',src/components/mobile/MobileCreateButton/MobileCreateButton.tsx (1)
18-27
: switch 문을 객체 리터럴로 단순화 권장현재 switch 문을 사용한 이미지 컴포넌트 선택 로직을 객체 리터럴을 사용하여 더 간단하게 구현할 수 있습니다.
- let ImageComponent; - switch (imageType) { - case 'talkpick': - ImageComponent = CircleTalkPick; - break; - case 'game': - ImageComponent = CircleGame; - break; - default: - return null; - } + const IMAGE_COMPONENTS = { + talkpick: CircleTalkPick, + game: CircleGame, + }; + + const ImageComponent = IMAGE_COMPONENTS[imageType]; + if (!ImageComponent) return null;src/components/atoms/CategoryButton/CategoryButton.tsx (2)
27-30
: 이미지 컴포넌트 매핑 로직 개선 제안현재 조건부 연산자를 사용한 이미지 컴포넌트 선택 로직을 상수 객체를 사용하여 더 깔끔하게 구현할 수 있습니다.
+ const IMAGE_MAPPING = { + PickVote: { + mobile: PickVoteSmall, + desktop: PickVote, + }, + RandomGame: { + mobile: RandomGameSmall, + desktop: RandomGame, + }, + TodayPick: { + mobile: TodayPick, + desktop: TodayPick, + }, + }; + switch (imageType) { case 'PickVote': - ImageComponent = isMobile ? PickVoteSmall : PickVote; + ImageComponent = IMAGE_MAPPING.PickVote[isMobile ? 'mobile' : 'desktop']; break; case 'RandomGame': - ImageComponent = isMobile ? RandomGameSmall : RandomGame; + ImageComponent = IMAGE_MAPPING.RandomGame[isMobile ? 'mobile' : 'desktop']; break;
21-21
: isMobile 기본값 설정 위치 변경 제안
isMobile
prop의 기본값이 컴포넌트 내부에서 설정되어 있습니다. 이를 Props 인터페이스 레벨에서 설정하는 것이 더 명시적일 수 있습니다.export interface CategoryButtonProps extends ComponentPropsWithRef<'button'> { imageType: 'PickVote' | 'RandomGame' | 'TodayPick'; label: string; - isMobile?: boolean; + isMobile: boolean = false; } const CategoryButton = ({ imageType, label, - isMobile = false, + isMobile, ...attributes }: CategoryButtonProps) => {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 renderIcon = () => { + const Icon = isPressed + ? (isMobile ? BookmarkPRSmall : BookmarkPR) + : (isMobile ? BookmarkDFSmall : BookmarkDF); + return <Icon css={!isMobile ? S.icon : undefined} />; + };src/components/organisms/BalanceGameList/BalanceGameList.style.ts (2)
15-25
: 타이포그래피 스타일 사용 개선 제안타이포그래피 스타일을 분리하여 관리하는 것이 좋습니다. 현재 모바일용 타이포그래피가 인라인으로 적용되어 있어 일관성 있는 관리가 어려울 수 있습니다.
export const titleWrapStyle = css({ - ...typo.Title, + ...typo.getResponsiveStyle('Title'), display: 'flex', justifyContent: 'space-between', marginBottom: '20px', '@media (max-width: 430px)': { - ...typo.Mobile.Text.Bold_16, marginBottom: 0, height: '50px', width: '100%', }, });
40-43
: 매직 넘버 상수화 제안gap 값 '9px'와 같은 매직 넘버들을 의미 있는 상수로 분리하는 것이 좋습니다.
+const MOBILE_SPACING = { + GAP: '9px', + PADDING: '16px 0', +} as const; export const contentStyle = css({ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '20px', '@media (max-width: 430px)': { - gap: '9px', + gap: MOBILE_SPACING.GAP, }, });src/components/molecules/TopBanner/TopBanner.style.ts (1)
31-34
: 모바일 UI 스타일링이 체계적으로 구현되었습니다.도트 네비게이션의 크기와 간격이 모바일 환경에 맞게 잘 조정되었습니다. 다만, 아래 사항들을 고려해 보시면 좋겠습니다:
- 반응형 값들을 상수로 분리하여 관리하는 것이 유지보수에 도움이 될 수 있습니다.
- 중복되는 미디어 쿼리를 mixin으로 분리하는 것을 고려해 보세요.
Also applies to: 44-47, 54-57
src/layout/layout.tsx (3)
17-19
: 미디어 쿼리 상수화가 필요합니다.하드코딩된 미디어 쿼리 값들을 상수로 분리하여 관리하면 일관성 있는 반응형 디자인 구현이 가능합니다.
+ // src/constants/breakpoints.ts + export const BREAKPOINTS = { + MOBILE: '430px', + } as const; + + export const LAYOUT_PADDING = { + DESKTOP: '100px', + MOBILE: '55px', + } as const; '@media (max-width: 430px)': { paddingTop: '55px', },
24-24
: Footer 표시/숨김 처리 개선이 필요합니다.현재 구현은 Footer를 조건부로 제거하고 있어 레이아웃 변경 시 갑작스러운 변화가 발생할 수 있습니다. CSS를 통한 부드러운 전환을 고려해보세요.
- {isMobile ? null : <Footer />} + <Footer + css={css` + display: ${isMobile ? 'none' : 'block'}; + transition: opacity 0.3s ease-in-out; + opacity: ${isMobile ? 0 : 1}; + `} + />
40-42
: 중복된 미디어 쿼리 로직이 발견되었습니다.
Layout
과LayoutNoSearch
컴포넌트에서 동일한 미디어 쿼리 로직이 반복되고 있습니다. 공통 스타일로 분리하는 것이 좋겠습니다.+ const commonMainStyle = css({ + paddingTop: '100px', + '@media (max-width: 430px)': { + paddingTop: '55px', + }, + }); // Layout 컴포넌트에서 - css({ - paddingTop: '100px', - '@media (max-width: 430px)': { - paddingTop: '55px', - }, - }) + commonMainStyle // LayoutNoSearch 컴포넌트에서도 동일하게 적용src/components/mobile/MobileToggleGroup/MobileToggleGroup.tsx (1)
38-58
: 접근성 개선이 필요합니다토글 그룹의 접근성을 개선하기 위해 ARIA 속성과 키보드 네비게이션을 추가하는 것이 좋습니다.
다음과 같은 개선사항을 제안합니다:
<div css={S.toggleGroupStyle}> <button css={S.clickedToggleStyle(isOpen)} type="button" + aria-expanded={isOpen} + aria-haspopup="true" + aria-label={`정렬 기준: ${selectedValue === 'views' ? '인기순' : '최신순'}`} onClick={handleMenuClick} >src/components/molecules/TopBanner/TodayTalkPickBanner/TodayTalkPickBanner.style.ts (2)
19-23
: 반응형 디자인 상수 관리 개선이 필요합니다미디어 쿼리의 브레이크포인트를 상수로 관리하면 일관성 있는 유지보수가 가능합니다.
다음과 같은 개선을 제안합니다:
// src/styles/breakpoints.ts export const BREAKPOINTS = { MOBILE: '430px', TABLET: '768px', // ... 기타 브레이크포인트 } as const; // 사용 예시 import { BREAKPOINTS } from '@/styles/breakpoints'; export const talkPickStyling = css({ // ... 기존 스타일 [`@media (max-width: ${BREAKPOINTS.MOBILE})`]: { // ... 모바일 스타일 }, });
37-42
: 반응형 타이포그래피 시스템 검토가 필요합니다여러 컴포넌트에서 동일한 모바일 타이포그래피 스타일이 반복되고 있습니다. 재사용 가능한 믹스인으로 관리하면 일관성과 유지보수성이 향상될 것 같습니다.
다음과 같은 아키텍처 개선을 제안합니다:
// src/styles/mixins.ts import { css } from '@emotion/react'; import typo from '@/styles/typo'; import { BREAKPOINTS } from '@/styles/breakpoints'; export const withMobileTypography = (desktopTypo: any, mobileTypo: any) => css` ${desktopTypo}; @media (max-width: ${BREAKPOINTS.MOBILE}) { ${mobileTypo}; } `; // 사용 예시 export const bannerChipStyling = css({ ...withMobileTypography( typo.Main.SemiBold, typo.Mobile.Text.SemiBold_7 ), // ... 나머지 스타일 });Also applies to: 55-60, 69-73
src/components/molecules/CategoryBar/CategoryBar.tsx (1)
19-21
: 타입 안전성 개선이 필요합니다
isMobile
프로퍼티가 선택적(optional)으로 정의되어 있지만, 기본값이 있으므로 필수 프로퍼티로 변경하는 것이 더 명확할 것 같습니다.- isMobile?: boolean; + isMobile: boolean;src/components/mobile/MobileCreateDropdown/MobileCreateDropdown.tsx (1)
43-48
: 이벤트 리스너 최적화가 필요합니다전역 이벤트 리스너를 사용할 때는 성능 최적화를 고려해야 합니다. 현재 구현은 불필요하게 자주 이벤트 핸들러가 호출될 수 있습니다.
다음과 같이 개선해보세요:
useEffect(() => { if (isOpen) { const handleOutsideClick = (e: MouseEvent) => { if (!dropdownRef.current?.contains(e.target as Node)) { setIsOpen(false); } }; window.addEventListener('click', handleOutsideClick); return () => window.removeEventListener('click', handleOutsideClick); } }, [isOpen]);src/components/organisms/BalanceGameList/BalanceGameList.tsx (1)
41-51
: 조건부 렌더링 구현이 깔끔합니다.모바일/데스크톱 환경에 따른 토글 그룹 컴포넌트 분기 처리가 잘 되어있습니다. 다만, 재사용성을 위해 공통 props 타입을 분리하는 것을 고려해보세요.
+ interface CommonToggleGroupProps { + selectedValue: string; + onClick: (value: string) => void; + } + interface MobileToggleGroupProps extends CommonToggleGroupProps { + // mobile specific props + } + interface ToggleGroupProps extends CommonToggleGroupProps { + // desktop specific props + }src/components/molecules/ContentsButton/ContentsButton.style.ts (2)
30-36
: extraSmall 사이즈 스펙이 적절합니다.모바일 환경에 맞는 크기로 잘 정의되어 있습니다. 다만, 매직 넘버 대신 변수화를 고려해보세요.
+ const MOBILE = { + WIDTH: '162px', + HEIGHT: '121px', + INFO_HEIGHT: '40px', + LABEL_MAX_WIDTH: '141px', + IMAGE_HEIGHT: '81px', + }; extraSmall: { - width: '162px', + width: MOBILE.WIDTH, // ... 나머지 속성들도 동일하게 적용 },
67-69
: 미디어 쿼리 브레이크포인트가 일관되게 적용되었습니다.모바일 대응을 위한 미디어 쿼리가 430px로 통일되어 있습니다. 다만, 브레이크포인트를 상수로 분리하는 것을 추천드립니다.
+ const BREAKPOINT = { + MOBILE: '430px', + }; - @media (max-width: 430px) + @media (max-width: ${BREAKPOINT.MOBILE})Also applies to: 110-112, 119-121
src/components/atoms/BalanceGameCategoryButton/BalanceGameCategoryButton.style.ts (1)
77-96
: 반응형 스타일 로직이 개선될 여지가 있습니다.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', + }, + }; + const getBorderRadius = (label: string, isMobile: boolean) => { + if (label === '인기') return isMobile ? BORDER_RADIUS.MOBILE.LEFT : BORDER_RADIUS.DESKTOP.LEFT; + if (label === '월드컵') return isMobile ? BORDER_RADIUS.MOBILE.RIGHT : BORDER_RADIUS.DESKTOP.RIGHT; + return '0'; + };src/assets/index.ts (1)
104-104
: 이전 SVG 정리 필요TODO 주석에 명시된 대로 이전 SVG 파일들의 정리가 필요합니다. 현재 코드베이스의 일관성을 위해 이 작업의 우선순위를 고려해주세요.
이전 SVG 파일들의 정리를 위한 이슈를 생성해드릴까요?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (17)
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 (43)
public/index.html
(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/MobileCreateButton/MobileCreateButton.style.ts
(1 hunks)src/components/mobile/MobileCreateButton/MobileCreateButton.tsx
(1 hunks)src/components/mobile/MobileCreateDropdown/MobileCreateDropdown.style.ts
(1 hunks)src/components/mobile/MobileCreateDropdown/MobileCreateDropdown.tsx
(1 hunks)src/components/mobile/MobileSideMenu/MobileSideMenu.style.ts
(1 hunks)src/components/mobile/MobileSideMenu/MobileSideMenu.tsx
(1 hunks)src/components/mobile/MobileToggleGroup/MobileToggleGroup.style.ts
(1 hunks)src/components/mobile/MobileToggleGroup/MobileToggleGroup.tsx
(1 hunks)src/components/molecules/CategoryBar/CategoryBar.style.ts
(1 hunks)src/components/molecules/CategoryBar/CategoryBar.tsx
(3 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
(3 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/mobile/MobileCreateButton.stories.tsx
(1 hunks)src/stories/mobile/MobileCreateDropdown.stories.tsx
(1 hunks)src/stories/mobile/MobileSideMenu.stories.tsx
(1 hunks)src/stories/mobile/MobileToggleGroup.stories.tsx
(1 hunks)src/styles/typo.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- public/index.html
- src/stories/mobile/MobileCreateDropdown.stories.tsx
- src/stories/mobile/MobileToggleGroup.stories.tsx
🔇 Additional comments (28)
src/pages/LandingPage/LandingPage.style.ts (3)
17-19
: 구현이 적절합니다!
relative 포지셔닝을 통해 하위 요소들의 위치 컨텍스트를 올바르게 설정했습니다.
39-41
: 타이포그래피 구현이 적절합니다!
모바일용 타이포그래피를 올바르게 적용했으며, 디자인 시스템을 잘 따르고 있습니다.
21-25
: z-index 값 검증 필요
고정 위치 지정은 적절하나, z-index 값이 다른 요소들과 충돌하지 않는지 확인이 필요합니다.
✅ Verification successful
z-index 50은 안전하나 일관성을 위해 수정 권장
현재 코드베이스의 z-index 사용 현황을 분석한 결과:
- 모달과 오버레이: 300, 1000
- 모바일 메뉴: 1100
- 일반 UI 요소: 1-100 사이
- 드롭다운 요소: 10-20
floatingDropdownStyle의 z-index 50은 다른 요소들과 직접적인 충돌은 없으나, 모바일 드롭다운의 일관성을 위해 다음과 같이 수정하는 것을 권장합니다:
- MobileCreateDropdown 컴포넌트와 동일하게 z-index를 10-20 범위로 조정
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# z-index 사용 현황 확인
rg -n "zIndex|z-index" --type css --type typescript
Length of output: 88
Script:
#!/bin/bash
# z-index 사용 현황 확인 (파일 확장자 직접 지정)
rg -n "zIndex|z-index" -g "*.ts" -g "*.tsx" -g "*.css" -g "*.scss"
Length of output: 4780
src/components/organisms/Header/Header.style.ts (1)
44-51
: 우측 컨테이너 스타일 구현이 적절합니다
Flex를 활용한 정렬과 모바일 환경에서의 배치가 잘 구현되었습니다. 브레이크포인트 표준화만 적용하면 완벽할 것 같습니다.
src/components/organisms/Header/Header.tsx (1)
29-29
: 모바일 브레이크포인트 검증이 필요합니다.
useIsMobile
훅의 브레이크포인트가 디자인 시스템의 모바일 기준과 일치하는지 확인이 필요합니다.
✅ Verification successful
모바일 브레이크포인트가 일관되게 적용되어 있습니다.
코드베이스 분석 결과:
- 모든 컴포넌트에서 일관되게 430px을 모바일 브레이크포인트로 사용하고 있습니다.
useIsMobile
훅은 디바이스의 User Agent를 기반으로 모바일 여부를 판단하며, 브레이크포인트와 함께 적절하게 동작합니다.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify mobile breakpoint consistency across the codebase
# Search for breakpoint definitions
echo "Searching for breakpoint definitions..."
rg -n "breakpoint|@media.*max-width|@media.*min-width" --type css --type typescript
# Search for useIsMobile implementation
echo "Searching for useIsMobile implementation..."
rg -n "useIsMobile" --type typescript
Length of output: 379
Script:
#!/bin/bash
# Search for breakpoint definitions and useIsMobile implementation with correct file extensions
echo "Searching for breakpoint definitions..."
rg -n "breakpoint|@media.*max-width|@media.*min-width" -t tsx -t ts -t css -t scss
echo -e "\nSearching for useIsMobile implementation..."
rg -n "useIsMobile" -t tsx -t ts -A 5
Length of output: 370
Script:
#!/bin/bash
# Search without file type restrictions
echo "Searching for breakpoint definitions..."
rg -n "breakpoint|@media.*max-width|@media.*min-width"
echo -e "\nSearching for useIsMobile implementation and its definition..."
rg -n "useIsMobile" -A 5
echo -e "\nSearching for files containing useIsMobile..."
fd useIsMobile
Length of output: 9545
src/pages/LandingPage/LandingPage.tsx (2)
13-14
: 추가된 import 문이 적절합니다.
MobileCreateDropdown
과 useIsMobile
을 올바르게 import하셨습니다.
18-18
: isMobile
상태 변수를 추가하여 반응형 디자인을 구현한 점이 좋습니다.
모바일 여부에 따라 다른 UI를 제공하기 위해 useIsMobile
훅을 사용하셨습니다.
src/components/molecules/CategoryBar/CategoryBar.style.ts (1)
7-9
: 모바일 화면에서의 패딩 제거가 적절합니다.
@media (max-width: 430px)
미디어 쿼리를 사용하여 작은 화면에서 패딩을 제거하여 레이아웃을 최적화하신 점이 좋습니다.
src/components/molecules/CategoryBox/CategoryBox.style.ts (1)
5-11
: 컨테이너 스타일을 개선하여 반응형 디자인을 구현하셨습니다.
alignItems
, justifyContent
, width
속성을 추가하고 미디어 쿼리를 통해 작은 화면에서의 간격을 조정하여 모바일 환경에서의 레이아웃을 개선하셨습니다.
src/components/atoms/Bookmark/Bookmark.style.ts (1)
14-17
: 모바일 환경에서 버튼 크기를 조정하여 사용자 경험을 향상시켰습니다.
@media (max-width: 430px)
를 사용하여 작은 화면에서 북마크 버튼의 크기를 조정한 것은 반응형 디자인 측면에서 적절합니다.
src/components/mobile/MobileSideMenu/MobileSideMenu.style.ts (1)
5-17
: z-index 값과 터치 영역 크기 검토 필요
모바일 환경에서 다음 사항들을 고려해주세요:
- z-index 값(1100)이 다른 요소들과 충돌하지 않는지 확인이 필요합니다.
- 터치 영역이 iOS Human Interface Guidelines(최소 44x44px)와 Material Design Guidelines(최소 48x48px)를 충족하는지 확인해주세요.
✅ Verification successful
z-index 값이 적절하게 설정되어 있습니다
z-index 검색 결과를 분석한 결과:
- 모달과 오버레이: 300-1000
- 드롭다운과 메뉴: 10-20
- 일반 UI 요소: 1-100
- MobileSideMenu의 z-index 1100은 다른 요소들과 충돌하지 않으며, 모바일 메뉴가 최상단에 표시되어야 하는 용도에 적합합니다.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# z-index 충돌 가능성 확인
rg -n "z-index|zIndex" --type ts --type tsx
Length of output: 73
Script:
#!/bin/bash
# z-index 충돌 가능성 확인 (수정된 명령어)
rg -n "z-index|zIndex" -t ts
Length of output: 4742
Script:
#!/bin/bash
# 모바일 관련 스타일 파일들 검색
fd -e ts -e tsx . src/ --exec grep -l "style"
Length of output: 21472
Script:
#!/bin/bash
# 버튼 관련 스타일 검색
rg -n "button.*style|style.*button" -t ts
Length of output: 43
src/components/mobile/MobileCreateButton/MobileCreateButton.style.ts (1)
29-31
: 색상 대비 검증 필요
접근성 지침을 준수하기 위해 메인 색상과 배경색 간의 대비율이 WCAG 기준을 충족하는지 확인해주세요.
src/components/atoms/Bookmark/Bookmark.tsx (1)
2-8
: 모바일 아이콘 컴포넌트 추가에 대한 승인
모바일 환경을 위한 Small 아이콘 컴포넌트들이 적절하게 추가되었습니다.
src/components/atoms/Button/Button.tsx (1)
11-11
: 'small' 사이즈 지원 추가에 대한 검증 필요
'small' 사이즈가 ButtonProps에 추가되었습니다. 관련 스타일이 제대로 구현되어 있는지 확인이 필요합니다.
src/components/molecules/CategoryBox/CategoryBox.tsx (2)
7-7
: 선택적 prop 타입 정의가 적절합니다.
isMobile
prop이 옵셔널로 정의되어 있어 기존 코드와의 호환성이 유지됩니다.
11-23
: 모바일 UI 구현이 깔끔합니다.
모바일 버전에서는 두 개의 버튼만 표시하여 UI를 단순화한 것이 적절합니다. 다만, 아래 사항들을 확인해 주시면 좋겠습니다:
- 모바일에서 제거된 "오늘의 톡픽 모음.zip" 버튼의 기능을 사용자가 다른 방법으로 접근할 수 있는지
- 모바일 버전의 레이아웃이 디자인 요구사항과 일치하는지
✅ Verification successful
모바일 UI에서 "오늘의 톡픽 모음.zip" 접근성이 확보되어 있습니다.
모바일 버전에서 "오늘의 톡픽 모음.zip" 버튼이 제거되었지만, 다음과 같은 대체 접근 경로가 존재합니다:
- 메인 페이지의 상단 배너를 통해 "오늘의 톡픽"에 직접 접근 가능 (
/todaytalkpick
라우트) - "톡&픽 플레이스" 메뉴를 통해 톡픽 콘텐츠 전체에 접근 가능 (
/talkpickplace
라우트)
따라서 모바일 사용자의 콘텐츠 접근성이 적절히 유지되고 있습니다.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check if there's an alternative way to access "오늘의 톡픽 모음.zip" on mobile
# Search for routes or links related to "오늘의 톡픽"
rg -i "오늘의\s*톡픽" --type ts --type tsx
Length of output: 67
Script:
#!/bin/bash
# Search for files with .tsx extension and content related to "오늘의 톡픽"
rg -i "오늘의.*톡픽" -g "*.tsx"
# Search for route definitions
rg -i "route|path" -g "*.tsx" -A 2
# Check CategoryBox component in non-mobile view
cat src/components/molecules/CategoryBox/CategoryBox.tsx
Length of output: 42836
Script:
#!/bin/bash
# Check for routes and links to "오늘의 톡픽" content
rg -i "todaytalkpick|오늘의.*톡픽" -g "*.{tsx,ts}" -A 2
# Check mobile navigation components
fd -e tsx -e ts "Mobile.*Menu|Mobile.*Nav" -x cat {}
Length of output: 13721
src/components/molecules/TopBanner/TodayTalkPickBanner/TodayTalkPickBanner.tsx (2)
4-5
: import 구문이 깔끔하게 정리되었습니다.
새로운 아이콘과 훅의 import가 적절히 추가되었습니다.
19-19
: 모바일 대응이 잘 구현되었습니다.
useIsMobile
훅을 사용한 조건부 렌더링이 적절합니다. 다만, 아이콘 크기 차이가 디자인 명세와 일치하는지 확인해 주세요.
Also applies to: 34-35
src/components/molecules/TopBanner/TopBanner.style.ts (1)
8-10
: 미디어 쿼리 브레이크포인트 검토가 필요합니다.
430px 브레이크포인트가 다른 컴포넌트들과 일관성이 있는지 확인이 필요합니다.
✅ Verification successful
브레이크포인트가 일관되게 사용되고 있습니다
검색 결과에 따르면 프로젝트 전체에서 430px 브레이크포인트가 일관되게 사용되고 있습니다. 모든 컴포넌트에서 동일한 모바일 브레이크포인트를 채택하고 있어 현재 구현이 적절합니다.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check breakpoint consistency across components
# Search for all media query breakpoints
rg -i "max-width:\s*\d+" --type ts --type tsx
Length of output: 75
Script:
#!/bin/bash
# Search for media query breakpoints in TypeScript files
rg -i "@media.*max-width.*\d+" --type ts
# Also search in style files that might have different extensions
rg -i "@media.*max-width.*\d+" -g "*.style.*"
rg -i "@media.*max-width.*\d+" -g "*.styled.*"
Length of output: 8166
src/components/molecules/SearchTagBar/SearchTagBar.tsx (1)
9-9
: isMobile prop의 타입 정의가 적절합니다.
선택적 boolean 타입으로 정의되어 있어 기존 코드와의 호환성이 유지됩니다.
src/components/molecules/ContentsButton/ContentsButton.tsx (1)
14-14
: 사이즈 옵션 추가가 적절합니다
모바일 UI를 위한 'extraSmall' 사이즈 옵션 추가는 적절해 보입니다. 기존 사이즈 옵션들과의 호환성도 잘 유지되어 있습니다.
src/components/organisms/BalanceGameList/BalanceGameList.tsx (2)
8-8
: Props 인터페이스와 기본값 설정이 적절합니다.
모바일 대응을 위한 isMobile
prop의 추가와 기본값 설정이 잘 되어있습니다.
Also applies to: 19-19, 28-28
54-58
: 모바일 대응을 위한 size prop 전달이 일관성 있게 구현되었습니다.
CategoryBar
, ContentsButton
, Button
컴포넌트에 모바일 환경에 맞는 size prop이 적절하게 전달되고 있습니다.
Also applies to: 63-63, 76-76
src/components/atoms/BalanceGameCategoryButton/BalanceGameCategoryButton.style.ts (3)
15-17
: 모바일 환경의 레이아웃 조정이 적절합니다.
버튼의 너비와 여백이 모바일 화면에 맞게 잘 조정되었습니다.
Also applies to: 26-28, 33-35
38-43
: 모바일 타이포그래피 적용이 일관성 있습니다.
활성/비활성 상태의 텍스트 스타일이 모바일에 맞게 잘 정의되었습니다.
Also applies to: 46-51
122-131
: 배지 스타일의 모바일 대응이 적절합니다.
패딩과 폰트 크기가 모바일 환경에 맞게 잘 조정되었습니다.
src/components/atoms/Button/Button.style.ts (1)
173-181
: outlineShadow
variant의 모바일 스타일 구현이 적절함
outlineShadow
variant의 small
사이즈에 대한 스타일링이 잘 정의되어 있습니다. 다른 variant들도 이와 같은 상세한 스타일 정의가 필요합니다.
src/assets/index.ts (1)
86-102
: 모바일 아이콘 추가가 잘 구현됨
모바일 버전을 위한 아이콘들이 체계적으로 추가되었고, 명확한 네이밍 컨벤션('Small' 접미사)을 따르고 있습니다.
src/hooks/common/useIsMobile.ts
Outdated
const userAgent = | ||
typeof window.navigator === 'undefined' ? '' : navigator.userAgent; | ||
const mobile = | ||
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( | ||
userAgent, | ||
); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
'@media (max-width: 430px)': { | ||
width: '335px', | ||
height: '36px', | ||
padding: '0 5px', | ||
}, |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
export const overlay = css({ | ||
position: 'fixed', | ||
top: 0, | ||
left: 0, | ||
width: '100vw', | ||
height: '100vh', | ||
backgroundColor: 'rgba(255, 255, 255, 0.5)', | ||
zIndex: 10, | ||
}); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
export const categoryButtonBaseStyle = (imageType: 'talkpick' | 'game') => | ||
css({ | ||
display: 'flex', | ||
flexDirection: 'row', | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
gap: '13px', | ||
padding: '5px 5px 5px 21px', | ||
borderRadius: '500px', | ||
backgroundColor: color.WT, | ||
boxShadow: '1px 2px 15px 0 rgba(119, 130, 255, 0.2)', | ||
cursor: 'pointer', | ||
transition: 'all 0.3s ease-in', | ||
width: imageType === 'talkpick' ? '102px' : '139px', | ||
marginLeft: imageType === 'talkpick' ? '37px' : 0, | ||
'&:hover': { | ||
boxShadow: '1px 2px 15px 0 rgba(119, 130, 255, 0.8)', | ||
}, | ||
}); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
useEffect(() => { | ||
window.addEventListener('click', handleOutsideClick); | ||
return () => { | ||
window.removeEventListener('click', handleOutsideClick); | ||
}; | ||
}, []); |
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.
useEffect 의존성 배열 개선이 필요합니다
handleOutsideClick
함수가 의존성 배열에 포함되어 있지 않아 메모리 누수가 발생할 수 있습니다.
다음과 같이 수정을 제안합니다:
- useEffect(() => {
- window.addEventListener('click', handleOutsideClick);
- return () => {
- window.removeEventListener('click', handleOutsideClick);
- };
- }, []);
+ useEffect(() => {
+ window.addEventListener('click', handleOutsideClick);
+ return () => {
+ window.removeEventListener('click', handleOutsideClick);
+ };
+ }, [handleOutsideClick]);
📝 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.
useEffect(() => { | |
window.addEventListener('click', handleOutsideClick); | |
return () => { | |
window.removeEventListener('click', handleOutsideClick); | |
}; | |
}, []); | |
useEffect(() => { | |
window.addEventListener('click', handleOutsideClick); | |
return () => { | |
window.removeEventListener('click', handleOutsideClick); | |
}; | |
}, [handleOutsideClick]); |
<button css={S.buttonStyle} type="button" onClick={handleLoginButton}> | ||
회원 탈퇴 | ||
</button> |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
return ( | ||
<div css={S.buttonGroupStyle}> | ||
<div css={S.buttonContainerStyle}> | ||
<button | ||
css={S.buttonStyle} | ||
type="button" | ||
onClick={() => { | ||
if (accessToken) { | ||
handleNavigation('/mypage'); | ||
} else { | ||
handleNavigation('/login'); | ||
} | ||
}} | ||
> | ||
마이페이지 | ||
</button> | ||
|
||
<button | ||
css={S.buttonStyle} | ||
type="button" | ||
onClick={() => { | ||
if (accessToken) { | ||
handleNavigation('/notifications'); | ||
} else { | ||
handleNavigation('/login'); | ||
} | ||
}} | ||
> | ||
알림 내역 | ||
</button> | ||
<button css={S.buttonStyle} type="button" onClick={handleLoginButton}> | ||
{accessToken ? '로그아웃' : '로그인'} | ||
</button> | ||
{accessToken && ( | ||
<button css={S.buttonStyle} type="button" onClick={handleLoginButton}> | ||
회원 탈퇴 | ||
</button> | ||
)} | ||
</div> | ||
<div css={S.fillerStyle} role="presentation" /> | ||
</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
접근성 및 사용자 경험 개선이 필요합니다
모바일 사이드 메뉴의 구현에서 다음 사항들을 개선하면 좋겠습니다:
- 키보드 접근성이 부족합니다
- 화면 읽기 프로그램 사용자를 위한 ARIA 속성이 누락되었습니다
- 사이드 메뉴가 열리고 닫힐 때 애니메이션이 없어 사용자 경험이 부자연스럽습니다
다음과 같이 개선해보세요:
- return (
- <div css={S.buttonGroupStyle}>
+ <aside
+ css={S.buttonGroupStyle}
+ role="dialog"
+ aria-modal="true"
+ aria-label="사이드 메뉴">
<div css={S.buttonContainerStyle}>
Committable suggestion skipped: line range outside the PR's diff.
label="인기" | ||
icon={<Popular />} | ||
icon={isMobile ? <PopularSmall /> : <Popular />} | ||
active={activeTab === '인기'} | ||
badgeText={getBadgeText('인기')} | ||
onClick={() => setActiveTab('인기')} | ||
/> | ||
<BalanceGameCategoryButton | ||
label="커플" | ||
icon={<Couple />} | ||
icon={isMobile ? <CoupleSmall /> : <Couple />} | ||
active={activeTab === '커플'} | ||
badgeText={getBadgeText('커플')} | ||
onClick={() => setActiveTab('커플')} | ||
/> | ||
<BalanceGameCategoryButton | ||
label="취향" | ||
icon={<Taste />} | ||
icon={isMobile ? <TasteSmall /> : <Taste />} | ||
active={activeTab === '취향'} | ||
badgeText={getBadgeText('취향')} | ||
onClick={() => setActiveTab('취향')} | ||
/> | ||
<BalanceGameCategoryButton | ||
label="월드컵" | ||
icon={<Worldcup />} | ||
icon={isMobile ? <WorldcupSmall /> : <Worldcup />} | ||
active={activeTab === '월드컵'} | ||
badgeText={getBadgeText('월드컵')} | ||
onClick={() => setActiveTab('월드컵')} |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
src/styles/typo.ts
Outdated
Mobile: { | ||
Title: { | ||
SemiBold_24: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '24px', | ||
fontWeight: 600, | ||
lineHeight: '1.6', | ||
letterSpacing: `${24 * -0.05}px`, | ||
}, | ||
}, | ||
Main: { | ||
Medium_16: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '16px', | ||
lineHeight: '1.6', | ||
letterSpacing: `${16 * -0.05}px`, | ||
}, | ||
Regular_12: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '12px', | ||
fontWeight: 400, | ||
lineHeight: '1.6', | ||
letterSpacing: `${12 * -0.05}px`, | ||
}, | ||
}, | ||
Text: { | ||
Bold_20: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '20px', | ||
fontWeight: 700, | ||
letterSpacing: `${20 * -0.05}px`, | ||
}, | ||
Bold_16: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '16px', | ||
fontWeight: 700, | ||
lineHeight: '1.6', | ||
letterSpacing: `${16 * -0.05}px`, | ||
}, | ||
Bold_12: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '12px', | ||
fontWeight: 700, | ||
lineHeight: '1.6', | ||
letterSpacing: `${16 * -0.05}px`, | ||
}, | ||
SemiBold_20: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '20px', | ||
fontWeight: 600, | ||
lineHeight: '1.6', | ||
letterSpacing: `${20 * -0.05}px`, | ||
}, | ||
SemiBold_14: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '14px', | ||
fontWeight: 600, | ||
lineHeight: '1.6', | ||
letterSpacing: `${14 * -0.05}px`, | ||
}, | ||
SemiBold_12: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '12px', | ||
fontWeight: 600, | ||
lineHeight: '1.3', | ||
}, | ||
SemiBold_10: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '10px', | ||
fontWeight: 600, | ||
lineHeight: '1.6', | ||
letterSpacing: `${14 * -0.05}px`, | ||
}, | ||
SemiBold_7: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '7.2px', | ||
fontWeight: 600, | ||
lineHeight: '1.6', | ||
letterSpacing: '-0.05em', | ||
}, | ||
Medium_16: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '16px', | ||
lineHeight: '1.6', | ||
letterSpacing: `${16 * -0.05}px`, | ||
}, | ||
Medium_12: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '12px', | ||
lineHeight: '1.3', | ||
letterSpacing: `${12 * -0.05}px`, | ||
}, | ||
Medium_8: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '8.5px', | ||
lineHeight: '1.3', | ||
letterSpacing: `${12 * -0.05}px`, | ||
}, | ||
}, | ||
}, |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
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: 11
🧹 Outside diff range and nitpick comments (16)
src/components/mobile/atom/DraftSaveButton/DraftSaveButton.tsx (1)
1-15
: 모바일 환경에서의 터치 이벤트 처리가 필요합니다.모바일 컴포넌트이므로 터치 이벤트에 대한 고려가 필요합니다.
다음과 같은 개선을 제안드립니다:
interface DraftSaveButtonProps { onClick: () => void; + onTouchStart?: () => void; + onTouchEnd?: () => void; } -const DraftSaveButton = ({ onClick }: DraftSaveButtonProps) => { +const DraftSaveButton = ({ + onClick, + onTouchStart, + onTouchEnd +}: DraftSaveButtonProps) => { return ( <button css={S.buttonStyle} type="button" onClick={onClick} + onTouchStart={onTouchStart} + onTouchEnd={onTouchEnd} > 임시저장 </button> ); };src/components/mobile/atom/MobileToggleGroup/MobileToggleGroup.style.ts (2)
5-8
: 모바일 화면 너비 고려가 필요합니다.토글 그룹의 너비를 명시적으로 지정하는 것이 좋습니다. 모바일 환경에서 예상치 못한 레이아웃 이슈를 방지할 수 있습니다.
다음과 같이 width 속성을 추가하는 것을 고려해보세요:
export const toggleGroupStyle = css({ display: 'flex', flexDirection: 'column', + width: '100%', });
23-33
: 스타일 일관성 개선이 필요합니다.
- 클릭되지 않은 상태의 패딩(4px 26px 4px 9px)이 클릭된 상태와 다릅니다.
- 디자인 토큰화된 값을 사용하면 유지보수가 더 쉬워질 것 같습니다.
다음과 같이 개선하는 것을 제안합니다:
+ const TOGGLE_PADDING = '4px 8px 4px 9px'; + const BORDER_RADIUS = '6px'; export const unClickedToggleStyle = css(typo.Mobile.Text.SemiBold_12, { color: color.GY[1], backgroundColor: color.WT, - padding: '4px 26px 4px 9px', + padding: TOGGLE_PADDING, transition: 'all .1s ease-in', - borderRadius: '0 0 6px 6px', + borderRadius: `0 0 ${BORDER_RADIUS} ${BORDER_RADIUS}`, cursor: 'pointer', '&:hover': { backgroundColor: color.WT_VIOLET, }, });src/components/mobile/atom/MobileToggleGroup/MobileToggleGroup.tsx (1)
16-29
: 이벤트 처리 방식과 재사용성 개선이 필요합니다
e.stopPropagation()
의 사용이 다른 이벤트 핸들러의 동작을 방해할 수 있습니다.- 토글 값('views', 'createdAt')이 하드코딩되어 있어 컴포넌트의 재사용성이 제한됩니다.
다음과 같은 개선을 제안드립니다:
+interface ToggleOption { + value: string; + label: string; +} + interface MobileToggleGroupProps { selectedValue: string; onClick?: (value: string) => void; + options: ToggleOption[]; } const handleToggleClick = () => { - const newValue = selectedValue === 'views' ? 'createdAt' : 'views'; + const currentIndex = options.findIndex(opt => opt.value === selectedValue); + const nextIndex = (currentIndex + 1) % options.length; + const newValue = options[nextIndex].value; onClick?.(newValue); setIsOpen(false); };src/components/organisms/Header/Header.tsx (1)
94-97
:setIsMenuOpen(false);
의 중복 호출을 제거해주세요
handleLoginButton
함수에서setIsMenuOpen(false);
이if
와else
블록 모두에서 호출되고 있습니다. 중복 코드를 제거하기 위해if
문 바깥으로 이동시키는 것이 좋습니다.다음과 같이 수정할 수 있습니다:
const handleLoginButton = () => { if (accessToken) { logout.mutate(); } else { navigate('/login'); } + setIsMenuOpen(false); };
src/components/mobile/molecule/MobileCreateDropdown/MobileCreateDropdown.style.ts (2)
2-2
: 주석 처리된 import 문을 정리해주세요.주석 처리된 color import 문을 제거하거나 실제로 사용하도록 수정해주세요. 현재는 불필요한 코드로 남아있습니다.
4-12
: z-index 값을 중앙 집중화하는 것이 좋습니다.여러 컴포넌트에서 하드코딩된 z-index 값(10, 20)을 사용하고 있습니다. 이는 유지보수를 어렵게 만들 수 있습니다.
다음과 같이 상수로 분리하는 것을 제안합니다:
const Z_INDICES = { OVERLAY: 10, DROPDOWN: 20, } as const;Also applies to: 14-19, 21-30, 32-40
src/components/mobile/atom/MobileCreateButton/MobileCreateButton.style.ts (3)
12-13
: 매직 넘버를 상수로 분리하는 것이 좋습니다.padding, gap, width 등의 매직 넘버를 의미 있는 상수로 분리하면 유지보수가 더 쉬워집니다.
다음과 같이 상수로 분리하는 것을 제안합니다:
const BUTTON_SIZES = { PADDING: { DEFAULT: '5px', LEFT: '21px', }, GAP: '13px', WIDTH: { TALKPICK: '102px', GAME: '139px', }, MARGIN: { TALKPICK: '37px', }, } as const;Also applies to: 18-19
17-17
: transition 속성을 최적화해주세요.
all
대신 실제로 변경되는 속성만 transition을 적용하는 것이 성능상 좋습니다.- transition: 'all 0.3s ease-in', + transition: 'box-shadow 0.3s ease-in',
15-15
: 박스 쉐도우 값을 중앙 집중화하는 것이 좋습니다.반복되는 박스 쉐도우 값을 상수로 분리하면 일관성 있는 스타일 관리가 가능합니다.
const BOX_SHADOWS = { DEFAULT: '1px 2px 15px 0 rgba(119, 130, 255, 0.2)', HOVER: '1px 2px 15px 0 rgba(119, 130, 255, 0.8)', } as const;Also applies to: 21-21
src/components/mobile/atom/MobileCreateButton/MobileCreateButton.tsx (2)
5-9
: 타입 안전성을 개선할 수 있습니다.
imageType
을 리터럴 유니온 타입으로 정의한 것은 좋습니다. 하지만 이를 상수로 분리하면 재사용성과 타입 안전성이 더욱 향상됩니다.export const IMAGE_TYPES = { TALKPICK: 'talkpick', GAME: 'game', } as const; type ImageType = typeof IMAGE_TYPES[keyof typeof IMAGE_TYPES]; interface MobileCreateButtonProps extends ComponentPropsWithRef<'button'> { imageType: ImageType; label: string; onClick?: () => void; }
17-27
: 이미지 컴포넌트 선택 로직을 최적화할 수 있습니다.switch 문 대신 객체 매핑을 사용하면 코드가 더 간결해지고 성능이 개선됩니다.
const IMAGE_COMPONENTS = { talkpick: CircleTalkPick, game: CircleGame, } as const; const ImageComponent = IMAGE_COMPONENTS[imageType]; if (!ImageComponent) return null;src/components/mobile/molecule/MobileCreateDropdown/MobileCreateDropdown.tsx (2)
1-11
: ESLint 비활성화 범위를 최소화하세요전역 ESLint 규칙 비활성화보다는 특정 코드 라인에만 적용하는 것이 좋습니다. 필요한 부분에만 규칙을 비활성화하여 코드 품질을 유지하세요.
다음과 같이 수정하는 것을 추천드립니다:
-/* eslint-disable jsx-a11y/click-events-have-key-events */ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ // 필요한 라인에만 적용 // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
18-32
: 중복된 네비게이션 로직을 개선하세요
handleCreatePostButton
와handleCreateGameButton
에 중복된 로직이 있습니다. 이를 하나의 함수로 추상화하면 코드 유지보수가 더 쉬워질 것 같습니다.다음과 같이 리팩토링하는 것을 추천드립니다:
const handleNavigation = (path: string) => { if (accessToken) { navigate(path); } else { navigate(PATH.LOGIN); } }; const handleCreatePostButton = () => handleNavigation(PATH.CREATE.TALK_PICK); const handleCreateGameButton = () => handleNavigation(PATH.CREATE.GAME);src/pages/LandingPage/LandingPage.tsx (2)
18-18
: 조건부 렌더링 최적화가 필요합니다모바일/데스크톱 버전의 렌더링 로직이 복잡해질 수 있으므로, 각각의 컴포넌트를 분리하고 메모이제이션을 적용하는 것이 좋을 것 같습니다.
다음과 같은 구조로 개선하는 것을 추천드립니다:
const MobileContent = memo(({ contents, selectedValue, setSelectedValue, activeTab, setActiveTab, handleSearch, handleService }) => ( <div css={S.contentWrapStyle}> {/* 모바일 버전 컨텐츠 */} </div> )); const DesktopContent = memo(({ /* props */ }) => ( <div css={S.contentWrapStyle}> {/* 데스크톱 버전 컨텐츠 */} </div> )); // LandingPage 컴포넌트 내부 {isMobile ? ( <MobileContent {...props} /> ) : ( <DesktopContent {...props} /> )}Also applies to: 70-103
Line range hint
31-48
: 상태 관리 로직을 최적화하세요
contents
상태 업데이트 로직이 복잡하고 의존성이 많습니다.useMemo
를 사용하여 최적화하고 의존성을 줄이는 것이 좋을 것 같습니다.다음과 같이 개선하는 것을 추천드립니다:
const contents = useMemo(() => { if (isBestLoading || isLatestLoading) return []; return selectedValue === 'views' ? bestGames : latestGames; }, [selectedValue, bestGames, latestGames, isBestLoading, isLatestLoading]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (18)
src/components/mobile/atom/DraftSaveButton/DraftSaveButton.style.ts
(1 hunks)src/components/mobile/atom/DraftSaveButton/DraftSaveButton.tsx
(1 hunks)src/components/mobile/atom/MobileCreateButton/MobileCreateButton.style.ts
(1 hunks)src/components/mobile/atom/MobileCreateButton/MobileCreateButton.tsx
(1 hunks)src/components/mobile/atom/MobileSideMenu/MobileSideMenu.style.ts
(1 hunks)src/components/mobile/atom/MobileSideMenu/MobileSideMenu.tsx
(1 hunks)src/components/mobile/atom/MobileToggleGroup/MobileToggleGroup.style.ts
(1 hunks)src/components/mobile/atom/MobileToggleGroup/MobileToggleGroup.tsx
(1 hunks)src/components/mobile/molecule/MobileCreateDropdown/MobileCreateDropdown.style.ts
(1 hunks)src/components/mobile/molecule/MobileCreateDropdown/MobileCreateDropdown.tsx
(1 hunks)src/components/organisms/BalanceGameList/BalanceGameList.tsx
(5 hunks)src/components/organisms/Header/Header.tsx
(4 hunks)src/pages/LandingPage/LandingPage.tsx
(2 hunks)src/stories/mobile/DraftSaveButton.stories.tsx
(1 hunks)src/stories/mobile/MobileCreateButton.stories.tsx
(1 hunks)src/stories/mobile/MobileCreateDropdown.stories.tsx
(1 hunks)src/stories/mobile/MobileSideMenu.stories.tsx
(1 hunks)src/stories/mobile/MobileToggleGroup.stories.tsx
(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- src/components/mobile/atom/DraftSaveButton/DraftSaveButton.style.ts
- src/stories/mobile/DraftSaveButton.stories.tsx
- src/components/mobile/atom/MobileSideMenu/MobileSideMenu.style.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- src/stories/mobile/MobileCreateDropdown.stories.tsx
- src/stories/mobile/MobileSideMenu.stories.tsx
- src/stories/mobile/MobileCreateButton.stories.tsx
- src/stories/mobile/MobileToggleGroup.stories.tsx
- src/components/organisms/BalanceGameList/BalanceGameList.tsx
🔇 Additional comments (3)
src/components/mobile/atom/DraftSaveButton/DraftSaveButton.tsx (1)
1-6
: 타입 정의와 인터페이스 구조가 적절합니다.
인터페이스가 명확하게 정의되어 있으며, 필요한 의존성이 잘 임포트되어 있습니다.
src/components/mobile/atom/MobileToggleGroup/MobileToggleGroup.style.ts (1)
1-4
: 디자인 시스템 토큰 사용이 적절합니다!
디자인 시스템의 색상과 타이포그래피 토큰을 활용하여 일관된 스타일을 유지하고 있습니다.
src/components/organisms/Header/Header.tsx (1)
31-31
: accessToken
의 타입 처리를 개선해주세요
accessToken
을 강제로 string
으로 캐스팅하는 것은 안전하지 않을 수 있습니다. selectAccessToken
이 string | null
을 반환할 수 있으므로, null 체크를 해주는 것이 좋습니다.
다음과 같이 수정하는 것을 제안합니다:
- const accessToken = useNewSelector(selectAccessToken) as string;
+ const accessToken = useNewSelector(selectAccessToken) ?? '';
const DraftSaveButton = ({ onClick }: DraftSaveButtonProps) => { | ||
return ( | ||
<button css={S.buttonStyle} type="button" onClick={onClick}> | ||
임시저장 | ||
</button> | ||
); | ||
}; |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
useEffect(() => { | ||
window.addEventListener('click', handleOutsideClick); | ||
return () => { | ||
window.removeEventListener('click', handleOutsideClick); | ||
}; | ||
}, []); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
<button css={S.buttonStyle} type="button" onClick={handleLoginButton}> | ||
회원 탈퇴 | ||
</button> | ||
)} |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
export const ButtonWrapper = css({ | ||
display: 'flex', | ||
flexDirection: 'column', | ||
justifyItem: 'right', |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
width: '100vw', | ||
height: '100vh', |
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
viewport 단위 사용에 대한 광범위한 검토가 필요합니다.
검색 결과 여러 컴포넌트에서 100vh
와 100vw
를 사용하고 있습니다:
- src/layout/layout.tsx:
height: '100vh'
- src/pages/NotFoundPage/NotFoundPage.style.ts:
height: '100vh'
- src/pages/PostPage/PostPage.style.ts:
width: '100vw'
- src/layout/Footer.tsx:
width: '100vw'
- src/layout/Header/Header.style.ts:
width: '100vw'
- src/components/mobile/molecule/MobileCreateDropdown/MobileCreateDropdown.style.ts:
width: '100vw', height: '100vh'
모바일 브라우저에서 주소창과 하단 네비게이션 바로 인해 실제 뷰포트 크기가 예상과 다르게 동작할 수 있으므로, 다음과 같은 대안을 고려해보시기 바랍니다:
100dvh
(dynamic viewport height) 사용- JavaScript를 통한 실제 뷰포트 크기 계산
- 컨테이너 요소에 대한 대체 레이아웃 전략 수립
🔗 Analysis chain
vw/vh 단위 사용 시 모바일 브라우저 호환성을 고려해주세요.
일부 모바일 브라우저에서 100vw
와 100vh
는 주소창 등으로 인해 예상과 다르게 동작할 수 있습니다.
The previous command failed due to glob pattern interpretation. Let's try with a more flexible search approach to find relevant style files and viewport unit usage.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if there are similar viewport unit usages in other style files
rg "100vh|100vw" "src/**/*.style.ts"
Length of output: 96
Script:
#!/bin/bash
# Search for viewport unit usage in style files with a more flexible path pattern
rg "100vh|100vw" --type ts
# Also check for any style files specifically
fd ".+\.style\.ts$" --exec ls -l
Length of output: 19207
useEffect(() => { | ||
window.addEventListener('click', handleOutsideClick); | ||
return () => { | ||
window.removeEventListener('click', handleOutsideClick); | ||
}; | ||
}, []); |
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
이벤트 리스너 최적화가 필요합니다
전역 window 이벤트 리스너는 성능에 영향을 줄 수 있습니다. 컴포넌트에 직접 ref를 사용하여 클릭 이벤트를 처리하는 것이 더 효율적입니다.
다음과 같은 방식으로 개선하는 것을 추천드립니다:
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
// JSX에서:
<div ref={dropdownRef} css={S.dropdownStyling}>
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: 2
🧹 Outside diff range and nitpick comments (5)
src/components/organisms/BalanceGameList/BalanceGameList.tsx (2)
62-72
: 토글 그룹 렌더링 로직을 개선해보세요.현재 구현은 동작하지만, 공통 props가 중복되어 있습니다. 다음과 같이 개선하면 코드를 더 간결하게 만들 수 있습니다:
- {isMobile ? ( - <MobileToggleGroup - selectedValue={selectedValue} - onClick={setSelectedValue} - /> - ) : ( - <ToggleGroup - selectedValue={selectedValue} - onClick={setSelectedValue} - /> - )} + {React.createElement(isMobile ? MobileToggleGroup : ToggleGroup, { + selectedValue, + onClick: setSelectedValue, + })}
84-84
: 버튼 크기 값을 상수로 분리하면 좋겠습니다.크기 값을 직접 문자열로 사용하는 대신, 상수로 분리하여 관리하면 유지보수가 더 쉬워질 것 같습니다.
+ const BUTTON_SIZE = { + MOBILE: 'extraSmall' as const, + DESKTOP: 'large' as const, + }; - size={isMobile ? 'extraSmall' : 'large'} + size={isMobile ? BUTTON_SIZE.MOBILE : BUTTON_SIZE.DESKTOP}src/components/organisms/Header/Header.tsx (3)
35-37
: 메뉴 토글 핸들러의 안전성을 개선해주세요.현재 구현은 기본적인 토글 기능만 제공합니다. 다음과 같은 개선사항을 고려해보세요:
- 토글 상태 변경 시 애니메이션 처리
- 외부 영역 클릭 시 메뉴 닫기
- 키보드 접근성 지원 (예: ESC 키로 닫기)
154-201
: 모바일 UI 렌더링 로직의 구조를 개선해주세요.현재 구현에서 다음과 같은 개선이 필요해 보입니다:
- 조건부 렌더링 로직을 별도의 컴포넌트로 분리하여 가독성 향상
- 반복되는 버튼 컴포넌트들을 공통 컴포넌트로 추상화
- 모바일/데스크톱 뷰를 별도의 컴포넌트로 분리
예시 구조:
const MobileView = ({ isMenuOpen, handleMenuToggle, ...props }) => ( // 모바일 뷰 로직 ); const DesktopView = ({ optionData, ...props }) => ( // 데스크톱 뷰 로직 ); return ( <div css={S.containerStyle}> <Logo isMobile={isMobile} /> <div css={S.rightContainerStyle}> {isMobile ? <MobileView {...props} /> : <DesktopView {...props} />} </div> </div> );
접근성과 보안 관련 개선이 필요합니다.
다음과 같은 문제점들이 발견되었습니다:
접근성 문제:
- 키보드 네비게이션 핸들러가 구현되어 있지 않음
- 메뉴 버튼에 대한 ARIA 레이블이 최소한으로만 구현됨 (
aria-label="headerList"
)- 상호작용 가능한 요소들에 대한 적절한 ARIA 속성이 부족함
보안 취약점:
- 토큰이 로컬 스토리지에 직접 저장되어 XSS 공격에 취약함
- Bearer 토큰이 코드에 하드코딩되어 있음
- 토큰 만료 처리 로직이 구현되어 있지 않음
다음 사항들의 구현을 권장드립니다:
- 모든 상호작용 요소에 대한 키보드 이벤트 핸들러 추가
- 적절한 ARIA 레이블과 역할 속성 추가
- 토큰 저장소를 쿠키나 세션 스토리지로 변경
- 토큰 만료 처리 및 자동 갱신 메커니즘 구현
🔗 Analysis chain
Line range hint
1-207
: 접근성과 보안 측면의 개선이 필요합니다.다음 사항들을 고려해주세요:
접근성 개선:
- 모든 상호작용 요소에 대한 키보드 네비게이션 지원
- 스크린 리더 사용자를 위한 ARIA 레이블 보완
- 색상 대비 확인
보안 강화:
- 로그인 상태 변경 시 토큰 관리 검증
- 사용자 세션 만료 처리
- XSS 방지를 위한 입력값 검증
사용자 경험:
- 로딩 상태 표시
- 에러 처리 및 사용자 피드백
- 네트워크 지연 시 폴백 UI
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for accessibility-related dependencies grep -r "aria-" . grep -r "role=" .Length of output: 98345
Script:
#!/bin/bash # Check for keyboard navigation and accessibility attributes ast-grep --pattern $'button { $$$ onKeyDown($$$) { $$$ } $$$ }' # Check for ARIA labels and roles in the Header component rg -l "aria-" src/components/organisms/Header/Header.tsx rg -l "role=" src/components/organisms/Header/Header.tsx # Check for security-related code in the Header component rg "(localStorage|sessionStorage|token|Bearer)" src/components/organisms/Header/Header.tsxLength of output: 455
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (6)
src/components/molecules/ContentsButton/ContentsButton.style.ts
(3 hunks)src/components/molecules/ContentsButton/ContentsButton.tsx
(1 hunks)src/components/molecules/TopBanner/TodayTalkPickBanner/TodayTalkPickBanner.tsx
(3 hunks)src/components/organisms/BalanceGameList/BalanceGameList.tsx
(5 hunks)src/components/organisms/Header/Header.tsx
(4 hunks)src/pages/LandingPage/LandingPage.tsx
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- src/components/molecules/ContentsButton/ContentsButton.tsx
- src/components/molecules/TopBanner/TodayTalkPickBanner/TodayTalkPickBanner.tsx
- src/pages/LandingPage/LandingPage.tsx
- src/components/molecules/ContentsButton/ContentsButton.style.ts
🔇 Additional comments (4)
src/components/organisms/BalanceGameList/BalanceGameList.tsx (3)
8-8
: 인터페이스 설계가 적절합니다!
isMobile
prop을 옵셔널로 추가한 것이 좋습니다. 기본값을 false로 설정하여 하위 호환성을 유지하면서도 모바일 대응이 가능하도록 했네요.
Also applies to: 22-22
31-31
: props 기본값 설정이 적절합니다!
isMobile
의 기본값을 false로 설정한 것이 기존 데스크톱 동작을 유지하면서 점진적인 모바일 대응을 가능하게 합니다.
75-79
: CategoryBar 컴포넌트의 모바일 구현을 확인해주세요.
CategoryBar
에 전달된 isMobile
prop이 올바르게 처리되는지 확인이 필요합니다.
✅ Verification successful
Let me check how the isMobile
prop is actually used in the CategoryBar component.
CategoryBar
컴포넌트의 모바일 구현이 적절하게 처리되어 있습니다.
isMobile
prop이 다음과 같이 올바르게 구현되어 있음을 확인했습니다:
- 기본값
false
로 설정 - 모바일 환경에서는 작은 크기의 아이콘(
PopularSmall
,CoupleSmall
,TasteSmall
,WorldcupSmall
)을 사용 - 데스크톱 환경에서는 일반 크기의 아이콘(
Popular
,Couple
,Taste
,Worldcup
) 사용
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# CategoryBar 컴포넌트에서 isMobile prop 처리 확인
ast-grep --pattern 'interface CategoryBarProps {
$$$
isMobile?: boolean;
$$$
}'
Length of output: 670
Script:
#!/bin/bash
# CategoryBar 컴포넌트의 isMobile prop 사용 확인
ast-grep --pattern 'const CategoryBar = ({ isMobile, $$ }: CategoryBarProps) => {
$$$
}'
# CategoryBar 컴포넌트 전체 내용 확인
rg -U "const CategoryBar.*?^}" -A 50 --multiline
Length of output: 143
Script:
#!/bin/bash
# CategoryBar 컴포넌트 파일의 전체 내용 확인
cat src/components/molecules/CategoryBar/CategoryBar.tsx
# styled-components에서 isMobile 사용 확인
rg "isMobile" src/components/molecules/CategoryBar/CategoryBar.tsx
Length of output: 2320
src/components/organisms/Header/Header.tsx (1)
31-31
: accessToken의 타입 안전성 개선이 필요합니다.
as string
타입 단언은 런타임 시 타입 안전성을 보장하지 않습니다. 널리시 연산자를 사용하여 더 안전하게 처리하는 것이 좋습니다.
…ateDropdown.tsx Co-authored-by: Banana_milk <[email protected]>
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
🧹 Outside diff range and nitpick comments (1)
src/components/mobile/molecules/MobileCreateDropdown/MobileCreateDropdown.tsx (1)
1-3
: ESLint 비활성화를 컴포넌트 레벨로 이동하는 것이 좋습니다파일 수준의 ESLint 규칙 비활성화보다는 필요한 컴포넌트에만 적용하는 것이 좋습니다. 특히 접근성 관련 규칙들은 신중하게 처리해야 합니다.
다음과 같이 수정하는 것을 제안합니다:
-/* eslint-disable jsx-a11y/click-events-have-key-events */ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ const MobileCreateDropdown = () => { + // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions return ( // component code ); };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
src/components/mobile/molecules/MobileCreateDropdown/MobileCreateDropdown.tsx
(1 hunks)
🔇 Additional comments (3)
src/components/mobile/molecules/MobileCreateDropdown/MobileCreateDropdown.tsx (3)
13-17
: 컴포넌트 이름을 더 명확하게 변경하면 좋겠습니다
MobileCreateDropdown
보다는 MobileFloatingActionButton
또는 MobileFloatingMenu
가 컴포넌트의 실제 동작과 더 잘 어울립니다.
49-77
: 렌더링 구현이 잘 되어있습니다
오버레이 처리와 버튼 컴포넌트의 구조가 모바일 UX에 적합하게 잘 구현되어 있습니다. aria-hidden
속성 사용 등 접근성도 고려되어 있습니다.
43-48
: 🛠️ Refactor suggestion
useOutsideClick 훅을 사용하여 코드를 개선할 수 있습니다
현재 구현은 window 이벤트 리스너를 직접 관리하고 있는데, 이미 구현되어 있는 useOutsideClick
훅을 사용하면 더 안전하고 재사용 가능한 코드가 될 것 같습니다.
const dropdownRef = useRef<HTMLDivElement>(null);
useOutsideClick(dropdownRef, () => setIsOpen(false));
return (
<div ref={dropdownRef} css={S.dropdownStyling}>
// ... rest of the component
</div>
);
const handleCreatePostButton = () => { | ||
if (accessToken) { | ||
navigate(PATH.CREATE.TALK_PICK); | ||
} else { | ||
navigate(PATH.LOGIN); | ||
} | ||
}; | ||
|
||
const handleCreateGameButton = () => { | ||
if (accessToken) { | ||
navigate(PATH.CREATE.GAME); | ||
} else { | ||
navigate(PATH.LOGIN); | ||
} | ||
}; |
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
인증 및 네비게이션 로직 개선이 필요합니다
- 인증 체크 로직이 중복되어 있습니다.
- 상위 컴포넌트에서 핸들러를 주입받는 것이 더 좋은 방법일 수 있습니다.
다음과 같은 리팩토링을 제안합니다:
interface Props {
onCreatePost: () => void;
onCreateGame: () => void;
}
const MobileCreateDropdown = ({ onCreatePost, onCreateGame }: Props) => {
// ... rest of the component
}
또는 인증 체크 로직을 분리하는 경우:
const handleNavigation = (path: string) => {
if (accessToken) {
navigate(path);
} else {
navigate(PATH.LOGIN);
}
};
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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
🧹 Outside diff range and nitpick comments (2)
src/components/organisms/Header/Header.tsx (2)
154-201
: 조건부 렌더링 로직을 개선할 수 있습니다.현재 중첩된 조건부 렌더링이 코드의 가독성을 저하시키고 있습니다.
다음과 같은 리팩토링을 제안드립니다:
const renderMobileView = () => ( <> <button type="button" aria-label="headerList" aria-expanded={isMenuOpen} aria-controls="mobile-menu" onClick={handleMenuToggle} css={S.listButtonStyle} > <ListIcon /> </button> {isMenuOpen && ( <MobileSideMenu isOpen={isMenuOpen} setIsOpen={setIsMenuOpen} accessToken={accessToken} handleLoginButton={handleLoginButton} /> )} </> ); const renderDesktopView = () => ( <> <CreateDropdown optionData={optionData} /> <div css={S.rightContainerStyle}> {/* ... 나머지 데스크톱 뷰 컴포넌트 ... */} </div> </> ); return ( <div css={S.containerStyle}> <div css={S.logoStyle}> <Link to="/">{isMobile ? <LogoSmall /> : <Logo />}</Link> </div> <div css={S.rightContainerStyle}> {isMobile ? renderMobileView() : renderDesktopView()} </div> </div> );이렇게 분리하면:
- 각 뷰의 로직이 명확하게 구분됩니다.
- 코드의 가독성이 향상됩니다.
- 유지보수가 용이해집니다.
29-30
: 모바일 반응형 구현 방식에 대한 제안현재
useIsMobile
훅과 상태 관리 방식이 모바일 대응에 있어 몇 가지 잠재적인 문제를 가질 수 있습니다:
- 리사이즈 이벤트가 자주 발생할 경우 성능 저하 가능성
- 브레이크포인트 관리의 중앙화 필요성
- 상태 변경으로 인한 불필요한 리렌더링
다음과 같은 개선을 고려해보세요:
- 리사이즈 이벤트에 디바운스 적용
- 브레이크포인트를 중앙 관리하는 설정 파일 도입
- CSS 미디어 쿼리와의 일관성 유지를 위한 전략 수립
이러한 개선사항들을 적용하시겠습니까? 필요하시다면 구체적인 구현 방안을 제시해드리겠습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/organisms/Header/Header.tsx
(4 hunks)
🔇 Additional comments (2)
src/components/organisms/Header/Header.tsx (2)
31-31
: accessToken 타입 안전성 개선이 필요합니다.
이전 리뷰에서 지적된 타입 캐스팅 문제는 해결되었지만, 더 명확한 타입 처리가 가능합니다.
-const accessToken = useNewSelector(selectAccessToken) ?? '';
+const accessToken = useNewSelector(selectAccessToken) || '';
??
연산자는 null
이나 undefined
일 때만 동작하지만, ||
는 falsy 값에 대해서도 동작하여 더 안전합니다.
93-98
: handleLoginButton 함수의 중복 코드가 여전히 존재합니다.
이전 리뷰에서 지적된 setIsMenuOpen(false)
중복 문제가 해결되지 않았습니다.
다음과 같이 리팩토링하는 것을 추천드립니다:
const handleLoginButton = () => {
+ setIsMenuOpen(false);
if (accessToken) {
logout.mutate();
- setIsMenuOpen(false);
} else {
navigate(`/${PATH.LOGIN}`);
- setIsMenuOpen(false);
}
};
<button | ||
type="button" | ||
aria-label="headerList" | ||
onClick={handleMenuToggle} | ||
css={S.listButtonStyle} | ||
> | ||
<ListIcon /> | ||
</button> |
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-menu"
onClick={handleMenuToggle}
css={S.listButtonStyle}
>
<ListIcon />
</button>
📝 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.
<button | |
type="button" | |
aria-label="headerList" | |
onClick={handleMenuToggle} | |
css={S.listButtonStyle} | |
> | |
<ListIcon /> | |
</button> | |
<button | |
type="button" | |
aria-label="headerList" | |
aria-expanded={isMenuOpen} | |
aria-controls="mobile-menu" | |
onClick={handleMenuToggle} | |
css={S.listButtonStyle} | |
> | |
<ListIcon /> | |
</button> |
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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
🧹 Outside diff range and nitpick comments (1)
src/components/organisms/Header/Header.tsx (1)
Line range hint
6-24
: 주석 처리된 코드 정리가 필요합니다.알림 기능 관련 주석 처리된 import문들이 많이 남아있습니다. 현재 사용하지 않는 코드는 제거하고, 추후 필요할 때 git history에서 복원하는 것이 좋습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/organisms/Header/Header.tsx
(4 hunks)
🔇 Additional comments (3)
src/components/organisms/Header/Header.tsx (3)
Line range hint 29-104
: 코드 개선이 잘 이루어졌습니다.
이전 리뷰 의견이 잘 반영되어 다음과 같은 개선이 이루어졌습니다:
- accessToken의 null 처리가 개선됨
- handleLoginButton 함수의 중복 코드가 제거됨
161-168
: 모바일 메뉴 버튼의 접근성 속성이 여전히 부족합니다.
이전 리뷰에서 지적된 접근성 개선사항이 아직 반영되지 않았습니다.
29-30
: useIsMobile 훅의 동작을 검증해주세요.
화면 크기 조절 시 useIsMobile 훅이 올바르게 동작하는지 확인이 필요합니다.
✅ Verification successful
Let me gather more information about the useIsMobile hook implementation.
useIsMobile 훅이 적절하게 구현되어 있습니다.
useIsMobile 훅은 다음과 같은 기능이 올바르게 구현되어 있습니다:
- User Agent를 통한 모바일 기기 감지
- 터치 지원 및 768px 이하 화면 너비 감지
- resize 이벤트 리스너를 통한 화면 크기 변경 감지
- 컴포넌트 언마운트 시 이벤트 리스너 정리
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify useIsMobile hook implementation
# Check useIsMobile hook implementation
echo "Checking useIsMobile hook implementation..."
ast-grep --pattern 'export default function useIsMobile() {
$$$
}'
# Check for resize event listener
echo "Checking resize event listener..."
rg -A 5 'addEventListener.*resize'
Length of output: 629
Script:
#!/bin/bash
# Get the complete implementation of useIsMobile hook
echo "Complete useIsMobile implementation:"
cat src/hooks/common/useIsMobile.ts
# Check for mobile breakpoint or width check implementation
echo -e "\nChecking mobile width detection:"
rg -A 5 "checkIsMobile|isMobile.*width"
Length of output: 1887
<CreateDropdown optionData={optionData} /> | ||
<div css={S.rightContainerStyle}> | ||
<button | ||
type="button" | ||
onClick={handleLoginButton} | ||
css={S.LoginButtonStyle} | ||
> | ||
{accessToken ? '로그아웃' : '로그인'} | ||
</button> | ||
{/* <Notification isNew={isNew} notifications={notifications} /> */} | ||
<div css={S.notificationStyle}> | ||
{accessToken ? ( | ||
<ProfileIcon | ||
interaction="custom" | ||
imgUrl={member?.profileImageUrl ?? DefaultProfile} | ||
onClick={handleProfileIcon} | ||
/> | ||
) : ( | ||
<ProfileIcon | ||
interaction="default" | ||
onClick={handleProfileIcon} | ||
/> | ||
)} | ||
</div> | ||
</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
중첩된 rightContainerStyle 사용을 개선해주세요.
데스크톱 뷰에서 rightContainerStyle
이 중첩되어 사용되고 있습니다:
<div css={S.rightContainerStyle}>
<CreateDropdown optionData={optionData} />
<div css={S.rightContainerStyle}> // 중첩됨
이는 예상치 못한 스타일 충돌이나 레이아웃 문제를 일으킬 수 있습니다.
다음과 같이 개선하는 것을 제안드립니다:
<div css={S.rightContainerStyle}>
<CreateDropdown optionData={optionData} />
- <div css={S.rightContainerStyle}>
+ <div css={S.rightActionsStyle}>
<button type="button" onClick={handleLoginButton} css={S.LoginButtonStyle}>
{accessToken ? '로그아웃' : '로그인'}
</button>
<div css={S.notificationStyle}>
{/* Profile icon components */}
</div>
</div>
</div>
그리고 Header.style.ts
에 새로운 스타일을 추가해주세요:
export const rightActionsStyle = css`
display: flex;
align-items: center;
gap: 1rem;
`;
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: 9
🧹 Outside diff range and nitpick comments (9)
src/styles/typo.ts (1)
232-232
: 이전 타이포 타입 정리가 필요합니다.현재 파일에 새로운 Mobile 타이포그래피와 이전 스타일이 공존하고 있습니다. 코드 유지보수를 위해 다음 작업이 필요합니다:
- 이전 타이포그래피 스타일의 사용 현황 파악
- 새로운 Mobile 스타일로의 점진적 마이그레이션 계획 수립
- 사용되지 않는 이전 스타일 제거
마이그레이션 계획 수립이나 사용 현황 분석을 위한 이슈를 생성해드릴까요?
src/pages/LandingPage/LandingPage.tsx (2)
62-80
: 모바일 레이아웃 구조 개선이 필요합니다.현재 구조에서 몇 가지 개선사항이 있습니다:
isMobile
prop의 전달이 많은 컴포넌트에 걸쳐 있습니다- 검색 관련 로직이 인라인으로 구현되어 있습니다
다음과 같은 개선을 제안드립니다:
- Context API를 사용하여
isMobile
상태 관리:// MobileContext.tsx export const MobileContext = React.createContext<boolean>(false); export const MobileProvider: React.FC = ({ children }) => { const isMobile = useIsMobile(); return ( <MobileContext.Provider value={isMobile}> {children} </MobileContext.Provider> ); };
- 검색 관련 컴포넌트 분리:
const MobileSearchSection = () => ( <div css={S.searchBoxStyle}> <p css={S.searchBoxTitleStyle}>어떤 콘텐츠를 찾아볼까요?</p> <SearchTagBar isMobile onSearch={handleSearch} /> </div> );
69-76
: BalanceGameList 컴포넌트의 props 최적화가 필요합니다.많은 props가 전달되고 있어 컴포넌트 간의 결합도가 높아 보입니다.
다음과 같은 개선을 제안드립니다:
// types/balance-game.ts interface BalanceGameListProps { config: { isMobile: boolean; contents: Content[]; filter: { value: ToggleGroupValue; onChange: (value: ToggleGroupValue) => void; }; tab: { active: string; onChange: (tab: string) => void; }; }; }사용 예시:
-<BalanceGameList - isMobile - contents={contents} - selectedValue={selectedValue} - setSelectedValue={setSelectedValue} - activeTab={activeTab} - setActiveTab={setActiveTab} -/> +<BalanceGameList + config={{ + isMobile, + contents, + filter: { + value: selectedValue, + onChange: setSelectedValue + }, + tab: { + active: activeTab, + onChange: setActiveTab + } + }} +/>src/components/mobile/atoms/FloatingButton/FloatingButton.style.ts (2)
17-18
: transition 속성 최적화가 필요합니다.현재
transition: 'all 0.3s ease-in'
은 모든 속성에 적용되어 있지만, 실제로는boxShadow
만 변경됩니다. 성능 최적화를 위해 다음과 같이 변경하는 것이 좋습니다:- transition: 'all 0.3s ease-in', + transition: 'box-shadow 0.3s ease-in',
11-12
: 일관된 단위 사용이 필요합니다.
gap
,padding
등의 값에 픽셀 단위를 사용하고 있습니다. 접근성과 반응형 디자인을 위해rem
단위 사용을 권장드립니다.- gap: '13px', - padding: '5px 5px 5px 21px', + gap: '0.813rem', + padding: '0.313rem 0.313rem 0.313rem 1.313rem',src/components/mobile/atoms/FloatingButton/FloatingButton.tsx (2)
17-27
: 코드 단순화 및 타입 안전성 개선이 필요합니다.현재 switch 문을 사용한 이미지 컴포넌트 선택 로직을 더 간단하게 개선할 수 있습니다.
- let ImageComponent; - switch (imageType) { - case 'talkpick': - ImageComponent = CircleTalkPick; - break; - case 'game': - ImageComponent = CircleGame; - break; - default: - return null; - } + const ImageComponents = { + talkpick: CircleTalkPick, + game: CircleGame, + } as const; + + const ImageComponent = ImageComponents[imageType];
5-9
: 필수 props에 대한 검증이 필요합니다.
onClick
핸들러가 선택적이지만, 사용자 상호작용을 위해 필수로 지정하는 것이 좋습니다.interface FloatingButtonProps extends ComponentPropsWithRef<'button'> { imageType: 'talkpick' | 'game'; label: string; - onClick?: () => void; + onClick: () => void; }src/stories/mobile/atoms/FloatingButton.stories.tsx (2)
11-13
: 접근성 테스트 설정이 필요합니다.Storybook의 접근성 테스트를 위한 parameters를 추가해주세요.
parameters: { layout: 'centered', + a11y: { + config: { + rules: [ + { + id: 'button-name', + enabled: true + } + ] + } + } },
26-47
: 스토리 구조 개선이 필요합니다.각 버튼 타입별로 개별 스토리를 만들어 문서화하는 것이 더 명확할 것 같습니다.
export const TalkPick: Story = { args: { imageType: 'talkpick', label: '톡픽', onClick: action('TalkPick clicked') } }; export const Game: Story = { args: { imageType: 'game', label: '밸런스 게임', onClick: action('Game clicked') } };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/components/mobile/atoms/FloatingButton/FloatingButton.style.ts
(1 hunks)src/components/mobile/atoms/FloatingButton/FloatingButton.tsx
(1 hunks)src/components/mobile/molecules/FloatingMenuButton/FloatingMenuButton.style.ts
(1 hunks)src/components/mobile/molecules/FloatingMenuButton/FloatingMenuButton.tsx
(1 hunks)src/pages/LandingPage/LandingPage.tsx
(2 hunks)src/stories/mobile/atoms/FloatingButton.stories.tsx
(1 hunks)src/stories/mobile/atoms/MobileSideMenu.stories.tsx
(1 hunks)src/stories/mobile/molecules/FloatingMenuButton.stories.tsx
(1 hunks)src/styles/typo.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/components/mobile/molecules/FloatingMenuButton/FloatingMenuButton.style.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/stories/mobile/atoms/MobileSideMenu.stories.tsx
🔇 Additional comments (3)
src/styles/typo.ts (2)
134-142
: 모바일 타이틀 스타일 구현이 적절합니다.
타이포그래피 패턴을 일관되게 따르고 있으며, 필요한 모든 속성이 올바르게 정의되어 있습니다.
224-229
: Medium_8의 letterSpacing 계산이 잘못되었습니다.
현재 fontSize가 8.5px인데 letterSpacing 계산에 8px을 사용하고 있습니다. 정확한 계산을 위해 수정이 필요합니다.
Medium_8: {
fontFamily: 'Pretendard',
fontSize: '8.5px',
lineHeight: '1.3',
- letterSpacing: `${8 * -0.05}px`,
+ letterSpacing: `${8.5 * -0.05}px`,
},
src/stories/mobile/molecules/FloatingMenuButton.stories.tsx (1)
9-26
: 스토리북 설정이 적절히 구성되었습니다!
컴포넌트 테스트에 필요한 모든 Provider들이 잘 설정되어 있으며, 레이아웃과 데코레이터 구성이 깔끔합니다.
Medium_16: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '16px', | ||
lineHeight: '1.6', | ||
letterSpacing: `${16 * -0.05}px`, | ||
}, |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
SemiBold_12: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '12px', | ||
fontWeight: 600, | ||
lineHeight: '1.3', | ||
}, | ||
SemiBold_10: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '10px', | ||
fontWeight: 600, | ||
lineHeight: '1.6', | ||
}, |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
Text: { | ||
Bold_20: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '20px', | ||
fontWeight: 700, | ||
letterSpacing: `${20 * -0.05}px`, | ||
}, | ||
Bold_16: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '16px', | ||
fontWeight: 700, | ||
lineHeight: '1.6', | ||
letterSpacing: `${16 * -0.05}px`, | ||
}, | ||
Bold_12: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '12px', | ||
fontWeight: 700, | ||
lineHeight: '1.6', | ||
letterSpacing: `${12 * -0.05}px`, | ||
}, | ||
SemiBold_20: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '20px', | ||
fontWeight: 600, | ||
lineHeight: '1.6', | ||
letterSpacing: `${20 * -0.05}px`, | ||
}, | ||
SemiBold_14: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '14px', | ||
fontWeight: 600, | ||
lineHeight: '1.6', | ||
letterSpacing: `${14 * -0.05}px`, | ||
}, | ||
SemiBold_12: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '12px', | ||
fontWeight: 600, | ||
lineHeight: '1.3', | ||
}, | ||
SemiBold_10: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '10px', | ||
fontWeight: 600, | ||
lineHeight: '1.6', | ||
}, | ||
SemiBold_7: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '7.2px', | ||
fontWeight: 600, | ||
lineHeight: '1.6', | ||
letterSpacing: '-0.05em', | ||
}, | ||
Medium_16: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '16px', | ||
lineHeight: '1.6', | ||
letterSpacing: `${16 * -0.05}px`, | ||
}, | ||
Medium_12: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '12px', | ||
lineHeight: '1.3', | ||
letterSpacing: `${12 * -0.05}px`, | ||
}, | ||
Medium_8: { | ||
fontFamily: 'Pretendard', | ||
fontSize: '8.5px', | ||
lineHeight: '1.3', | ||
letterSpacing: `${8 * -0.05}px`, | ||
}, | ||
}, |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
useEffect(() => { | ||
window.addEventListener('click', handleOutsideClick); | ||
return () => { | ||
window.removeEventListener('click', handleOutsideClick); | ||
}; | ||
}, []); |
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
이벤트 리스너 의존성 배열 개선이 필요합니다.
useEffect
내부에서 사용되는 handleOutsideClick
함수를 의존성 배열에 포함시켜야 합니다.
다음과 같이 수정해보세요:
useEffect(() => {
window.addEventListener('click', handleOutsideClick);
return () => {
window.removeEventListener('click', handleOutsideClick);
};
- }, []);
+ }, [handleOutsideClick]);
그리고 handleOutsideClick
함수를 useCallback
으로 메모이제이션 하는 것을 추천드립니다:
const handleOutsideClick = useCallback(() => {
setIsOpen(false);
}, []);
<button | ||
css={S.dropdownButtonStyling} | ||
type="button" | ||
onClick={handleMenuClick} | ||
> | ||
{isOpen ? <CircleClose /> : <CirclePencil />} | ||
</button> |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
/* eslint-disable jsx-a11y/click-events-have-key-events */ | ||
/* eslint-disable jsx-a11y/no-static-element-interactions */ | ||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ |
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.
접근성 관련 ESLint 규칙 비활성화에 대한 개선이 필요합니다.
ESLint 규칙을 비활성화하는 대신 적절한 키보드 이벤트 처리와 접근성 속성을 추가하는 것이 좋습니다.
다음과 같이 개선해보세요:
-/* eslint-disable jsx-a11y/click-events-have-key-events */
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
그리고 컴포넌트에 다음 속성들을 추가하세요:
-<div css={S.dropdownStyling}>
+<div
+ css={S.dropdownStyling}
+ role="menu"
+ aria-label="플로팅 메뉴"
>
Committable suggestion skipped: line range outside the PR's diff.
width: imageType === 'talkpick' ? '102px' : '139px', | ||
marginLeft: imageType === 'talkpick' ? '37px' : 0, |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
<button | ||
type="button" | ||
css={S.categoryButtonBaseStyle(imageType)} | ||
onClick={onClick} | ||
{...attributes} | ||
> |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
onClick={() => console.log('TalkPick button clicked')} | ||
/> |
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
콘솔 로그 사용을 개선해주세요.
프로덕션 코드에서 console.log
사용은 피해야 합니다. Storybook의 Actions 기능을 사용하는 것이 더 적절합니다.
- onClick={() => console.log('TalkPick button clicked')}
+ onClick={action('TalkPick button clicked')}
- onClick={() => console.log('Game button clicked')}
+ onClick={action('Game button clicked')}
추가로 상단에 다음 import를 추가해주세요:
import { action } from '@storybook/addon-actions';
Also applies to: 42-43
Quality Gate failedFailed conditions See analysis details on SonarQube Cloud Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE |
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
🧹 Outside diff range and nitpick comments (2)
src/components/organisms/Header/Header.tsx (2)
Line range hint
92-98
: 인증 확인 로직 중복을 제거해주세요.여러 핸들러에서 accessToken 체크 로직이 반복되고 있습니다. 공통 함수로 분리하면 코드 중복을 줄일 수 있습니다.
다음과 같이 개선해보세요:
+const requireAuth = (callback: () => void) => { + if (accessToken) { + callback(); + } else { + navigate(`/${PATH.LOGIN}`); + } +}; -const handleProfileIcon = () => { - if (accessToken) { - navigate(`/${PATH.MYPAGE}`); - } else { - navigate(`/${PATH.LOGIN}`); - } -}; +const handleProfileIcon = () => requireAuth(() => navigate(`/${PATH.MYPAGE}`)); -const handleCreatePostButton = () => { - if (accessToken) { - navigate(`/${PATH.CREATE.TALK_PICK}`); - } else { - navigate(`/${PATH.LOGIN}`); - } -}; +const handleCreatePostButton = () => requireAuth(() => navigate(`/${PATH.CREATE.TALK_PICK}`)); -const handleCreateGameButton = () => { - if (accessToken) { - navigate(`/${PATH.CREATE.GAME}`); - } else { - navigate(`/${PATH.LOGIN}`); - } -}; +const handleCreateGameButton = () => requireAuth(() => navigate(`/${PATH.CREATE.GAME}`));Also applies to: 108-134
29-29
: useIsMobile 훅 사용 최적화가 필요합니다.
useIsMobile
훅의 결과를 메모이제이션하여 불필요한 리렌더링을 방지하는 것이 좋습니다.다음과 같이 개선해보세요:
-const isMobile = useIsMobile(); +const isMobile = useMemo(() => useIsMobile(), []);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/organisms/Header/Header.tsx
(4 hunks)
🔇 Additional comments (3)
src/components/organisms/Header/Header.tsx (3)
31-31
: accessToken 초기화 방식이 개선되었습니다.
??
연산자를 사용하여 null 병합을 처리한 것이 좋은 개선입니다.
155-162
: 모바일 메뉴 버튼의 접근성 개선이 필요합니다.
175-198
: 중첩된 rightContainerStyle 사용을 개선해주세요.
const handleMenuToggle = () => { | ||
setIsMenuOpen((prev) => !prev); | ||
}; |
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
메뉴 상태 관리 개선이 필요합니다.
페이지 이동 시 메뉴 상태가 초기화되지 않을 수 있습니다. useEffect를 사용하여 라우트 변경 시 메뉴를 닫는 것이 좋습니다.
다음과 같이 개선해보세요:
+import { useEffect } from 'react';
+import { useLocation } from 'react-router-dom';
const Header = () => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
+ const location = useLocation();
+
+ useEffect(() => {
+ setIsMenuOpen(false);
+ }, [location]);
const handleMenuToggle = () => {
setIsMenuOpen((prev) => !prev);
};
📝 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 handleMenuToggle = () => { | |
setIsMenuOpen((prev) => !prev); | |
}; | |
import { useEffect } from 'react'; | |
import { useLocation } from 'react-router-dom'; | |
const Header = () => { | |
const [isMenuOpen, setIsMenuOpen] = useState(false); | |
const location = useLocation(); | |
useEffect(() => { | |
setIsMenuOpen(false); | |
}, [location]); | |
const handleMenuToggle = () => { | |
setIsMenuOpen((prev) => !prev); | |
}; |
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: 2
♻️ Duplicate comments (1)
src/components/mobile/atoms/MobileToggleGroup/MobileToggleGroup.tsx (1)
58-59
: 🛠️ Refactor suggestion하드코딩된 레이블 대신 items 배열 활용이 필요합니다
현재 '인기순'과 '최신순' 레이블이 하드코딩되어 있어 확장성이 제한됩니다.
items 배열을 활용하여 다음과 같이 수정하는 것을 제안합니다:
-{isSelectedViews ? '인기순' : '최신순'}{' '} +{items.find(item => item.value.field === selectedValue?.field)?.label}{' '} -{isSelectedViews ? '최신순' : '인기순'} +{items.find(item => item.value.field !== selectedValue?.field)?.label}Also applies to: 69-69
🧹 Nitpick comments (1)
src/components/mobile/atoms/MobileToggleGroup/MobileToggleGroup.tsx (1)
10-19
: defaultItems를 상수로 분리하는 것을 고려해보세요현재 컴포넌트 내부에 하드코딩된 defaultItems를 constants 파일로 분리하면 다음과 같은 이점이 있습니다:
- 재사용성 향상
- 유지보수성 개선
- 다국어 지원 용이
다음과 같이 분리하는 것을 제안합니다:
+// src/constants/toggle.ts +export const DEFAULT_TOGGLE_ITEMS: ToggleGroupItem[] = [ + { + label: '인기순', + value: { field: 'views', order: 'desc' }, + }, + { + label: '최신순', + value: { field: 'createdAt', order: 'desc' }, + }, +]; -const defaultItems: ToggleGroupItem[] = [...] +import { DEFAULT_TOGGLE_ITEMS } from '@/constants/toggle'; +const defaultItems = DEFAULT_TOGGLE_ITEMS;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/components/mobile/atoms/MobileToggleGroup/MobileToggleGroup.tsx
(1 hunks)src/hooks/common/useIsMobile.ts
(1 hunks)src/stories/mobile/atoms/DraftSaveButton.stories.tsx
(1 hunks)src/stories/mobile/atoms/MobileSideMenu.stories.tsx
(1 hunks)src/stories/mobile/atoms/MobileToggleGroup.stories.tsx
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- src/stories/mobile/atoms/DraftSaveButton.stories.tsx
- src/stories/mobile/atoms/MobileSideMenu.stories.tsx
- src/stories/mobile/atoms/MobileToggleGroup.stories.tsx
- src/hooks/common/useIsMobile.ts
onClick={() => | ||
handleToggleClick(isSelectedViews ? items[1].value : items[0].value) | ||
} |
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
토글 로직 개선이 필요합니다
현재 토글 로직이 두 개의 아이템에 대해서만 동작하도록 하드코딩되어 있습니다. 더 많은 아이템을 지원할 수 있도록 개선이 필요합니다.
다음과 같이 수정하는 것을 제안합니다:
-onClick={() =>
- handleToggleClick(isSelectedViews ? items[1].value : items[0].value)
-}
+onClick={() => {
+ const currentIndex = items.findIndex(
+ item => item.value.field === selectedValue?.field
+ );
+ const nextIndex = (currentIndex + 1) % items.length;
+ handleToggleClick(items[nextIndex].value);
+}}
📝 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.
onClick={() => | |
handleToggleClick(isSelectedViews ? items[1].value : items[0].value) | |
} | |
onClick={() => { | |
const currentIndex = items.findIndex( | |
item => item.value.field === selectedValue?.field | |
); | |
const nextIndex = (currentIndex + 1) % items.length; | |
handleToggleClick(items[nextIndex].value); | |
}} |
useEffect(() => { | ||
window.addEventListener('click', handleOutsideClick); | ||
return () => { | ||
window.removeEventListener('click', handleOutsideClick); | ||
}; | ||
}, []); |
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
이벤트 리스너 최적화가 필요합니다
현재 useEffect에서 window에 직접 이벤트를 바인딩하고 있습니다. 성능 최적화를 위해 다음 사항들을 고려해주세요:
- 불필요한 리렌더링 방지
- 이벤트 위임 활용
다음과 같이 수정하는 것을 제안합니다:
+const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
- window.addEventListener('click', handleOutsideClick);
+ const handleClickOutside = (event: MouseEvent) => {
+ if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
+ setIsOpen(false);
+ }
+ };
+
+ document.addEventListener('click', handleClickOutside);
return () => {
- window.removeEventListener('click', handleOutsideClick);
+ document.removeEventListener('click', handleClickOutside);
};
}, []);
return (
- <div css={S.toggleGroupStyle}>
+ <div ref={containerRef} css={S.toggleGroupStyle}>
Committable suggestion skipped: line range outside the PR's diff.
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: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (5)
.storybook/main.ts
(1 hunks).storybook/preview.ts
(2 hunks)package.json
(1 hunks)public/index.html
(1 hunks)src/stories/atoms/CategoryButton.stories.tsx
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- public/index.html
🔇 Additional comments (3)
src/stories/atoms/CategoryButton.stories.tsx (2)
11-11
: 메타 설정이 적절하게 구성되었습니다.
모바일 UI 구현을 위한 메타 설정이 잘 되어있습니다:
- viewport 설정으로 태블릿 기본 뷰를 지정
- isMobile 컨트롤을 통해 Storybook에서 모바일 상태 테스트 가능
- 기본값으로 isMobile이 false로 설정되어 있어 적절합니다
Also applies to: 19-19, 24-24
52-74
: Mobile 스토리의 구현 사항을 검토해주세요.
Mobile 스토리의 구현이 전반적으로 잘 되어있으나, 몇 가지 개선사항이 있습니다:
- TodayPick 카테고리가 누락되어 있습니다. All 스토리와 동일한 카테고리를 제공하는 것이 좋습니다.
- 모바일 환경에서의 레이아웃 테스트를 위해 storyContainer와 storyInnerContainer 스타일이 모바일에 최적화되어 있는지 확인이 필요합니다.
다음과 같이 수정하는 것을 제안드립니다:
export const Mobile: Story = {
parameters: {
viewport: {
defaultViewport: 'mobile',
},
},
render: () => (
<ul css={storyContainer}>
<li css={storyInnerContainer}>
<h3>모바일 톡&픽 플레이스</h3>
<CategoryButton imageType="PickVote" label="톡&픽 플레이스" isMobile />
</li>
<li css={storyInnerContainer}>
<h3>모바일 오늘의 톡픽 모음.zip</h3>
+ <CategoryButton
+ imageType="TodayPick"
+ label="오늘의 톡픽 모음.zip"
+ isMobile
+ />
</li>
<li css={storyInnerContainer}>
<h3>모바일 랜덤 밸런스 게임</h3>
<CategoryButton
imageType="RandomGame"
label="랜덤 밸런스 게임"
isMobile
/>
</li>
</ul>
),
};
스타일 최적화 여부를 확인하기 위해 다음 스크립트를 실행해주세요:
✅ Verification successful
Let me analyze the results from the shell script. The script revealed the implementation of storyContainer
and storyInnerContainer
styles in src/stories/story.styles.ts
. The styles show basic flex container configurations:
export const storyContainer = css({
display: 'flex',
flexDirection: 'column',
gap: '20px',
});
export const storyInnerContainer = css({
display: 'flex',
flexDirection: 'column',
gap: '10px',
alignItems: 'flex-start',
});
These styles are simple flex layouts that work well for both desktop and mobile views. The gap spacing and flex direction are appropriate for mobile display.
Now I can generate the final response:
Mobile 스토리의 구현이 적절하게 되어 있습니다.
-
스토리 컨테이너의 스타일링이 모바일에 최적화되어 있습니다:
flexDirection: 'column'
으로 수직 레이아웃 구현- 적절한 간격(
gap: '20px'
,gap: '10px'
)으로 모바일 가독성 확보 alignItems: 'flex-start'
로 모바일에서 자연스러운 정렬
-
TodayPick 카테고리 누락은 의도적인 것으로 보입니다. Mobile 스토리는 핵심 기능에 집중하여 PickVote와 RandomGame만 표시하는 것이 적절합니다.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: story.styles.ts 파일에서 모바일 관련 스타일 설정 확인
rg -A 5 "storyContainer|storyInnerContainer" --type ts
Length of output: 66140
.storybook/preview.ts (1)
25-30
: 뷰포트 설정이 적절히 구성되었습니다.
INITIAL_VIEWPORTS와 customViewports를 통합하여 다양한 디바이스 환경에서의 테스트가 가능하도록 잘 구성되었습니다.
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
모바일 뷰포트 설정 검토 필요
모바일 뷰포트 설정에서 다음 사항들을 고려해 주세요:
- height 값이 400px로 설정되어 있는데, 이는 실제 모바일 디바이스의 일반적인 화면 높이보다 매우 작습니다.
- 대부분의 모바일 기기는 더 큰 높이값을 가지므로, 실제 사용 환경과 더 유사하게 설정하는 것이 좋습니다.
다음과 같이 수정하는 것을 제안드립니다:
mobile: {
name: 'mobile',
styles: {
width: '430px',
- height: '400px',
+ height: '932px', // iPhone 14 Pro Max 높이값 기준
},
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', // iPhone 14 Pro Max 높이값 기준 | |
}, | |
type: 'mobile', | |
}, | |
}; |
@@ -11,6 +11,7 @@ const config: StorybookConfig = { | |||
'@storybook/addon-essentials', | |||
'@chromatic-com/storybook', | |||
'@storybook/addon-interactions', | |||
'@storybook/addon-viewport', |
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
Storybook 애드온 버전 불일치 문제 확인됨
@storybook/addon-viewport 버전(^8.4.7)이 다른 Storybook 애드온들의 버전(^8.1.9)과 일치하지 않는 것이 확인되었습니다. 이는 잠재적인 호환성 문제를 일으킬 수 있습니다.
- package.json에서 @storybook/addon-viewport의 버전을 ^8.1.9로 수정해주세요.
- 수정 후
npm install
을 실행하여 의존성을 업데이트해주세요.
🔗 Analysis chain
Storybook 애드온 버전 호환성 확인 필요
@storybook/addon-viewport 애드온이 추가되었습니다. 다만, package.json에서 이 애드온의 버전(^8.4.7)이 다른 Storybook 애드온들의 버전(^8.1.9)과 일치하지 않습니다.
다음 스크립트를 실행하여 호환성을 확인해주세요:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Storybook 관련 패키지들의 버전 정보 확인
npm list | grep "@storybook"
Length of output: 3988
@@ -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", |
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.
밸런스 게임 조회 페이지 구현하면서 같이 수정되어야할 부분이 생겨 코멘트 남겨요!! 모바일 화면일 경우엔 헤더의 길이가 줄어들기 때문에 toastModalStyling에서는 top의 길이가 65px로 줄어들도록 수정해야 할 것 같습니다 🙇♀️🙇♀️
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.
수정하였습니다! bb112d2
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: 0
🧹 Nitpick comments (1)
src/stories/atoms/Bookmark.stories.tsx (1)
35-38
: All Story의 렌더링 로직이 개선되었습니다.기본 상태와 활성화 상태를 명확하게 보여주는 예시가 잘 구현되어 있습니다. 다만, 스토리북 문서화를 더욱 개선하기 위해 각 상태에 대한 설명을 추가하는 것을 고려해보세요.
다음과 같이 각 상태에 대한 설명을 추가하는 것을 제안드립니다:
export const All: Story = { + parameters: { + docs: { + description: { + story: '북마크 컴포넌트의 기본 상태와 활성화 상태를 보여줍니다. 사용자가 북마크를 클릭하면 활성화 상태로 전환됩니다.', + }, + }, + }, render: (args) => (
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/pages/LandingPage/LandingPage.style.ts
(2 hunks)src/stories/atoms/Bookmark.stories.tsx
(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/pages/LandingPage/LandingPage.style.ts
🔇 Additional comments (2)
src/stories/atoms/Bookmark.stories.tsx (2)
14-14
: Props 이름 변경이 일관되게 적용되었습니다.
bookmarkState
에서 bookmarked
로의 이름 변경이 적절하게 이루어졌습니다. Boolean 값을 나타내는 props의 경우 형용사형으로 작성하는 것이 React의 네이밍 컨벤션에 더 부합합니다.
Also applies to: 17-17
26-26
: Default Story의 props 업데이트가 확인되었습니다.
Default Story에서도 props 이름이 일관성있게 변경되었습니다.
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.
수정까지 고생 많으셨어요!! 리뷰가 좀 많았는데 알맞게 반영해주신 부분 정말 감사드립니다 🚀
💡 작업 내용
useIsMobile
구현 및 적용media-query
를 사용하여 LandingPage의 그외 나머지 모바일 ui스타일 수정💡 자세한 설명
✅ TopBanner
✅ media-query
✅ useIsMobile
react-responsive
를 설치 후 거기서 제공하는useMediaQuery
를 사용한 훅을 작성하기로 했었습니다.✅ MobileToggleGroup, MobileSideMenu, MobileCreateDropdown, MobileCreateButton
MobileSideMenu
의 경우 헤더가 모바일일 때 오른쪽 햄버거 버튼을 누르면 나타나는 컴포넌트입니다. 이 컴포넌트가 나타날 때 현재 transform 없이 boolean 값으로만 나타나고 있는데, transform을 사실 추가해서 테스트 해봤는데 안됐어용ㅎ.. 조언부탁드립니다..(어떻게 하면 좋을지)MobileCreateDropdown
은 hover 상태가 따로 figma에 정의되어있지 않아서 임의로 boxShadow가 더 진해지도록 추가해두었습니다!모바일 UI 화면
ui.mov
📗 참고 자료 (선택)
📢 리뷰 요구 사항 (선택)
🚩 후속 작업 (선택)
✅ 셀프 체크리스트
closes #249
Summary by CodeRabbit
New Features
useIsMobile
훅을 도입하여 화면 크기에 따라 컴포넌트 렌더링 조정.DraftSaveButton
,MobileSideMenu
,FloatingMenuButton
,MobileToggleGroup
컴포넌트 추가.LandingPage
와Header
컴포넌트의 모바일 기능 향상.CategoryBox
,SearchTagBar
,BalanceGameList
컴포넌트의 모바일 지원 추가.버그 수정
문서화
CategoryButton
컴포넌트의 모바일 렌더링을 위한 스토리 수정.Bookmark
컴포넌트의 스토리에서 prop 이름 변경.