feat: 카테고리 페이지 구현 및 카테고리에 따른 이벤트 무한 스크롤 API 연동#125
Conversation
## Walkthrough
이 변경사항은 카테고리별 이벤트 목록 페이지를 추가하고, 이벤트 목록 컴포넌트와 API를 카테고리 및 태그 기반 필터링을 지원하도록 확장합니다. 라우터, 메인 페이지, 타입 정의, 패키지 의존성, 그리고 관련 API 함수들이 이에 맞게 수정 및 추가되었습니다.
## Changes
| 파일/경로 요약 | 변경 요약 |
|-------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|
| package.json | `@tanstack/react-query-devtools` 패키지 의존성 추가 |
| src/app/routes/Router.tsx, src/app/routes/routes.ts | 카테고리 페이지 라우트 추가, `MAIN_ROUTES`에 `/category` 경로 및 관련 설정 추가 |
| src/entities/event/api/event.ts | 카테고리 기반 무한 스크롤 이벤트 조회 함수 추가, 기존 무한 스크롤 함수 리팩토링 및 반환값 구조화 |
| src/features/event-manage/event-list/ui/EventList.tsx | `category`, `tag` prop 지원, 관련 타입 및 쿼리 로직 확장 |
| src/pages/event/ui/AllEventsPage.tsx | `EventList`에 `tag="current"` prop 추가 |
| src/pages/event/ui/CategoryPage.tsx | 카테고리별 이벤트 페이지 컴포넌트 신설, 라우터 연동 및 레이아웃 구성 |
| src/pages/home/ui/MainPage.tsx | 메인 카드 버튼 클릭 시 각 카테고리로 이동하도록 네비게이션 로직 변경 |
| src/shared/types/mainCardButtonType.ts | 각 메인 카드 버튼에 `category` 속성 추가 |
## Sequence Diagram(s)
```mermaid
sequenceDiagram
participant User
participant MainPage
participant Router
participant CategoryPage
participant EventList
participant EventAPI
User->>MainPage: 카테고리 카드 버튼 클릭
MainPage->>Router: '/category' 경로로 category state 전달
Router->>CategoryPage: CategoryPage 렌더링
CategoryPage->>EventList: category prop 전달
EventList->>EventAPI: getCategoryEventsInfinite(category, page, size)
EventAPI-->>EventList: { items, hasNextPage }
EventList-->>CategoryPage: 이벤트 목록 렌더링Possibly related PRs
Suggested reviewers
Poem
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (6)
src/shared/types/mainCardButtonType.ts (1)
5-5: 카테고리 속성이 추가되었으며 타입 안전성을 향상시킬 수 있습니다.카테고리 속성이 각 카드 버튼에 문자열로 추가되었습니다. 이는 기능적으로는 정확하지만, 타입 안전성을 높이기 위해
CategoryType을 명시적으로 사용하는 것이 좋을 것 같습니다.다음과 같이 수정해 보세요:
+import { CategoryType } from '../types/baseEventType'; export const cardButtons = [ { iconPath: '/assets/main/DevStudy.svg', label: '개발/스터디', - category: 'DEVELOPMENT_STUDY', + category: 'DEVELOPMENT_STUDY' as CategoryType, onClick: () => console.log('DevStudy clicked'), }, // 다른 버튼들도 동일하게 수정또는 전체 객체에 타입을 적용하는 방법도 있습니다:
+import { CategoryType } from '../types/baseEventType'; +interface CardButton { + iconPath: string; + label: string; + category: CategoryType; + onClick: () => void; +} -export const cardButtons = [ +export const cardButtons: CardButton[] = [ // ...Also applies to: 11-11, 17-17, 23-23
src/pages/event/ui/CategoryPage.tsx (1)
10-10: 변수명 오타 수정이 필요합니다.
navigater는 오타입니다. 올바른 변수명은navigate입니다.- const navigater = useNavigate(); + const navigate = useNavigate();그리고 이 변수의 모든 사용처도 수정해야 합니다:
- onClick={() => navigater('/search')} + onClick={() => navigate('/search')} - leftButtonClick={() => navigater('/')} + leftButtonClick={() => navigate('/')}src/features/event-manage/event-list/ui/EventList.tsx (2)
21-21: 쿼리 키 최적화 필요현재
queryKey는 category나 tag가 undefined일 때 빈 문자열('')을 사용합니다. 이는 불필요한 리페칭을 발생시킬 수 있습니다.- queryKey: ['events', 'infinite', category ?? '', tag ?? ''], + queryKey: ['events', 'infinite', category || null, tag || null],또는 더 명확하게:
- queryKey: ['events', 'infinite', category ?? '', tag ?? ''], + queryKey: ['events', 'infinite', { category, tag }],
35-35: 프로덕션 코드에서 console.log 제거 필요디버깅을 위한 console.log가 남아있습니다. 프로덕션 코드에서는 제거하는 것이 좋습니다.
- console.log('EventList data.pages:', data?.pages);src/entities/event/api/event.ts (2)
74-78: 일관성을 위해 getEventByTag 함수도 URLSearchParams를 사용하도록 수정을 고려해보세요.
getEventByTag함수는 여전히 문자열 보간법을 사용하여 URL을 구성하고 있습니다. 코드베이스의 일관성을 위해 이 함수도 URLSearchParams를 사용하도록 리팩토링하는 것을 고려해보세요.export const getEventByTag = async (tag: TagType, { page, size }: PaginationParams): Promise<EventItem[]> => { - const response = await axiosClient.get<{ result: EventItem[] }>(`/events?tags=${tag}&page=${page}&size=${size}`); + const params = new URLSearchParams(); + + params.append('tags', tag); + params.append('page', page.toString()); + params.append('size', size.toString()); + + const response = await axiosClient.get<ApiResponse<EventItem[]>>(`/events?${params.toString()}`); return response.data.result || []; };
80-89: getEventByCategory 함수도 URLSearchParams 패턴과 일관된 타입 사용을 고려해 보세요.이 함수도 문자열 보간법 대신 URLSearchParams를 사용하도록 업데이트하는 것이 좋습니다. 또한 다른 API 함수들과 달리 응답 타입이
ApiResponse<EventItem[]>대신EventItem[]로 되어 있어 일관성이 떨어집니다. 응답 처리 방식을 표준화하는 것을 고려해보세요.export const getEventByCategory = async ( category: CategoryType, { page, size }: PaginationParams ): Promise<EventItem[]> => { - const response = await axiosClient.get<EventItem[]>( - `/events/category?category=${category}&page=${page}&size=${size}` - ); - return response.data; + const params = new URLSearchParams(); + + params.append('category', category); + params.append('page', page.toString()); + params.append('size', size.toString()); + + const response = await axiosClient.get<ApiResponse<EventItem[]>>(`/events/category?${params.toString()}`); + return response.data.result || []; };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (9)
package.json(1 hunks)src/app/routes/Router.tsx(2 hunks)src/app/routes/routes.ts(1 hunks)src/entities/event/api/event.ts(2 hunks)src/features/event-manage/event-list/ui/EventList.tsx(1 hunks)src/pages/event/ui/AllEventsPage.tsx(1 hunks)src/pages/event/ui/CategoryPage.tsx(1 hunks)src/pages/home/ui/MainPage.tsx(1 hunks)src/shared/types/mainCardButtonType.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/app/routes/Router.tsx (1)
src/app/routes/routes.ts (1)
MAIN_ROUTES(1-12)
src/entities/event/api/event.ts (4)
src/entities/event/model/event.ts (2)
PaginationParams(19-22)EventItem(30-34)src/shared/types/api/apiResponse.ts (1)
ApiResponse(1-5)src/shared/types/api/http-client.ts (1)
axiosClient(6-13)src/shared/types/baseEventType.ts (1)
CategoryType(2-2)
src/features/event-manage/event-list/ui/EventList.tsx (3)
src/shared/types/baseEventType.ts (3)
BaseEvent(5-24)CategoryType(2-2)TagType(3-3)src/shared/hooks/useInfiniteScroll.ts (1)
useInfiniteScroll(14-21)src/entities/event/api/event.ts (2)
getCategoryEventsInfinite(53-72)getAllEventsInfinite(31-50)
🔇 Additional comments (12)
package.json (1)
19-19: React Query DevTools 패키지 추가가 적절합니다.React Query 상태를 디버깅하기 위한 DevTools 패키지를 추가한 것은 개발 과정에서 큰 도움이 될 것입니다. 선택한 버전이 기존 React Query 버전(^5.61.3)과 호환됩니다.
src/app/routes/routes.ts (1)
10-11: 카테고리 라우트 추가가 적절합니다.카테고리 페이지를 위한 새로운 라우트 추가는 PR 목표와 일치합니다. 트레일링 콤마를 추가한 것도 향후 새로운 라우트를 추가할 때 Git diff를 깔끔하게 유지하는 좋은 관행입니다.
src/pages/event/ui/AllEventsPage.tsx (1)
29-29: EventList 컴포넌트에 태그 속성 추가가 적절합니다.이벤트 목록에 "current" 태그를 명시적으로 전달하여 현재 이벤트만 표시하도록 한 것은 좋은 변경사항입니다. 이는 카테고리 필터링 구현이라는 PR 목표와 일치합니다.
src/pages/home/ui/MainPage.tsx (2)
62-62: 카테고리 페이지로 네비게이션 처리가 적절합니다.카테고리 버튼 클릭 시 해당 카테고리 정보를 state로 전달하며 '/category' 경로로 이동하도록 구현한 것은 PR 목표에 부합하는 좋은 접근 방식입니다. React Router의 state 기능을 활용하여 데이터를 전달하는 방식도 적절합니다.
80-80: React Query DevTools 사용이 개발 경험을 향상시킬 것입니다.React Query DevTools를 추가한 것은 package.json에 추가된 새로운, 의존성과 일치하며, 데이터 패칭 및 캐싱 상태를 디버깅하는 데 유용하게 사용될 것입니다.
src/app/routes/Router.tsx (1)
39-39: 카테고리 페이지 라우트가 올바르게 추가되었습니다.새로운 CategoryPage 컴포넌트를 위한 라우트가 적절하게 추가되었습니다. 카테고리 정보는 URL 파라미터가 아닌 location state를 통해 전달되는 것으로 보입니다.
Also applies to: 50-50
src/pages/event/ui/CategoryPage.tsx (1)
18-24: 검색 기능이 완전히 구현되지 않았습니다.현재 SearchTextField가 있지만
onChange핸들러가 비어있고 실제 검색 기능이 구현되지 않았습니다.검색 기능을 제대로 구현하거나, 현재 단계에서 필요하지 않다면 주석으로 TODO를 추가하는 것이 좋습니다.
centerContent={ <SearchTextField iconPath={<img src={searchIcon} alt="searchIcon" />} onClick={() => navigater('/search')} - onChange={() => {}} + onChange={(e) => { + // TODO: 검색 기능 구현 예정 + console.log('Search input:', e.target.value); + }} placeholder="입력해주세요" /> }src/features/event-manage/event-list/ui/EventList.tsx (2)
22-27: API 호출 로직이 명확하고 잘 구현되었습니다.카테고리 기반 이벤트 가져오기와 태그 기반 이벤트 가져오기를 구분하여 조건부로 처리하는 로직이 깔끔합니다.
29-29:Details
✅ Verification successful
필터에 undefined 값 전달 여부 검토 필요
현재
tag와category가 undefined인 경우에도 filters에 포함됩니다. API 호출 시 이러한 undefined 값이 어떻게 처리되는지 확인이 필요합니다.
🏁 Script executed:
#!/bin/bash # entities/event/api/event.ts 파일에서 undefined 매개변수 처리 방식 확인 # getAllEventsInfinite 및 getCategoryEventsInfinite 함수에서 undefined 매개변수 처리 확인 rg -A 5 "if \(tag\)" src/entities/event/api/event.ts rg -A 5 "if \(category\)" src/entities/event/api/event.tsLength of output: 339
🏁 Script executed:
#!/bin/bash # category 필터 처리 여부 확인 rg -A5 "params.append('category'" src/entities/event/api/event.ts rg -A5 "getCategoryEventsInfinite" src/entities/event/api/event.tsLength of output: 457
🏁 Script executed:
#!/bin/bash # getAllEventsInfinite 함수 전체 확인 rg -n "export const getAllEventsInfinite" -A10 src/entities/event/api/event.ts # getCategoryEventsInfinite 함수 전체 확인 rg -n "export const getCategoryEventsInfinite" -A10 src/entities/event/api/event.ts # 모든 params.append 호출 확인 rg -n "params\.append" src/entities/event/api/event.tsLength of output: 1370
필터 undefined 처리 검토 결과
getAllEventsInfinite함수에서는tag가 falsy(undefined 등)인 경우if (tag)조건에 의해tags파라미터를 추가하지 않습니다.getCategoryEventsInfinite함수의 시그니처에는category: CategoryType가 필수로 정의되어 있어, 컴파일 시 undefined 전달이 차단되며 항상 올바른category파라미터가 포함됩니다.결론적으로, UI에서
filters: { tag, category }를 전달해도 API 호출 시 의도치 않은 undefined 파라미터가 전송되지 않으므로 추가 조치가 필요 없습니다.src/entities/event/api/event.ts (3)
15-26: searchEvents 함수의 가독성이 개선되었습니다.함수 시그니처와 구현이 더 명확하게 포맷팅되어 가독성이 향상되었습니다. 파라미터를 URLSearchParams를 사용하여 구성하는 것은 URL 인코딩을 올바르게 처리하는 좋은 방법입니다.
31-50: URLSearchParams를 사용한 개선된 파라미터 처리 방식입니다.문자열 보간법 대신 URLSearchParams를 사용하여 쿼리 파라미터를 구성하는 방식으로 개선되었습니다. 이 접근 방식은 다음과 같은 이점이 있습니다:
- 특수 문자의 자동 인코딩
- 코드 가독성 향상
- 파라미터 구성의 일관성
또한
response.data.result ?? []와 같은 nullish 병합 연산자를 사용하여 결과가 없을 경우를 적절히 처리하고 있습니다.
52-72: 카테고리별 이벤트 목록을 무한 스크롤로 조회하는 함수가 잘 구현되었습니다.새로 추가된
getCategoryEventsInfinite함수는 카테고리 기반 이벤트 필터링을 위한 API를 잘 구현하고 있습니다. URLSearchParams를 사용한 파라미터 구성 방식과 반환 구조가getAllEventsInfinite함수와 일관성 있게 구현되어 있습니다.다만 이미 존재하는
getEventByCategory함수(80-89줄)와의 관계를 고려해볼 필요가 있습니다:
getEventByCategory는/events/category엔드포인트를 사용하지만, 새 함수는/events/categories를 사용합니다.- 두 함수의 역할이 유사하므로 기존 함수를 대체하거나 통합할 계획이 있는지 확인해보세요.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/pages/event/ui/CategoryPage.tsx (2)
15-15: 카테고리가 undefined일 경우에 대한 처리가 필요합니다.현재 코드에서는 사용자가 직접 URL을 통해
/category로 접근할 경우category값이undefined가 될 수 있습니다. 이 경우에 대한 처리가 없어 오류가 발생할 수 있습니다.카테고리가 없을 경우에 대한 처리를 추가하세요:
const category = location.state?.category; + // 카테고리가 없을 경우 메인 페이지로 리다이렉트 + useEffect(() => { + if (!category) { + navigater('/', { replace: true }); + } + }, [category, navigater]);
43-43: 🛠️ Refactor suggestion카테고리가 없을 경우 EventList 렌더링에 대한 처리가 필요합니다.
category값이undefined일 경우,EventList컴포넌트가 오류를 발생시킬 수 있습니다.조건부 렌더링을 추가하여
category값이 있는 경우에만 해당 카테고리의 이벤트를 표시하도록 수정하세요:- <EventList category={category} /> + {category ? ( + <EventList category={category} /> + ) : ( + <div className="mt-8 text-center text-gray-500"> + 카테고리를 선택해 주세요. + </div> + )}
🧹 Nitpick comments (2)
src/pages/event/ui/CategoryPage.tsx (1)
12-14: navigater 변수명 오타를 수정하세요.
navigater변수명은 일반적으로navigator로 작성하는 것이 관례입니다. 또는 React Router의 경우navigate로 작성하는 것이 일반적입니다.- const navigater = useNavigate(); + const navigate = useNavigate();이 변경에 따라 다른 부분도
navigate로 변경해야 합니다:- onClick={() => navigater('/search')} + onClick={() => navigate('/search')} - leftButtonClick={() => navigater('/')} + leftButtonClick={() => navigate('/')}src/pages/event/ui/AllEventsPage.tsx (1)
13-13: navigater 변수명 오타를 수정하세요.
navigater변수명은 일반적으로navigator로 작성하는 것이 관례입니다. 또는 React Router의 경우navigate로 작성하는 것이 일반적입니다.- const navigater = useNavigate(); + const navigate = useNavigate();이 변경에 따라 다른 부분도
navigate로 변경해야 합니다:- onClick={() => navigater('/search')} + onClick={() => navigate('/search')} - leftButtonClick={() => navigater('/')} + leftButtonClick={() => navigate('/')}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/pages/event/ui/AllEventsPage.tsx(2 hunks)src/pages/event/ui/CategoryPage.tsx(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/pages/event/ui/AllEventsPage.tsx (1)
src/app/provider/authStore.ts (1)
useAuthStore(17-33)
src/pages/event/ui/CategoryPage.tsx (1)
src/app/provider/authStore.ts (1)
useAuthStore(17-33)
🔇 Additional comments (5)
src/pages/event/ui/AllEventsPage.tsx (5)
8-10: 인증 관련 기능 추가에 대한 구현이 적절합니다.인증 상태 관리와 로그인 모달 기능을 위한 필요한 임포트가 추가되었습니다.
14-14: 인증 스토어 활용이 적절하게 구현되었습니다.useAuthStore에서 필요한 상태와 함수들을 적절하게 구조 분해 할당하여 사용하고 있습니다.
30-37: 로그인 버튼 구현이 적절합니다.사용자의 로그인 상태에 따라 버튼 텍스트와 동작을 다르게 설정하는 것이 잘 구현되었습니다.
39-39: 로그인 모달 구현이 적절합니다.framer-motion의 AnimatePresence를 사용하여 모달이 자연스럽게 나타나고 사라지도록 구현하였습니다.
41-41: EventList에 tag 속성 추가가 적절합니다.현재 이벤트 목록만 표시하도록 tag="current" 속성을 추가한 것이 적절합니다. 이는 CategoryPage와의 일관성을 유지하면서 필터링 기능을 구현한 좋은 방식입니다.
6716c24 to
38439df
Compare
개발/스터디 이벤트 페이지
네트워킹 이벤트 페이지
해커톤 이벤트 페이지
컨퍼런스 이벤트 페이지
작업 방식
Summary by CodeRabbit
신규 기능
기능 개선
기타