-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 탐색하기 리팩토링 #137
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: 탐색하기 리팩토링 #137
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @yura0302, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 이 PR은 '탐색하기' 페이지의 전반적인 사용자 경험을 개선하기 위한 리팩토링을 포함합니다. 특히 모바일 환경에서의 반응형 디자인을 강화하고, 필터링 및 정렬 기능을 위한 새로운 UI 컴포넌트(드로어, 모바일 필터 바)를 도입하여 사용자가 더 쉽고 편리하게 정보를 탐색할 수 있도록 합니다. 또한, 코드 구조를 개선하여 유지보수성을 높였습니다. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
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.
Code Review
탐색하기 페이지의 반응형 구현 및 코드 구조 개선을 위한 리팩토링이 인상적입니다. 특히 모바일 환경을 위한 필터 컴포넌트(MobileFilterBar, MobileMultiDropdown, MobileTab)를 분리하고, drawer를 활용하여 사용자 경험을 개선한 점이 좋습니다. 또한, 상수들을 중앙에서 관리하도록 변경하여 코드의 모듈성과 유지보수성을 높였습니다.
몇 가지 추가 개선 사항을 제안합니다. 주로 누락된 UI 상태를 복구하고, 타입 안정성과 컴포넌트 재사용성을 높이는 데 중점을 두었습니다. 전반적으로 훌륭한 개선 작업입니다.
| {/* 동아리 목록 섹션 */} | ||
| <div className={`${isDesktop ? 'px-5 mt-8 pt-6 pb-12' : 'pt-4'}`}> | ||
| <div className="mx-auto max-w-[calc(17.625rem*3+1rem*2)]"> | ||
| {/* 필터 바 */} | ||
| <div | ||
| className={`flex flex-row items-center justify-between gap-2 ${isDesktop ? 'mb-12' : 'pl-5 mb-6'}`} | ||
| > | ||
| {isDesktop ? ( | ||
| <> | ||
| <div className="flex flex-wrap items-center gap-2"> | ||
| <MultiDropDown | ||
| groups={PART_OPTIONS} | ||
| value={partArray} | ||
| onChange={handlePartChange} | ||
| placeholder="파트" | ||
| maxSummary={1} | ||
| className="w-auto" | ||
| /> | ||
| <MultiDropDown | ||
| groups={WAY_OPTIONS} | ||
| value={wayArray} | ||
| onChange={handleWayChange} | ||
| placeholder="방식" | ||
| maxSummary={1} | ||
| className="w-auto" | ||
| /> | ||
| <MultiDropDown | ||
| groups={TARGET_OPTIONS} | ||
| value={targetArray} | ||
| onChange={handleTargetChange} | ||
| placeholder="모집 대상" | ||
| maxSummary={1} | ||
| className="w-auto" | ||
| /> | ||
| <button | ||
| onClick={() => resetFilters()} | ||
| className="flex items-center gap-1 px-3 py-2 text-grey-color-2 typo-button-m h-[32px] cursor-pointer" | ||
| > | ||
| <Image | ||
| src="/icons/reset.svg" | ||
| alt="reset" | ||
| width={20} | ||
| height={20} | ||
| /> | ||
| </div> | ||
| 초기화 | ||
| </button> | ||
| </div> | ||
|
|
||
| {/* 카드 그리드 */} | ||
| <div | ||
| className={`grid ${isDesktop ? 'grid-cols-3 gap-8 pt-0 pb-12' : 'grid-cols-1 gap-4'}`} | ||
| > | ||
| {clubs.map((club) => ( | ||
| <CardOverlay | ||
| key={club.clubId} | ||
| club={club} | ||
| isSubscribed={subscribedClubIds.has(club.clubId)} | ||
| onBookmarkClick={handleBookmarkClick} | ||
| /> | ||
| ))} | ||
| <div className="flex"> | ||
| <TabOverlay | ||
| options={SORT_OPTIONS} | ||
| value={currentSort as '인기순' | '이름순' | '마감순'} | ||
| onChange={(value) => setSort(value)} | ||
| onReset={() => setSort('인기순')} | ||
| /> | ||
| </div> | ||
| </div> | ||
| </> | ||
| ) : ( | ||
| <MobileFilterBar | ||
| tabs={[ | ||
| { | ||
| id: 'sort', | ||
| label: '정렬', | ||
| type: 'sort', | ||
| options: SORT_OPTIONS, | ||
| value: currentSort, | ||
| defaultValue: '인기순', | ||
| onChange: (value) => setSort(value as string), | ||
| onReset: () => setSort('인기순'), | ||
| }, | ||
| { | ||
| id: 'part', | ||
| label: '파트', | ||
| type: 'multi', | ||
| options: PART_OPTIONS, | ||
| value: partArray, | ||
| defaultValue: [], | ||
| onChange: (value) => handlePartChange(value as string[]), | ||
| onReset: () => setPart(null), | ||
| }, | ||
| { | ||
| id: 'way', | ||
| label: '방식', | ||
| type: 'multi', | ||
| options: WAY_OPTIONS, | ||
| value: wayArray, | ||
| defaultValue: [], | ||
| onChange: (value) => handleWayChange(value as string[]), | ||
| onReset: () => setWay(null), | ||
| }, | ||
| { | ||
| id: 'target', | ||
| label: '모집대상', | ||
| type: 'multi', | ||
| options: TARGET_OPTIONS, | ||
| value: targetArray, | ||
| defaultValue: [], | ||
| onChange: (value) => | ||
| handleTargetChange(value as string[]), | ||
| onReset: () => setTarget(null), | ||
| }, | ||
| ]} | ||
| onReset={resetFilters} | ||
| /> | ||
| )} | ||
| </div> | ||
|
|
||
| {/* 빈 상태 표시 */} | ||
| {clubs.length === 0 && ( | ||
| <div className="text-center py-20"> | ||
| <div className="w-16 h-16 bg-light-color-2 rounded-full flex items-center justify-center mx-auto mb-4"></div> | ||
| <h3 className="text-lg font-semibold mb-2"> | ||
| {currentField === 'all' | ||
| ? '동아리를 찾을 수 없습니다' | ||
| : `${fieldLabel} 카테고리의 동아리가 없습니다`} | ||
| </h3> | ||
| <p className="text-grey-color-2"> | ||
| {currentField === 'all' | ||
| ? '다른 필터를 시도해보세요.' | ||
| : '다른 카테고리를 선택해보세요.'} | ||
| </p> | ||
| </div> | ||
| )} | ||
| </div> | ||
| {/* 카드 그리드 */} | ||
| <div | ||
| className={`grid ${isDesktop ? 'grid-cols-3 gap-8 pt-0 pb-12' : 'grid-cols-1 gap-4 px-5'}`} | ||
| > | ||
| {clubs.map((club) => ( | ||
| <CardOverlay | ||
| key={club.clubId} | ||
| club={club} | ||
| isSubscribed={subscribedClubIds.has(club.clubId)} | ||
| onBookmarkClick={handleBookmarkClick} | ||
| /> | ||
| ))} | ||
| </div> | ||
| </> | ||
| )} | ||
| </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.
필터링 결과 동아리가 없을 때 사용자에게 아무런 정보도 표시되지 않는 문제가 있습니다. 이전 코드에 있던 "동아리를 찾을 수 없습니다"와 같은 빈 상태(empty state) UI가 제거되어, 사용자는 검색 결과가 없는 것인지 로딩 중인지 알기 어렵습니다.
사용자 경험을 위해, clubs 배열이 비어있을 때 적절한 안내 문구를 보여주는 UI를 다시 추가하는 것이 좋습니다. 예를 들어, 카드 그리드 아래에 다음과 같은 코드를 추가할 수 있습니다.
{clubs.length === 0 && (
<div className="text-center py-20">
<div className="w-16 h-16 bg-light-color-2 rounded-full flex items-center justify-center mx-auto mb-4"></div>
<h3 className="text-lg font-semibold mb-2">
{currentField === 'all'
? '동아리를 찾을 수 없습니다'
: `${fieldLabel} 카테고리의 동아리가 없습니다`}
</h3>
<p className="text-grey-color-2">
{currentField === 'all'
? '다른 필터를 시도해보세요.'
: '다른 카테고리를 선택해보세요.'}
</p>
</div>
)}| export type MobileMultiDropdownTab = { | ||
| id: string | ||
| label: string | ||
| type: 'sort' | 'multi' | ||
| options: TabOption[] | Group[] | ||
| value: string | string[] | ||
| defaultValue?: string | string[] | ||
| onChange: (value: string | string[]) => void | ||
| onReset?: () => void | ||
| } |
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.
MobileMultiDropdownTab 타입의 options와 value 속성이 | (Union) 타입으로 정의되어 있어, 이 타입을 사용하는 MobileFilterBar.tsx 컴포넌트에서 타입 캐스팅(as)이 필요해집니다. 이는 타입 안정성을 저해할 수 있습니다.
type 속성을 기반으로 하는 Discriminated Union 패턴을 사용하여 타입을 더 명확하게 정의하는 것을 추천합니다. 이렇게 하면 타입스크립트가 type 값에 따라 options와 value의 타입을 자동으로 추론할 수 있어 코드의 안정성과 가독성이 향상됩니다.
예시:
type SortTab = {
type: 'sort';
options: TabOption[];
value: string;
// ... other sort-specific props
};
type MultiTab = {
type: 'multi';
options: Group[];
value: string[];
// ... other multi-specific props
};
export type MobileMultiDropdownTab = SortTab | MultiTab;* Feat/#99 (#107) * feat: 검색 부분 미비한 로직 추가 * fix: 이미지 수정 * Feat/#79 (#96) * fix: 히어로 이미지 수정 * feat: 프리미엄 후기 구현 * feat: 세부페이지 구현 * fix: 에러해결 --------- Co-authored-by: yura <[email protected]> * feat: 좋아요 API 명세 추가 --------- Co-authored-by: oaoong <[email protected]> Co-authored-by: yura <[email protected]> * style: 디자인토큰, 타이포그래피 수정 (#112) * style: 디자인토큰, 타이포그래피 수정 * fix: onsuccess 콜백에 mutaiton 인자 추가 --------- Co-authored-by: yura <[email protected]> * fix : 디자인 토큰, 타이포그래피 수정 (#113) * style: 디자인토큰, 타이포그래피 수정 * fix: onsuccess 콜백에 mutaiton 인자 추가 * fix: mutation파일에 onmutationresult 추가 --------- Co-authored-by: yura <[email protected]> * style: 배경 기본 색상 변경 * fix: tab filter 마감순 추가 (#129) Co-authored-by: yura <[email protected]> * fix: 탐색하기 레이아웃 수정 (#131) * style: 탐색하기 사이드바, 히어로 이미지 수정 * style: 탐색하기 레이아웃 수정 --------- Co-authored-by: yura <[email protected]> * feat: 리뷰 작성 레이아웃 (#132) * feat: 리뷰 작성 연결 페이지 재정의 * feat: 리뷰 UI 개발사항 반영 * fix: formatting * feat: QnA 컴포넌트 추가 * feat: 모바일카드 컴포넌트 구현 (#135) * refactor: card 컴포넌트 수정 * feat: 북마크 추가 * refactor: card 컴포넌트 수정 * feat: 모바일 카드 구현 * feat: 탐색하기 리팩토링 (#137) * feat: drawer 구현 * feat: 탐색 반응형 구현 * build: next 버전 패치 * build: lock파일 삭제 및 재설치 --------- Co-authored-by: oaoong <[email protected]> Co-authored-by: yura <[email protected]>
* Feat/#99 (#107) * feat: 검색 부분 미비한 로직 추가 * fix: 이미지 수정 * Feat/#79 (#96) * fix: 히어로 이미지 수정 * feat: 프리미엄 후기 구현 * feat: 세부페이지 구현 * fix: 에러해결 --------- Co-authored-by: yura <[email protected]> * feat: 좋아요 API 명세 추가 --------- Co-authored-by: oaoong <[email protected]> Co-authored-by: yura <[email protected]> * style: 디자인토큰, 타이포그래피 수정 (#112) * style: 디자인토큰, 타이포그래피 수정 * fix: onsuccess 콜백에 mutaiton 인자 추가 --------- Co-authored-by: yura <[email protected]> * fix : 디자인 토큰, 타이포그래피 수정 (#113) * style: 디자인토큰, 타이포그래피 수정 * fix: onsuccess 콜백에 mutaiton 인자 추가 * fix: mutation파일에 onmutationresult 추가 --------- Co-authored-by: yura <[email protected]> * style: 배경 기본 색상 변경 * fix: tab filter 마감순 추가 (#129) Co-authored-by: yura <[email protected]> * fix: 탐색하기 레이아웃 수정 (#131) * style: 탐색하기 사이드바, 히어로 이미지 수정 * style: 탐색하기 레이아웃 수정 --------- Co-authored-by: yura <[email protected]> * feat: 리뷰 작성 레이아웃 (#132) * feat: 리뷰 작성 연결 페이지 재정의 * feat: 리뷰 UI 개발사항 반영 * fix: formatting * feat: QnA 컴포넌트 추가 * feat: 모바일카드 컴포넌트 구현 (#135) * refactor: card 컴포넌트 수정 * feat: 북마크 추가 * refactor: card 컴포넌트 수정 * feat: 모바일 카드 구현 * feat: 탐색하기 리팩토링 (#137) * feat: drawer 구현 * feat: 탐색 반응형 구현 * build: next 버전 패치 (#139) * build: next 버전 업데이트 (#143) * build: 빌드수정 --------- Co-authored-by: oaoong <[email protected]> Co-authored-by: yura <[email protected]>
🔗 관련 이슈
Closes #130 #133 #135
✨ 변경사항
🎯 리뷰 포인트
📝 추가 정보