-
Notifications
You must be signed in to change notification settings - Fork 1
Fix/121 QA(1,2차 통합) 후 나온 메인페이지, 체험 등록/수정 페이지 이슈 해결 #133
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
Conversation
Walkthrough이 변경 사항은 메인 페이지의 UI/UX 개선, 인기 체험 무한 스크롤, 드롭다운 커스텀 스타일링, 카테고리 필터 드래그 스크롤, 카드 타이틀 줄임 처리, 쿼리 무효화 로직, 헤더/푸터/프로필 드롭다운 스타일, 글로벌 유저 상태 동기화 등 다양한 컴포넌트와 스타일에 걸쳐 다수의 UI 및 UX 관련 수정을 포함합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CategoryFilter
participant ExperienceList
participant PopularExperiences
participant API
User->>CategoryFilter: 마우스 드래그로 스크롤 시도
CategoryFilter->>CategoryFilter: 스크롤 위치 조정, gradient 숨김
User->>ExperienceList: 정렬/카테고리 선택
ExperienceList->>Dropdown: 드롭다운 선택
Dropdown->>ExperienceList: 정렬 옵션 전달
User->>PopularExperiences: 스크롤하여 sentinel 노출
PopularExperiences->>API: getPopularExperiences(cursorId)
API-->>PopularExperiences: activities, cursorId, totalCount
PopularExperiences->>PopularExperiences: 데이터 누적 및 렌더링
User->>EditActivityForm: 체험 수정 제출
EditActivityForm->>useEditActivityForm: mutate
useEditActivityForm->>API: updateActivity
API-->>useEditActivityForm: 성공 응답
useEditActivityForm->>QueryClient: ['activity', id], ['experiences'], ['popularExperiences'] 쿼리 무효화
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🔇 Additional comments (1)
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. 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: 17
🔭 Outside diff range comments (2)
src/hooks/useMyPageQueries.ts (2)
131-136: 코드 중복을 제거하세요.
useUploadProfileImage함수에서 동일한 코드가 중복되어 있습니다.다음 중복 코드를 제거하세요:
setUser(updatedUser); queryClient.setQueryData(QUERY_KEYS.PROFILE, updatedUser); setLoading(false); setGlobalUser(updatedUser); // [추가] 헤더 상태(authStore)도 동기화 - queryClient.setQueryData(QUERY_KEYS.PROFILE, updatedUser); - - setLoading(false); alert('프로필 이미지가 성공적으로 업로드되었습니다!');
69-69: 사용자 경험 개선을 위해 알림 방식을 통일하세요.
alert()대신 toast 알림을 사용하는 것을 고려해보세요. 이미 헤더에서toastfrom 'sonner'를 사용하고 있어 일관성을 위해 동일한 방식을 적용하는 것이 좋겠습니다.- alert('프로필이 성공적으로 업데이트되었습니다!'); + toast.success('프로필이 성공적으로 업데이트되었습니다!');Also applies to: 76-76, 136-136, 142-142
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (21)
src/app/(with-header)/components/BannerSection.tsx(1 hunks)src/app/(with-header)/components/BasePage.tsx(2 hunks)src/app/(with-header)/components/CategoryFilter.tsx(2 hunks)src/app/(with-header)/components/ExperienceCard.tsx(2 hunks)src/app/(with-header)/components/ExperienceList.tsx(2 hunks)src/app/(with-header)/components/PopularCard.tsx(1 hunks)src/app/(with-header)/components/PopularExperiences.tsx(4 hunks)src/app/(with-header)/myactivity/[id]/components/EditActivityForm.tsx(1 hunks)src/app/(with-header)/myactivity/[id]/hooks/useEditActivityForm.ts(1 hunks)src/app/(with-header)/myactivity/components/CategoryInput.tsx(2 hunks)src/app/(with-header)/myactivity/components/ReservationForm.tsx(1 hunks)src/app/(with-header)/myactivity/components/ScheduleSelect.tsx(4 hunks)src/app/(with-header)/myactivity/hooks/useCreateActivityForm.ts(3 hunks)src/app/api/experiences/getPopularExperiences.ts(1 hunks)src/app/globals.css(1 hunks)src/components/Dropdown.tsx(4 hunks)src/components/Footer.tsx(1 hunks)src/components/Header.tsx(2 hunks)src/components/ProfileDropdown.tsx(1 hunks)src/hooks/useMyPageQueries.ts(1 hunks)src/types/dropdownTypes.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
src/hooks/useMyPageQueries.ts (1)
src/app/(with-header)/mypage/profile/page.tsx (1)
ProfilePage(14-183)
src/app/(with-header)/myactivity/components/ScheduleSelect.tsx (1)
src/app/(with-header)/myactivity/components/ScheduleSelectForm.tsx (3)
ScheduleSelectForm(17-60)ScheduleSelectFormProps(6-15)function(44-44)
src/app/api/experiences/getPopularExperiences.ts (3)
src/types/experienceListTypes.ts (2)
Experience(1-14)ExperienceResponse(16-20)src/apis/instance.ts (1)
instance(100-100)src/app/api/experiences/getExperiences.ts (1)
ExperienceResponse(11-15)
src/app/(with-header)/components/BasePage.tsx (3)
src/app/(with-header)/components/BannerSection.tsx (1)
BannerSection(9-39)src/app/(with-header)/components/ExperienceList.tsx (1)
ExperienceList(25-125)src/app/(with-header)/components/PopularExperiences.tsx (1)
PopularExperiences(16-119)
🪛 Biome (2.1.2)
src/components/ProfileDropdown.tsx
[error] 58-63: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.
Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.
(lint/a11y/useKeyWithClickEvents)
[error] 58-63: Static Elements should not be interactive.
To add interactivity such as a mouse or key event listener to a static element, give the element an appropriate role value.
(lint/a11y/noStaticElementInteractions)
src/app/(with-header)/components/CategoryFilter.tsx
[error] 64-74: Static Elements should not be interactive.
To add interactivity such as a mouse or key event listener to a static element, give the element an appropriate role value.
(lint/a11y/noStaticElementInteractions)
🔇 Additional comments (24)
src/app/(with-header)/components/BannerSection.tsx (1)
25-25: 반응형 디자인 개선을 승인합니다.고정 너비 클래스
w-220을 제거하고 반응형 클래스를 유지하는 것이 적절합니다. 이를 통해 다양한 화면 크기에서 더 나은 레이아웃 유연성을 제공합니다.src/app/(with-header)/myactivity/components/ReservationForm.tsx (1)
36-36: 레이아웃 일관성 개선을 승인합니다.수평 패딩을
px-16에서px-0으로 변경하여 활동 관련 폼 컴포넌트들 간의 일관성을 유지하는 것이 적절합니다.src/components/Header.tsx (1)
78-78: 반응형 간격 조정을 승인합니다.모바일에서
gap-8, 중간 화면 이상에서gap-24로 설정하여 화면 크기에 따른 적절한 간격을 제공합니다.src/hooks/useMyPageQueries.ts (1)
51-51: 전역 상태 동기화 개선을 승인합니다.헤더의 사용자 정보와 프로필 페이지의 상태를 동기화하는 것은 좋은 개선사항입니다. 이를 통해 닉네임 변경이 헤더에 실시간으로 반영됩니다.
Also applies to: 65-65, 87-87
src/components/Footer.tsx (2)
8-8: 간격 조정이 적절합니다모바일 버전에서 요소들 간의 간격을 12에서 25로 늘린 것은 가독성과 터치 인터페이스 사용성을 개선하는 좋은 변경입니다.
10-10: JSX에서 자체 종료 태그 형태를 사용하세요저작권 텍스트 업데이트는 좋습니다만, JSX에서는
<br />보다는<br />형태의 자체 종료 태그를 사용하는 것이 권장됩니다.-<div className='text-center md:text-left'>@NoSleepNoBugs<br />2025</div> +<div className='text-center md:text-left'>@NoSleepNoBugs<br />2025</div>Likely an incorrect or invalid review comment.
src/app/(with-header)/components/CategoryFilter.tsx (2)
6-6: 필요한 React hooks 가져오기가 적절합니다드래그 스크롤 기능 구현을 위해 필요한 hooks들을 적절히 가져왔습니다.
87-89: 그라데이션 오버레이 구현이 훌륭합니다사용자가 처음 상호작용할 때까지만 그라데이션을 보여주는 것은 스크롤 가능성을 시각적으로 알려주는 좋은 UX 패턴입니다.
src/app/(with-header)/components/PopularCard.tsx (1)
34-34: 텍스트 잘림 처리가 적절히 적용되었습니다
line-clamp-2-custom클래스를 추가하여 제목이 2줄을 초과할 때 적절히 잘리도록 처리한 것이 좋습니다. 카드 레이아웃의 일관성을 유지하는 데 도움이 됩니다.src/app/(with-header)/myactivity/[id]/components/EditActivityForm.tsx (1)
53-53: 패딩 조정이 적절합니다최외곽 컨테이너의 수평 패딩을 제거한 것은 레이아웃 일관성을 위한 적절한 조정으로 보입니다. 내부 컨테이너에서 여전히 적절한 패딩을 유지하고 있어 콘텐츠 가독성에는 문제가 없습니다.
src/app/(with-header)/myactivity/[id]/hooks/useEditActivityForm.ts (1)
159-160: 쿼리 캐시 무효화 로직이 올바르게 구현되었습니다.체험 수정 후 관련 리스트들이 실시간으로 업데이트되도록 하는 캐시 무효화가 적절히 추가되었습니다. 생성 폼과 일관된 패턴을 따르고 있어 좋습니다.
src/components/ProfileDropdown.tsx (1)
63-69: UI 스타일링 개선이 적절합니다.중앙 정렬과 둥근 모서리 추가로 일관된 UI 디자인을 구현했습니다.
src/app/(with-header)/components/ExperienceCard.tsx (3)
19-19: 호버 효과가 잘 구현되었습니다.카드에 부드러운 상승 애니메이션을 추가하여 사용자 인터랙션을 개선했습니다. PR 목표에 부합하는 모션 효과입니다.
25-25: 이미지 호버 효과가 적절합니다.이미지에 확대 효과를 추가하여 시각적 피드백을 향상시켰습니다.
35-35: 텍스트 스타일링 개선이 좋습니다.커스텀 line-clamp 클래스 사용으로 일관된 텍스트 줄임 처리를 구현했습니다. margin-bottom 사용도 적절합니다.
src/app/(with-header)/myactivity/hooks/useCreateActivityForm.ts (1)
60-61: 쿼리 캐시 무효화가 올바르게 구현되었습니다.체험 생성 후 관련 리스트들이 자동으로 업데이트되도록 하는 캐시 무효화 로직이 적절히 추가되었습니다. 수정 폼과 일관된 패턴을 따르고 있습니다.
src/components/Dropdown.tsx (3)
37-39: 컴포넌트 커스터마이징 옵션이 잘 추가되었습니다.드롭다운의 각 부분에 대한 스타일링 옵션을 제공하여 컴포넌트의 재사용성과 유연성을 크게 향상시켰습니다.
137-137: 새로운 className props 적용이 적절합니다.각 요소에 커스텀 클래스명을 올바르게 적용하여 외부에서 스타일을 세밀하게 제어할 수 있도록 했습니다.
Also applies to: 173-173, 193-193
198-206: 체크 아이콘 주석 처리가 합리적입니다.향후 사용을 위해 코드를 보존하면서 현재는 숨기는 방식이 적절합니다. 필요시 쉽게 복원할 수 있습니다.
src/app/(with-header)/myactivity/components/ScheduleSelect.tsx (1)
33-60: 반응형 레이아웃 개선 승인flex-wrap과 flex-1, min-w-0를 사용한 반응형 레이아웃 개선이 잘 적용되었습니다. 작은 화면에서도 입력 필드들이 적절히 배치될 것으로 보입니다.
src/app/api/experiences/getPopularExperiences.ts (1)
14-27: 커서 기반 페이지네이션 구현 승인무한 스크롤을 위한 커서 기반 페이지네이션이 잘 구현되었습니다. cursorId 파라미터의 옵셔널 처리도 적절합니다.
src/app/(with-header)/components/PopularExperiences.tsx (1)
20-39: 무한 스크롤 구현 승인React Query의 useInfiniteQuery를 활용한 무한 스크롤 구현이 매우 잘 되었습니다. 타입 정의와 페이지네이션 로직이 명확합니다.
src/app/(with-header)/components/ExperienceList.tsx (2)
47-47: 모바일 가독성을 위한 패딩 조정 👍
px-16 md:px-24 lg:px-0변경으로 작은 화면에서 좌우 여백이 좁아져 카드가 더 넉넉하게 보입니다. 디자인 의도와 일치하며 부작용도 없어 보입니다.
91-91: 브레이크포인트별 padding 값이 비선형입니다
pb-30 → md:pb-150 → lg:pb-100으로 중간 구간이 가장 크고, 더 큰 화면에서 다시 줄어듭니다. 디자인 시스템 의도라면 문제없지만, 오타일 가능성도 있으니 한번 더 확인 바랍니다.
| <motion.div | ||
| initial={{ opacity: 0, y: 20 }} | ||
| animate={{ opacity: 1, y: 0 }} | ||
| transition={{ duration: 0.6, ease: 'easeOut' }} | ||
| > | ||
| <BannerSection keyword={keyword} /> | ||
| </motion.div> | ||
|
|
||
| {isSearchMode ? ( | ||
| <ExperienceList keyword={keyword} isSearchMode /> | ||
| <motion.div | ||
| initial={{ opacity: 0, y: 20 }} | ||
| animate={{ opacity: 1, y: 0 }} | ||
| transition={{ duration: 0.6, delay: 0.2 }} | ||
| > | ||
| <ExperienceList keyword={keyword} isSearchMode /> | ||
| </motion.div> | ||
| ) : ( | ||
| <> | ||
| <PopularExperiences /> | ||
| <ExperienceList /> | ||
| <motion.div | ||
| initial={{ opacity: 0, y: 20 }} | ||
| animate={{ opacity: 1, y: 0 }} | ||
| transition={{ duration: 0.6, delay: 0.2 }} | ||
| > | ||
| <PopularExperiences /> | ||
| </motion.div> | ||
|
|
||
| <motion.div | ||
| initial={{ opacity: 0, y: 20 }} | ||
| animate={{ opacity: 1, y: 0 }} | ||
| transition={{ duration: 0.6, delay: 0.4 }} | ||
| > | ||
| <ExperienceList /> | ||
| </motion.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.
🧹 Nitpick (assertive)
애니메이션 구현 승인 및 접근성 개선 제안
framer-motion을 사용한 스태거드 애니메이션이 잘 구현되었습니다. 사용자 경험을 향상시키는 좋은 개선사항입니다.
다만, 접근성을 위해 prefers-reduced-motion 미디어 쿼리를 고려하는 것이 좋겠습니다.
+ const shouldReduceMotion = typeof window !== 'undefined' &&
+ window.matchMedia('(prefers-reduced-motion: reduce)').matches;
+
+ const animationProps = shouldReduceMotion ? {} : {
+ initial: { opacity: 0, y: 20 },
+ animate: { opacity: 1, y: 0 },
+ transition: { duration: 0.6, ease: 'easeOut' }
+ };
return (
<main>
<motion.div
- initial={{ opacity: 0, y: 20 }}
- animate={{ opacity: 1, y: 0 }}
- transition={{ duration: 0.6, ease: 'easeOut' }}
+ {...animationProps}
>📝 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.
| <motion.div | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| transition={{ duration: 0.6, ease: 'easeOut' }} | |
| > | |
| <BannerSection keyword={keyword} /> | |
| </motion.div> | |
| {isSearchMode ? ( | |
| <ExperienceList keyword={keyword} isSearchMode /> | |
| <motion.div | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| transition={{ duration: 0.6, delay: 0.2 }} | |
| > | |
| <ExperienceList keyword={keyword} isSearchMode /> | |
| </motion.div> | |
| ) : ( | |
| <> | |
| <PopularExperiences /> | |
| <ExperienceList /> | |
| <motion.div | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| transition={{ duration: 0.6, delay: 0.2 }} | |
| > | |
| <PopularExperiences /> | |
| </motion.div> | |
| <motion.div | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| transition={{ duration: 0.6, delay: 0.4 }} | |
| > | |
| <ExperienceList /> | |
| </motion.div> | |
| // Inside your BasePage component, before the return: | |
| const shouldReduceMotion = | |
| typeof window !== 'undefined' && | |
| window.matchMedia('(prefers-reduced-motion: reduce)').matches; | |
| const animationProps = shouldReduceMotion | |
| ? {} | |
| : { | |
| initial: { opacity: 0, y: 20 }, | |
| animate: { opacity: 1, y: 0 }, | |
| transition: { duration: 0.6, ease: 'easeOut' }, | |
| }; | |
| return ( | |
| <main> | |
| <motion.div {...animationProps}> | |
| <BannerSection keyword={keyword} /> | |
| </motion.div> | |
| {isSearchMode ? ( | |
| <motion.div | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| transition={{ duration: 0.6, delay: 0.2 }} | |
| > | |
| <ExperienceList keyword={keyword} isSearchMode /> | |
| </motion.div> | |
| ) : ( | |
| <> | |
| <motion.div | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| transition={{ duration: 0.6, delay: 0.2 }} | |
| > | |
| <PopularExperiences /> | |
| </motion.div> | |
| <motion.div | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| transition={{ duration: 0.6, delay: 0.4 }} | |
| > | |
| <ExperienceList /> | |
| </motion.div> | |
| </> | |
| )} | |
| </main> | |
| ); |
🤖 Prompt for AI Agents
In src/app/(with-header)/components/BasePage.tsx around lines 17 to 49, the
staggered animations using framer-motion are implemented well but lack
consideration for users who prefer reduced motion. To fix this, detect the
user's `prefers-reduced-motion` setting using a media query and conditionally
disable or simplify the animations accordingly. This can be done by checking the
media query in a React hook or utility and adjusting the motion props to avoid
motion when the preference is set.
| const scrollRef = useRef<HTMLDivElement | null>(null); | ||
| const [hasInteracted, setHasInteracted] = useState(false); | ||
| const [isDragging, setIsDragging] = useState(false); | ||
| const startX = useRef(0); | ||
| const scrollLeft = useRef(0); | ||
|
|
||
| const handleFirstInteraction = () => { | ||
| if (!hasInteracted) setHasInteracted(true); | ||
| }; | ||
|
|
||
| const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => { | ||
| setIsDragging(true); | ||
| startX.current = e.pageX - (scrollRef.current?.offsetLeft ?? 0); | ||
| scrollLeft.current = scrollRef.current?.scrollLeft ?? 0; | ||
| handleFirstInteraction(); | ||
| }; | ||
|
|
||
| const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => { | ||
| if (!isDragging || !scrollRef.current) return; | ||
| e.preventDefault(); | ||
| const x = e.pageX - scrollRef.current.offsetLeft; | ||
| const walk = (x - startX.current) * 1; | ||
| scrollRef.current.scrollLeft = scrollLeft.current - walk; | ||
| handleFirstInteraction(); | ||
| }; | ||
|
|
||
| const handleMouseUpOrLeave = () => { | ||
| setIsDragging(false); | ||
| }; | ||
|
|
||
| const handleScroll = () => { | ||
| handleFirstInteraction(); | ||
| }; | ||
|
|
||
| useEffect(() => { | ||
| const el = scrollRef.current; | ||
| if (!el) return; | ||
| el.addEventListener('scroll', handleScroll); | ||
| return () => el.removeEventListener('scroll', handleScroll); | ||
| }, []); |
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
드래그 스크롤 구현이 잘 되었지만 접근성을 개선하세요
드래그 스크롤 기능이 잘 구현되었고, 사용자 상호작용에 따른 그라데이션 오버레이 제어도 훌륭한 UX입니다. 하지만 접근성 측면에서 개선이 필요합니다.
정적 분석 도구의 경고를 해결하기 위해 다음과 같이 개선하세요:
<div
ref={scrollRef}
className={cn(
'relative z-20 flex w-full gap-8 pr-15 overflow-x-auto whitespace-nowrap no-scrollbar cursor-grab active:cursor-grabbing select-none',
className,
)}
+ role="tablist"
+ tabIndex={0}
+ aria-label="카테고리 필터"
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUpOrLeave}
onMouseLeave={handleMouseUpOrLeave}
+ onKeyDown={(e) => {
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
+ e.preventDefault();
+ const scrollAmount = 100;
+ if (scrollRef.current) {
+ scrollRef.current.scrollLeft += e.key === 'ArrowRight' ? scrollAmount : -scrollAmount;
+ handleFirstInteraction();
+ }
+ }
+ }}
>Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/app/(with-header)/components/CategoryFilter.tsx around lines 19 to 58,
the drag scroll implementation lacks accessibility improvements and triggers
static analysis warnings. To fix this, add keyboard event handlers to support
keyboard navigation for scrolling, ensure the scroll container has appropriate
ARIA roles and tabIndex for focusability, and clean up event listeners properly
in useEffect. This will enhance accessibility and resolve static analysis tool
warnings.
| <p className="text-left pt-24 lg:pt-40 text-black text-2xl md:text-3xl"> | ||
| <span className="text-primary font-bold">{keyword}</span>(으)로 검색한 결과입니다. | ||
| </p> | ||
| <p className="text-left text-sm font-normal ml-4 md:ml-0 mt-8 mb-16"> | ||
| <p className="text-left text-lg font-normal mt-8 mb-16"> | ||
| 총 <span className="font-semibold">{totalCount}</span>개의 결과 | ||
| </p> |
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.
🧹 Nitpick (assertive)
검색 안내 문구에 접근성 힌트 추가 권장
검색 결과 변화가 동적으로 발생하므로, 스크린리더 사용자를 위해 role="status" 혹은 aria-live="polite" 속성을 부여하면 접근성이 향상됩니다.
-<p className="text-left pt-24 lg:pt-40 text-black text-2xl md:text-3xl">
+<p
+ className="text-left pt-24 lg:pt-40 text-black text-2xl md:text-3xl"
+ role="status"
+ aria-live="polite"
+>📝 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.
| <p className="text-left pt-24 lg:pt-40 text-black text-2xl md:text-3xl"> | |
| <span className="text-primary font-bold">{keyword}</span>(으)로 검색한 결과입니다. | |
| </p> | |
| <p className="text-left text-sm font-normal ml-4 md:ml-0 mt-8 mb-16"> | |
| <p className="text-left text-lg font-normal mt-8 mb-16"> | |
| 총 <span className="font-semibold">{totalCount}</span>개의 결과 | |
| </p> | |
| <p | |
| className="text-left pt-24 lg:pt-40 text-black text-2xl md:text-3xl" | |
| role="status" | |
| aria-live="polite" | |
| > | |
| <span className="text-primary font-bold">{keyword}</span>(으)로 검색한 결과입니다. | |
| </p> | |
| <p className="text-left text-lg font-normal mt-8 mb-16"> | |
| 총 <span className="font-semibold">{totalCount}</span>개의 결과 | |
| </p> |
🤖 Prompt for AI Agents
In src/app/(with-header)/components/ExperienceList.tsx around lines 51 to 56,
the search result text updates dynamically but lacks accessibility hints for
screen readers. Add either role="status" or aria-live="polite" attribute to the
container element wrapping the dynamic search result text to notify assistive
technologies of content changes, improving accessibility.
| <div className='relative flex justify-between items-center mb-40 pr-120'> | ||
| <CategoryFilter | ||
| selectedCategory={selectedCategory} | ||
| onChange={(category) => { | ||
| setSelectedCategory(category); | ||
| setCurrentPage(1); | ||
| }} | ||
| /> | ||
| <Dropdown | ||
| className='w-200' | ||
| placeholder='가격' | ||
| options={SORT_OPTIONS} | ||
| value={sortOption && SORT_LABEL_MAP[sortOption as keyof typeof SORT_LABEL_MAP] || ''} | ||
| onChange={(label: keyof typeof SORT_VALUE_MAP) => { | ||
| const value = SORT_VALUE_MAP[label]; | ||
| setSortOption(value); | ||
| setCurrentPage(1); | ||
| }} | ||
| /> | ||
| {/* <div className=''> */} | ||
| <Dropdown | ||
| className='absolute right-0 md:w-130' | ||
| buttonClassName='flex flex-row items-center justify-between gap-0 border-nomad border rounded-[15px] text-md py-9 px-15' | ||
| listboxClassName='px-0 py-0' | ||
| optionClassName='pl-10 pr-0 py-9 text-md' | ||
| placeholder='가격' | ||
| options={SORT_OPTIONS} | ||
| value={sortOption && SORT_LABEL_MAP[sortOption as keyof typeof SORT_LABEL_MAP] || ''} | ||
| onChange={(label: keyof typeof SORT_VALUE_MAP) => { | ||
| const value = SORT_VALUE_MAP[label]; | ||
| setSortOption(value); | ||
| setCurrentPage(1); | ||
| }} | ||
| /> | ||
| {/* </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.
🧹 Nitpick (assertive)
절대 위치 Dropdown이 다른 요소를 가릴 가능성
relative + absolute 조합에 pr-120 / md:w-130 과 같은 커스텀 spacing-token을 사용하면
① Tailwind 설정 누락 시 빌드 오류 발생
② 작은 화면에서 카테고리 필터 영역을 덮어 클릭이 안 될 수 있습니다.
플렉스 레이아웃을 그대로 활용하면 여유 padding 없이도 동일한 정렬이 가능합니다.
-<div className='relative flex justify-between items-center mb-40 pr-120'>
+<div className='flex justify-between items-center mb-40'>
…
- <Dropdown
- className='absolute right-0 md:w-130'
+ <Dropdown
+ className='ml-auto w-[130px]'덧붙여, Lines 72 / 87의 주석 처리된 div 는 불필요하니 정리해주세요.
📝 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.
| <div className='relative flex justify-between items-center mb-40 pr-120'> | |
| <CategoryFilter | |
| selectedCategory={selectedCategory} | |
| onChange={(category) => { | |
| setSelectedCategory(category); | |
| setCurrentPage(1); | |
| }} | |
| /> | |
| <Dropdown | |
| className='w-200' | |
| placeholder='가격' | |
| options={SORT_OPTIONS} | |
| value={sortOption && SORT_LABEL_MAP[sortOption as keyof typeof SORT_LABEL_MAP] || ''} | |
| onChange={(label: keyof typeof SORT_VALUE_MAP) => { | |
| const value = SORT_VALUE_MAP[label]; | |
| setSortOption(value); | |
| setCurrentPage(1); | |
| }} | |
| /> | |
| {/* <div className=''> */} | |
| <Dropdown | |
| className='absolute right-0 md:w-130' | |
| buttonClassName='flex flex-row items-center justify-between gap-0 border-nomad border rounded-[15px] text-md py-9 px-15' | |
| listboxClassName='px-0 py-0' | |
| optionClassName='pl-10 pr-0 py-9 text-md' | |
| placeholder='가격' | |
| options={SORT_OPTIONS} | |
| value={sortOption && SORT_LABEL_MAP[sortOption as keyof typeof SORT_LABEL_MAP] || ''} | |
| onChange={(label: keyof typeof SORT_VALUE_MAP) => { | |
| const value = SORT_VALUE_MAP[label]; | |
| setSortOption(value); | |
| setCurrentPage(1); | |
| }} | |
| /> | |
| {/* </div> */} | |
| </div> | |
| <div className='flex justify-between items-center mb-40'> | |
| <CategoryFilter | |
| selectedCategory={selectedCategory} | |
| onChange={(category) => { | |
| setSelectedCategory(category); | |
| setCurrentPage(1); | |
| }} | |
| /> | |
| {/* <div className=''> */} | |
| <Dropdown | |
| className='ml-auto w-[130px]' | |
| buttonClassName='flex flex-row items-center justify-between gap-0 border-nomad border rounded-[15px] text-md py-9 px-15' | |
| listboxClassName='px-0 py-0' | |
| optionClassName='pl-10 pr-0 py-9 text-md' | |
| placeholder='가격' | |
| options={SORT_OPTIONS} | |
| value={sortOption && SORT_LABEL_MAP[sortOption as keyof typeof SORT_LABEL_MAP] || ''} | |
| onChange={(label: keyof typeof SORT_VALUE_MAP) => { | |
| const value = SORT_VALUE_MAP[label]; | |
| setSortOption(value); | |
| setCurrentPage(1); | |
| }} | |
| /> | |
| {/* </div> */} | |
| </div> |
🤖 Prompt for AI Agents
In src/app/(with-header)/components/ExperienceList.tsx between lines 64 and 88,
the Dropdown component uses absolute positioning with custom padding and width
tokens that may cause build errors if Tailwind config is missing and can overlap
the CategoryFilter on small screens. Remove the absolute positioning and custom
padding/width classes, instead rely on the existing flex layout to align
elements properly without extra spacing. Also, delete the commented-out div tags
on lines 72 and 87 as they are unnecessary.
| queryFn: ({ pageParam = undefined }) => | ||
| getPopularExperiences(pageParam as number | undefined), // 타입 단언 필요 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
타입 단언 대신 타입 가드 사용 고려
타입 단언보다는 타입 가드나 더 안전한 타입 처리를 고려해보세요.
- queryFn: ({ pageParam = undefined }) =>
- getPopularExperiences(pageParam as number | undefined), // 타입 단언 필요
+ queryFn: ({ pageParam }) =>
+ getPopularExperiences(typeof pageParam === 'number' ? pageParam : undefined),📝 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.
| queryFn: ({ pageParam = undefined }) => | |
| getPopularExperiences(pageParam as number | undefined), // 타입 단언 필요 | |
| queryFn: ({ pageParam }) => | |
| getPopularExperiences(typeof pageParam === 'number' ? pageParam : undefined), |
🤖 Prompt for AI Agents
In src/app/(with-header)/components/PopularExperiences.tsx around lines 32 to
33, replace the type assertion (pageParam as number | undefined) with a type
guard to safely check the type of pageParam before passing it to
getPopularExperiences. Implement a conditional check to verify if pageParam is a
number or undefined, and handle other cases appropriately to ensure type safety
without using type assertions.
| export interface ExperienceResponse { | ||
| cursorId: number; | ||
| totalCount: number; | ||
| activities: Experience[]; | ||
| } |
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.
💡 Verification agent
🧩 Analysis chain
인터페이스 이름 충돌 가능성 확인
ExperienceResponse라는 이름이 다른 파일의 유사한 인터페이스와 충돌할 수 있습니다. 더 구체적인 이름을 사용하는 것이 좋을 수 있습니다.
🏁 Script executed:
#!/bin/bash
# ExperienceResponse 인터페이스 중복 확인
ast-grep --pattern 'interface ExperienceResponse {
$$$
}'Length of output: 1044
ExperienceResponse 인터페이스 중복 정의 확인 및 에러 처리 보완 권장
중복 정의 위치:
- src/types/experienceListTypes.ts: export interface ExperienceResponse
- src/app/api/experiences/getPopularExperiences.ts: export interface ExperienceResponse
- src/app/api/experiences/getExperiences.ts: interface ExperienceResponse
제안 사항:
- 전역 타입 파일(
src/types/experienceListTypes.ts)에 정의된ExperienceResponse를 재사용하거나,
API별로 구체적인 이름(PopularExperiencesResponse,ExperiencesResponse)으로 분리하세요. fetch호출 시 네트워크 오류나 비정상 응답에 대비해try/catch를 추가하고,
에러 발생 시 적절히 처리(예: 예외 던지기, 사용자 알림)하는 로직을 구현해 주세요.
🤖 Prompt for AI Agents
In src/app/api/experiences/getPopularExperiences.ts around lines 4 to 8, the
ExperienceResponse interface is redundantly defined, causing potential
conflicts. Refactor by removing this local definition and importing the
ExperienceResponse interface from the global type file
src/types/experienceListTypes.ts, or rename it to a more specific name like
PopularExperiencesResponse if API-specific differentiation is needed.
Additionally, wrap the fetch call in a try/catch block to handle network errors
or unexpected responses, and implement proper error handling such as throwing
exceptions or notifying the user.
| @layer utilities { | ||
| .line-clamp-1-custom { | ||
| display: -webkit-box; | ||
| -webkit-line-clamp: 1; | ||
| -webkit-box-orient: vertical; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| } | ||
|
|
||
| .line-clamp-2-custom { | ||
| display: -webkit-box; | ||
| -webkit-line-clamp: 2; | ||
| -webkit-box-orient: vertical; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| } | ||
|
|
||
| .line-clamp-3-custom { | ||
| display: -webkit-box; | ||
| -webkit-line-clamp: 3; | ||
| -webkit-box-orient: vertical; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| } | ||
| } |
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.
💡 Verification agent
🧩 Analysis chain
브라우저 호환성을 확인하세요
새로운 line-clamp 유틸리티 클래스들이 올바르게 구현되었습니다. -webkit-line-clamp 속성은 현재 대부분의 현대 브라우저에서 지원되지만, 지원되지 않는 브라우저에서의 폴백 처리를 고려해보세요.
다음 스크립트를 통해 최신 브라우저 지원 상황을 확인해보세요:
🌐 Web query:
-webkit-line-clamp CSS property browser support 2025
💡 Result:
As of August 2025, the -webkit-line-clamp CSS property remains a non-standard, vendor-prefixed feature primarily supported in WebKit-based browsers like Safari and Chrome. This property allows developers to limit the number of lines of text displayed in a block element, truncating excess content with an ellipsis.
Current Browser Support:
- Safari: Fully supports
-webkit-line-clampwithout the need for additional prefixes. - Chrome: Supports
-webkit-line-clampwith the-webkit-prefix. - Firefox: Does not support
-webkit-line-clamp. - Edge: As a Chromium-based browser, Edge supports
-webkit-line-clampwith the-webkit-prefix.
Standardization Efforts:
The CSS Working Group has been working on standardizing the line-clamp feature. In June 2025, they resolved to make -webkit-line-clamp a shorthand for the standard line-clamp property, provided no compatibility issues arise. (w3.org) This move aims to align the vendor-prefixed version with the standardized property, facilitating broader adoption and interoperability.
Recommendations:
Given the ongoing standardization efforts and the current lack of support in Firefox, it's advisable to use the standard line-clamp property in conjunction with the -webkit-line-clamp for broader compatibility. This approach ensures that your styles are applied correctly across different browsers, including those that support the unprefixed version.
Example Usage:
.my-text-block {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
text-overflow: ellipsis;
}
.my-text-block {
display: block;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
text-overflow: ellipsis;
}In this example, the -webkit-line-clamp property is used to limit the text to three lines, with excess content truncated and replaced by an ellipsis. The standard line-clamp property is included to ensure future compatibility as browser support evolves.
By adopting this approach, you can create more robust and future-proof web designs that gracefully handle text truncation across various browsers.
Citations:
브라우저 호환성 및 표준 속성 추가 제안
src/app/globals.css (147–171)에 정의된 .line-clamp-{1,2,3}-custom 유틸리티는 기능적으로 올바르지만,
-webkit-line-clamp는 WebKit 기반 브라우저(Safari, Chrome, Edge)에서만 동작하며 Firefox에서는 무시됩니다.
향후 표준화된 line-clamp 속성을 함께 사용하거나, @supports로 대체 구현을 제공하는 것을 권장드립니다.
수정 예시:
@layer utilities {
.line-clamp-1-custom {
display: -webkit-box;
-webkit-box-orient: vertical;
- -webkit-line-clamp: 1;
+ /* WebKit 전용 */
+ -webkit-line-clamp: 1;
+ /* 표준 속성 (미래 호환성 확보) */
+ line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
/* .line-clamp-2-custom, .line-clamp-3-custom에도 동일하게 적용 */
}- Firefox 지원이 필요하다면 JS 폴리필 또는 max-height/line-height 기반 페일백을 고려하세요.
- @supports(line-clamp: 1) 조건부 스타일을 추가해 비표준 속성 우선순위를 관리할 수 있습니다.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @layer utilities { | |
| .line-clamp-1-custom { | |
| display: -webkit-box; | |
| -webkit-line-clamp: 1; | |
| -webkit-box-orient: vertical; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .line-clamp-2-custom { | |
| display: -webkit-box; | |
| -webkit-line-clamp: 2; | |
| -webkit-box-orient: vertical; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .line-clamp-3-custom { | |
| display: -webkit-box; | |
| -webkit-line-clamp: 3; | |
| -webkit-box-orient: vertical; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| } | |
| @layer utilities { | |
| .line-clamp-1-custom { | |
| display: -webkit-box; | |
| -webkit-box-orient: vertical; | |
| /* WebKit 전용 */ | |
| -webkit-line-clamp: 1; | |
| /* 표준 속성 (미래 호환성 확보) */ | |
| line-clamp: 1; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .line-clamp-2-custom { | |
| display: -webkit-box; | |
| -webkit-box-orient: vertical; | |
| /* WebKit 전용 */ | |
| -webkit-line-clamp: 2; | |
| /* 표준 속성 (미래 호환성 확보) */ | |
| line-clamp: 2; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .line-clamp-3-custom { | |
| display: -webkit-box; | |
| -webkit-box-orient: vertical; | |
| /* WebKit 전용 */ | |
| -webkit-line-clamp: 3; | |
| /* 표준 속성 (미래 호환성 확보) */ | |
| line-clamp: 3; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/app/globals.css between lines 147 and 171, the .line-clamp-{1,2,3}-custom
classes use the non-standard -webkit-line-clamp property which only works in
WebKit browsers. To improve cross-browser compatibility, add the standard
line-clamp property alongside -webkit-line-clamp and use @supports queries to
provide fallback styles or alternative implementations for browsers like
Firefox. Consider adding max-height and line-height based fallbacks or a JS
polyfill if Firefox support is required.
| {isOpen && ( | ||
| <NotificationDropdown | ||
| className='md:right- fixed inset-0 md:absolute md:top-90 md:left-800' | ||
| className='fixed inset-0 md:absolute md:top-90 md:left-auto md:right-20 lg:right-10 z-50' |
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
복잡한 포지셔닝 클래스를 단순화하거나 문서화하세요.
NotificationDropdown의 포지셔닝 클래스가 매우 복잡합니다. fixed inset-0과 md:absolute 등 서로 다른 포지셔닝 전략이 혼재되어 있어 유지보수가 어려울 수 있습니다.
다음과 같은 개선을 고려해보세요:
- 포지셔닝 로직을 별도 컴포넌트나 훅으로 분리
- 또는 최소한 주석으로 각 브레이크포인트별 의도를 설명
{isOpen && (
+ {/* 모바일: 전체화면 오버레이, 데스크톱: 우상단 절대위치 */}
<NotificationDropdown
className='fixed inset-0 md:absolute md:top-90 md:left-auto md:right-20 lg:right-10 z-50'
onClose={() => setIsOpen(false)}
/>
)}📝 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.
| className='fixed inset-0 md:absolute md:top-90 md:left-auto md:right-20 lg:right-10 z-50' | |
| {isOpen && ( | |
| {/* 모바일: 전체화면 오버레이, 데스크톱: 우상단 절대위치 */} | |
| <NotificationDropdown | |
| className='fixed inset-0 md:absolute md:top-90 md:left-auto md:right-20 lg:right-10 z-50' | |
| onClose={() => setIsOpen(false)} | |
| /> | |
| )} |
🤖 Prompt for AI Agents
In src/components/Header.tsx at line 97, the NotificationDropdown's positioning
classes combine conflicting strategies like 'fixed inset-0' and 'md:absolute',
making maintenance difficult. Simplify the positioning by either extracting the
logic into a separate component or custom hook that manages responsive
positioning, or add clear comments explaining the intent of each breakpoint's
positioning to improve readability and maintainability.
| className='absolute top-50 right-0 z-50 mt-12 w-120 rounded-md border border-gray-200 bg-white shadow-md' | ||
| onClick={(e) => e.stopPropagation()} | ||
| > | ||
| <Link | ||
| href='/mypage' | ||
| className='block w-full px-16 py-12 text-left hover:bg-gray-50' | ||
| className='block w-full px-16 py-12 hover:bg-gray-50 rounded-[15px] text-center' |
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
스타일링 개선은 좋지만 접근성 문제를 해결해야 합니다.
UI 스타일링 변경사항은 적절하지만, 정적 분석 도구가 지적한 접근성 문제를 해결해야 합니다.
다음 수정사항을 적용하여 키보드 네비게이션 지원을 추가하세요:
<div
- className='absolute top-50 right-0 z-50 mt-12 w-120 rounded-md border border-gray-200 bg-white shadow-md'
- onClick={(e) => e.stopPropagation()}
+ className='absolute top-50 right-0 z-50 mt-12 w-120 rounded-md border border-gray-200 bg-white shadow-md'
+ role="menu"
+ onKeyDown={(e) => {
+ if (e.key === 'Escape') {
+ setIsOpen(false);
+ }
+ }}
+ onClick={(e) => e.stopPropagation()}
>📝 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.
| className='absolute top-50 right-0 z-50 mt-12 w-120 rounded-md border border-gray-200 bg-white shadow-md' | |
| onClick={(e) => e.stopPropagation()} | |
| > | |
| <Link | |
| href='/mypage' | |
| className='block w-full px-16 py-12 text-left hover:bg-gray-50' | |
| className='block w-full px-16 py-12 hover:bg-gray-50 rounded-[15px] text-center' | |
| <div | |
| className='absolute top-50 right-0 z-50 mt-12 w-120 rounded-md border border-gray-200 bg-white shadow-md' | |
| role="menu" | |
| onKeyDown={(e) => { | |
| if (e.key === 'Escape') { | |
| setIsOpen(false); | |
| } | |
| }} | |
| onClick={(e) => e.stopPropagation()} | |
| > | |
| <Link | |
| href='/mypage' | |
| className='block w-full px-16 py-12 hover:bg-gray-50 rounded-[15px] text-center' |
🧰 Tools
🪛 Biome (2.1.2)
[error] 58-63: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.
Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.
(lint/a11y/useKeyWithClickEvents)
[error] 58-63: Static Elements should not be interactive.
To add interactivity such as a mouse or key event listener to a static element, give the element an appropriate role value.
(lint/a11y/noStaticElementInteractions)
🤖 Prompt for AI Agents
In src/components/ProfileDropdown.tsx around lines 58 to 63, the dropdown menu
lacks keyboard accessibility support. To fix this, add appropriate keyboard
event handlers such as onKeyDown to handle keyboard navigation (e.g., closing
the menu on Escape key), ensure focus management by setting tabIndex where
needed, and use semantic HTML elements or ARIA roles to improve accessibility.
This will enable users to navigate the dropdown using the keyboard effectively.
| buttonClassName?: string; | ||
| listboxClassName?: string; | ||
| optionClassName?: string; | ||
| } |
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.
🧹 Nitpick (assertive)
JSDoc 문서를 업데이트하세요.
새로 추가된 스타일링 속성들(buttonClassName, listboxClassName, optionClassName)에 대한 설명을 JSDoc 주석에 추가하는 것이 좋겠습니다.
다음과 같이 문서를 보완할 수 있습니다:
* @property className - 컴포넌트에 적용할 CSS 클래스 (크기, 위치 등)
* @property disabled - 비활성화 여부
* @property disableScroll - 스크롤 비활성화 여부
+ * @property buttonClassName - 드롭다운 버튼에 적용할 CSS 클래스
+ * @property listboxClassName - 드롭다운 목록 컨테이너에 적용할 CSS 클래스
+ * @property optionClassName - 각 옵션 요소에 적용할 CSS 클래스
*/🤖 Prompt for AI Agents
In src/types/dropdownTypes.ts around lines 25 to 28, the new styling properties
buttonClassName, listboxClassName, and optionClassName lack JSDoc comments.
Update the existing JSDoc block for this interface or type to include clear
descriptions for each of these properties, explaining their purpose and usage as
CSS class names for styling the button, listbox, and options respectively.
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: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
src/components/Dropdown.tsx(5 hunks)
🔇 Additional comments (3)
src/components/Dropdown.tsx (3)
37-39: 스타일링 유연성을 위한 새로운 className props 추가가 잘 구현되었습니다.
buttonClassName,listboxClassName,optionClassNameprops 추가로 외부에서 각 요소별로 세밀한 스타일링이 가능해져 컴포넌트의 재사용성이 크게 향상되었습니다.
137-137: buttonClassName이 올바르게 적용되었습니다.기본 스타일과 외부에서 전달된 스타일을 적절히 병합하여 스타일 우선순위가 올바르게 처리되었습니다.
173-173: listboxClassName이 올바르게 적용되었습니다.리스트박스 컨테이너에 대한 외부 스타일링이 적절히 적용되어 드롭다운 목록의 커스터마이징이 가능해졌습니다.
| import ChevronIcon from '@assets/svg/chevron'; | ||
| import CheckIcon from '@assets/svg/check'; | ||
| import { DropdownProps } from '@/types/dropdownTypes'; | ||
| // import CheckIcon from '@assets/svg/check'; |
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.
🧹 Nitpick (assertive)
import 문을 주석 처리하는 대신 제거하는 것을 고려해보세요.
사용하지 않는 import 문은 주석 처리보다는 완전히 제거하는 것이 일반적입니다. 향후 다시 사용할 계획이 있다면 현재 형태를 유지할 수 있지만, 코드 정리 차원에서는 제거가 권장됩니다.
🤖 Prompt for AI Agents
In src/components/Dropdown.tsx at line 9, the import statement for CheckIcon is
commented out. Instead of commenting it out, remove this unused import entirely
to keep the code clean and maintainable. If you plan to use it later, you can
re-add it when needed.
| ? 'bg-nomad rounded text-white' | ||
| : 'hover:bg-gray-100', | ||
| )} | ||
| optionClassName,)} |
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.
optionClassName 적용에 문법 오류가 있습니다.
cn() 함수 호출에서 닫는 괄호와 쉼표의 위치가 잘못되었습니다.
다음과 같이 수정하세요:
- optionClassName,)}
+ optionClassName,
+ )}🤖 Prompt for AI Agents
In src/components/Dropdown.tsx at line 193, there is a syntax error in the usage
of the cn() function related to the placement of the closing parenthesis and
comma around optionClassName. Fix this by correctly positioning the closing
parenthesis after all arguments and ensuring commas separate the arguments
properly within the cn() call.
| {/* <div className='flex w-24 justify-start'> | ||
| {isSelected && ( | ||
| <CheckIcon | ||
| size={20} | ||
| showBackground={false} | ||
| className='text-white' | ||
| /> | ||
| )} | ||
| </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.
🧹 Nitpick (assertive)
주석 처리된 체크 아이콘 코드를 제거하거나 더 명확한 주석을 추가하세요.
현재 체크 아이콘 관련 코드가 주석 처리되어 있는데, 향후 사용 계획이 있다면 더 명확한 주석을 추가하거나, 완전히 제거할 계획이라면 삭제하는 것이 좋습니다.
향후 사용 계획이 있다면 다음과 같이 명확한 주석을 추가하세요:
- {/* <div className='flex w-24 justify-start'>
+ {/* TODO: 체크 아이콘 기능 추후 재구현 예정
+ <div className='flex w-24 justify-start'>
{isSelected && (
<CheckIcon
size={20}
showBackground={false}
className='text-white'
/>
)}
</div> */}📝 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.
| {/* <div className='flex w-24 justify-start'> | |
| {isSelected && ( | |
| <CheckIcon | |
| size={20} | |
| showBackground={false} | |
| className='text-white' | |
| /> | |
| )} | |
| </div> | |
| </div> */} | |
| {/* TODO: 체크 아이콘 기능 추후 재구현 예정 | |
| <div className='flex w-24 justify-start'> | |
| {isSelected && ( | |
| <CheckIcon | |
| size={20} | |
| showBackground={false} | |
| className='text-white' | |
| /> | |
| )} | |
| </div> */} |
🤖 Prompt for AI Agents
In src/components/Dropdown.tsx around lines 198 to 206, the commented-out
CheckIcon code is unclear. Either remove this unused commented code entirely if
it is no longer needed, or add a clear comment explaining why it is commented
out and under what conditions it might be re-enabled in the future.
minimo-9
left a comment
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.
수정하시느라 고생하셨습니다!
LeeCh0129
left a comment
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.
QA에서 나온 이슈들 처리하느라 고생하셨습니다! 드디어 내일이면 끝이네요.
evaain706
left a comment
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.
수정 수고하셨습니다!
📌 변경 사항 개요
📝 상세 내용
🔗 관련 이슈
🖼️ 스크린샷(선택사항)
💡 참고 사항
Summary by CodeRabbit
신규 기능
개선 및 변경
버그 수정
기타