Conversation
|
Caution Review failedThe pull request is closed. """ Walkthrough이번 변경 사항은 이벤트 관리 및 대시보드 기능의 데이터 흐름을 전반적으로 개선하고, UI 컴포넌트의 상태 제어 방식을 외부 props 기반으로 전환하는 데 초점을 맞추고 있습니다. 이벤트 상세 조회 및 수정 API와 타입이 통합·정비되었으며, 여러 UI 컴포넌트(예: FileUpload, TextEditor, LinkInput, SearchBar, ChoiceChip 등)가 내부 상태에서 props 기반의 제어 컴포넌트로 리팩토링되었습니다. 또한, 이벤트 관련 타입 정의가 공통 모듈로 이동하고, 대시보드 및 이벤트 상세 페이지에서 데이터 fetch 및 업데이트 로직이 커스텀 훅을 통해 일관되게 관리됩니다. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as EventDetailPage
participant Hook as useEventDetail
participant API as eventDetail API
participant MutHook as useUpdateEventHook
participant MutAPI as updateEventInfo API
UI->>Hook: useEventDetail() 호출
Hook->>API: eventDetail({ eventId })
API-->>Hook: EventDetailResponse 반환
Hook-->>UI: eventDetail data 반환
UI->>UI: 상태값(이미지, 설명, 링크 등) 세팅
UI->>MutHook: useUpdateEventHook() 호출
UI->>MutHook: mutate(수정 데이터)
MutHook->>MutAPI: updateEventInfo(eventId, 수정 데이터)
MutAPI-->>MutHook: 응답 반환
MutHook-->>UI: onSuccess → 알림 및 네비게이션
Possibly related PRs
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (23)
✨ Finishing Touches
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:
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.
Actionable comments posted: 8
🔭 Outside diff range comments (1)
src/pages/event/ui/EventDetailsPage.tsx (1)
49-59: 🛠️ Refactor suggestion에러 처리 개선 필요
API 호출 실패 시 현재는 오류를 콘솔에 기록만 하고 사용자에게 어떤 피드백도 제공하지 않습니다. 오류 상태를 추가하여 사용자에게 적절한 오류 메시지를 표시하는 것이 좋겠습니다.
const [event, setEvent] = useState<EventDetailResponse | null>(null); + const [error, setError] = useState<string | null>(null); const eventId = 1; //수정 필요 useEffect(() => { const fetchEventDetail = async () => { try { const response = await eventDetail({ eventId }); setEvent(response); + setError(null); } catch (error) { console.error('이벤트 상세 정보 불러오기 실패:', error); + setError('이벤트 정보를 불러오는 데 실패했습니다. 나중에 다시 시도해주세요.'); } }; fetchEventDetail(); }, []);그리고 렌더링 부분에 오류 상태 표시 추가:
{event ? ( <> {/* 현재 JSX 내용 */} </> + ) : error ? ( + <div className="flex justify-center items-center h-screen text-red-500">{error}</div> ) : ( <div className="flex justify-center items-center h-screen">로딩 중...</div> )}
🧹 Nitpick comments (8)
design-system/ui/ChoiceChip.tsx (1)
3-6: ChoiceChipOption의 속성을 필수값으로 변경하는 것이 좋을 것 같습니다.현재
label과value속성이 모두 옵션(?)으로 정의되어 있어 undefined 값이 발생할 가능성이 있습니다. 컴포넌트 사용 시 혼란을 방지하기 위해 이 속성들을 필수값으로 변경하는 것이 안전합니다.interface ChoiceChipOption { - label?: string; // UI에 보여질 한국어 - value?: string; // 서버에서 오는 값 + label: string; // UI에 보여질 한국어 + value: string; // 서버에서 오는 값 }src/shared/types/baseEventType.ts (1)
1-25: 이벤트 관련 타입 정의가 잘 구성되었습니다.이벤트 데이터를 위한 표준화된 타입을 정의하여 애플리케이션 전반에 걸쳐 일관된 데이터 구조를 유지할 수 있게 되었습니다. 특히
OnlineType과CategoryType을 리터럴 유니온 타입으로 정의한 것은 타입 안정성을 높이는 좋은 방법입니다.각 필드의 목적과 사용 방법을 설명하는 JSDoc 주석을 추가하면 코드 유지보수성이 더욱 향상될 것입니다.
+/** + * 이벤트의 온라인/오프라인 유형을 정의합니다. + */ export type OnlineType = 'ONLINE' | 'OFFLINE'; +/** + * 이벤트의 카테고리 유형을 정의합니다. + */ export type CategoryType = 'DEVELOPMENT_STUDY' | 'NETWORKING' | 'HACKATHON' | 'CONFERENCE'; +/** + * 이벤트의 기본 데이터 구조를 정의합니다. + * 이 인터페이스는 이벤트 생성, 조회, 수정에 공통적으로 사용됩니다. + */ export interface BaseEvent { title: string; startDate: string; endDate: string; startTime: string; endTime: string; bannerImageUrl: string; description: string; + /** + * 이벤트 관련 참조 링크 배열 + * title: 링크 제목 + * url: 링크 URL + * address: 선택적 주소 정보 + * detailAddress: 선택적 상세 주소 정보 + */ referenceLinks: { title: string; url: string; address?: string; detailAddress?: string; }[]; onlineType: OnlineType; address: string; + /** + * 이벤트 위치의 좌표 정보 + * lat: 위도 + * lng: 경도 + */ location: { lat: number; lng: number }; category: CategoryType; hashtags: string[]; organizerEmail: string; organizerPhoneNumber: string; }src/entities/event/api/event.ts (1)
1-7: 이벤트 상세 조회 API 기능 구현이 잘 되었습니다.이벤트 ID를 기반으로 상세 정보를 조회하는 API 함수가 깔끔하게 구현되었습니다. 다만 에러 처리 로직이 포함되어 있지 않은데, 호출 측에서 에러 핸들링을 담당하는지 확인해보세요.
에러 처리를 추가하는 것이 좋을 수 있습니다:
export const eventDetail = async (dto: EventDetailRequest) => { + try { const response = await axiosClient.get(`/events/${dto.eventId}`); return response.data; + } catch (error) { + console.error('이벤트 상세 조회 중 오류 발생:', error); + throw error; + } };src/features/dashboard/api/event.ts (1)
13-16: 이벤트 정보 업데이트 API 기능 추가가 잘 되었습니다.이벤트 ID와 부분적 업데이트 데이터를 받아 처리하는 방식으로 구현되었습니다.
Partial<UpdateEventRequest>타입을 사용해 필요한 필드만 업데이트할 수 있게 한 점이 좋습니다.다음과 같이 에러 처리 로직을 추가하면 더 견고해질 수 있습니다:
export const updateEventInfo = async (eventId: number, dto: Partial<UpdateEventRequest>) => { + try { const response = await axiosClient.put(`/events/${eventId}`, dto); return response.data; + } catch (error) { + console.error('이벤트 정보 업데이트 중 오류 발생:', error); + throw error; + } };src/features/dashboard/hook/useEventHook.ts (1)
20-29: 뮤테이션 성공/실패 처리 및 낙관적 업데이트 고려현재
useUpdateEventHook은 기본적인 API 호출만 처리하고 있습니다. 성공/실패 콜백과 낙관적 업데이트를 추가하여 사용자 경험을 개선할 수 있습니다.export const useUpdateEventHook = () => { const { id } = useParams(); const eventId = Number(id); + const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: (dto: Partial<UpdateEventRequest>) => updateEventInfo(eventId, dto), + onSuccess: () => { + // 성공 시 캐시 무효화하여 최신 데이터 불러오기 + queryClient.invalidateQueries({ queryKey: ['eventInfo', eventId] }); + }, + // 낙관적 업데이트를 위한 설정 (필요시) + // onMutate: (newData) => { ... }, }); return mutation; };src/features/event-manage/event-create/ui/TextEditor.tsx (2)
83-89: handleChange 함수 최적화 필요
handleChange함수를useCallback으로 메모이제이션하여 불필요한 리렌더링을 방지하는 것이 좋습니다.- const handleChange = (value: string) => { + const handleChange = useCallback((value: string) => { setContent(value); onChange?.(value); if (setEventState) { setEventState(prev => ({ ...prev, description: value })); } - }; + }, [onChange, setEventState]);동시에
deps배열에onChange와setEventState를 포함시켜 이들이 변경될 때만 함수가 재생성되도록 합니다.
91-93: value prop 변경 시 동기화 개선 필요현재 구현은
valueprop이 변경될 때 내부content상태를 업데이트합니다. 그러나value가undefined인 경우 빈 문자열로 설정하고 있어, 불필요한 상태 업데이트가 발생할 수 있습니다. 이전 값과 비교하여 필요할 때만 상태를 업데이트하는 것이 좋습니다.useEffect(() => { - setContent(value ?? ''); + // value가 undefined이고 content가 이미 빈 문자열이면 상태 업데이트 방지 + if (value !== undefined || content !== '') { + setContent(value ?? ''); + } - }, [value]); + }, [value, content]);src/features/event-manage/event-create/ui/LinkInput.tsx (1)
56-58: value prop이 변경될 때만 상태 업데이트하도록 최적화 필요현재 구현은
valueprop이 변경될 때 내부links상태를 항상 업데이트합니다.value의 깊은 비교를 수행하여 실제로 변경되었을 때만 상태를 업데이트하는 것이 성능 향상에 도움이 됩니다.+ import { isEqual } from 'lodash'; // lodash를 추가하거나 직접 깊은 비교 함수를 구현 useEffect(() => { - setLinks(value ?? []); + // value가 실제로 변경되었을 때만 상태 업데이트 + if (!isEqual(links, value)) { + setLinks(value ?? []); + } - }, [value]); + }, [value, links]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (23)
design-system/ui/ChoiceChip.tsx(2 hunks)src/entities/event/api/event.ts(1 hunks)src/entities/event/hook/useEventHook.ts(1 hunks)src/entities/event/model/event.ts(1 hunks)src/features/dashboard/api/event.ts(1 hunks)src/features/dashboard/hook/useEventHook.ts(1 hunks)src/features/dashboard/hook/useGetEventHook.ts(0 hunks)src/features/dashboard/model/event.ts(1 hunks)src/features/event-manage/event-create/api/event.ts(0 hunks)src/features/event-manage/event-create/model/eventCreation.ts(1 hunks)src/features/event-manage/event-create/ui/FileUpload.tsx(3 hunks)src/features/event-manage/event-create/ui/LinkInput.tsx(4 hunks)src/features/event-manage/event-create/ui/TextEditor.tsx(3 hunks)src/pages/dashboard/ui/DashbaordPage.tsx(1 hunks)src/pages/dashboard/ui/EventDetailPage.tsx(1 hunks)src/pages/dashboard/ui/EventInfoPage.tsx(1 hunks)src/pages/event-manage/ui/EventInfoPage.tsx(1 hunks)src/pages/event/ui/EventDetailsPage.tsx(4 hunks)src/shared/lib/formatEventRequest.ts(1 hunks)src/shared/types/baseEventType.ts(1 hunks)src/shared/types/dashboardType.ts(1 hunks)src/shared/types/eventDetailType.ts(0 hunks)src/shared/ui/SearchBar.tsx(1 hunks)
💤 Files with no reviewable changes (3)
- src/features/event-manage/event-create/api/event.ts
- src/shared/types/eventDetailType.ts
- src/features/dashboard/hook/useGetEventHook.ts
🧰 Additional context used
🧬 Code Graph Analysis (14)
src/features/dashboard/model/event.ts (1)
src/shared/types/baseEventType.ts (1)
BaseEvent(4-25)
src/entities/event/hook/useEventHook.ts (1)
src/entities/event/api/event.ts (1)
eventDetail(4-7)
src/pages/event-manage/ui/EventInfoPage.tsx (1)
src/features/event-manage/event-create/model/FunnelContext.tsx (1)
useFunnelState(53-59)
src/entities/event/model/event.ts (1)
src/shared/types/baseEventType.ts (1)
BaseEvent(4-25)
src/shared/lib/formatEventRequest.ts (1)
src/shared/types/baseEventType.ts (1)
BaseEvent(4-25)
src/pages/dashboard/ui/EventDetailPage.tsx (3)
src/features/dashboard/hook/useEventHook.ts (1)
useUpdateEventHook(20-29)src/features/event-manage/event-create/ui/LinkInput.tsx (1)
Link(13-18)src/shared/lib/formatEventRequest.ts (1)
formatEventRequest(3-19)
src/features/event-manage/event-create/model/eventCreation.ts (1)
src/shared/types/baseEventType.ts (1)
BaseEvent(4-25)
src/features/dashboard/hook/useEventHook.ts (3)
src/shared/types/dashboardType.ts (1)
dashboardData(27-37)src/features/dashboard/api/event.ts (2)
getHostDashboard(4-11)updateEventInfo(13-16)src/features/dashboard/model/event.ts (1)
UpdateEventRequest(3-5)
src/features/dashboard/api/event.ts (2)
src/shared/types/api/http-client.ts (1)
axiosClient(6-14)src/features/dashboard/model/event.ts (1)
UpdateEventRequest(3-5)
src/features/event-manage/event-create/ui/FileUpload.tsx (1)
src/features/event-manage/event-create/model/FunnelContext.tsx (1)
FunnelState(5-11)
src/entities/event/api/event.ts (2)
src/entities/event/model/event.ts (1)
EventDetailRequest(3-5)src/shared/types/api/http-client.ts (1)
axiosClient(6-14)
src/features/event-manage/event-create/ui/TextEditor.tsx (1)
src/features/event-manage/event-create/model/FunnelContext.tsx (1)
FunnelState(5-11)
src/pages/event/ui/EventDetailsPage.tsx (2)
src/entities/event/model/event.ts (1)
EventDetailResponse(7-15)src/entities/event/api/event.ts (1)
eventDetail(4-7)
src/features/event-manage/event-create/ui/LinkInput.tsx (1)
src/features/event-manage/event-create/model/FunnelContext.tsx (1)
FunnelState(5-11)
🪛 ESLint
src/shared/lib/formatEventRequest.ts
[error] 3-3: Unexpected any. Specify a different type.
(@typescript-eslint/no-explicit-any)
🪛 Biome (1.9.4)
src/features/event-manage/event-create/ui/LinkInput.tsx
[error] 13-13: Shouldn't redeclare 'Link'. Consider to delete it or rename it.
'Link' is defined here:
(lint/suspicious/noRedeclare)
🔇 Additional comments (33)
src/shared/ui/SearchBar.tsx (1)
7-8: 코드 변경이 적절하게 이루어졌습니다!컴포넌트를 제어(controlled) 방식으로 사용할 수 있도록
value와onChangeprops를 추가한 것은 좋은 개선사항입니다. 이를 통해 외부에서 SearchBar의 상태를 관리할 수 있게 되었으며, 다른 UI 컴포넌트들과의 일관된 패턴을 유지할 수 있습니다.Also applies to: 11-11, 17-18
design-system/ui/ChoiceChip.tsx (2)
29-33: 적절한 상태 동기화 로직입니다.외부에서 전달된
valueprop이 변경될 때 내부 상태를 업데이트하는 useEffect 훅 사용은 controlled 컴포넌트 패턴을 올바르게 구현한 것입니다.
49-54: 옵션 값과 레이블 분리가 잘 구현되었습니다.UI 렌더링 및 상태 관리에서
option.value와option.label을 적절히 분리하여 사용한 점이 좋습니다. 이 패턴은 다국어 지원과 백엔드 API 값 처리에 유용합니다.src/pages/event-manage/ui/EventInfoPage.tsx (1)
7-7: 상태 관리 방식이 개선되었습니다.
eventState를 제거하고setEventState만 사용하도록 변경한 것은 상태 관리를 상위 컴포넌트로 이동시키는 리팩토링의 일부로 적절합니다. 이제 하위 컴포넌트들(FileUpload, TextEditor, LinkInput)에setEventState를 props로 전달함으로써 데이터 흐름이 더 명확하고 예측 가능해졌습니다.이러한 변경은 PR의 목표인 "FunnelState 상태 관리를 상위 컴포넌트로 이동"과 일치하며, 컴포넌트 간 일관된 상태 관리 패턴을 구현하는 데 기여합니다.
Also applies to: 10-12
src/features/dashboard/api/event.ts (1)
4-11: 함수명 변경 및 매개변수 타입 수정이 적절합니다.
getEventInfo에서getHostDashboard로의 함수명 변경은 함수 역할을 더 명확하게 나타내고,eventId의 타입을 string에서 number로 변경한 것은 타입 안정성을 높입니다.src/features/event-manage/event-create/model/eventCreation.ts (1)
1-5: BaseEvent 인터페이스 확장을 통한 코드 중복 제거가 훌륭합니다.공통 이벤트 속성을
BaseEvent인터페이스로 분리하고,CreateEventRequest에서 이를 확장하는 방식으로 코드를 리팩토링한 것은 매우 좋은 개선입니다. 이를 통해:
- 코드 중복이 감소하고
- 타입 일관성이 향상되며
- 유지보수성이 높아졌습니다
이벤트 관련 여러 모듈 및 API에서 동일한 구조를 재사용할 수 있어 효율적입니다.
src/entities/event/model/event.ts (1)
1-15: 이벤트 상세 정보에 대한 인터페이스가 잘 정의되었습니다.이벤트 세부 정보를 표현하는 타입이 명확하게 정의되어 있습니다.
BaseEvent타입을 확장하여 추가 필드(id,participantCount,hostChannelName,hostChannelDescription,status)를 포함시킨 점이 좋습니다. 이는 PR의 API 연동 목표와 잘 일치합니다.src/features/event-manage/event-create/ui/FileUpload.tsx (4)
6-10: 컴포넌트 인터페이스가 적절하게 정의되었습니다.컴포넌트를 제어 가능한(controlled) 방식으로 변경하기 위한
value와onChange속성, 그리고 선택적setEventState속성을 추가한 것이 좋습니다. 이는 PR 목표에 명시된 컴포넌트 통합 접근 방식과 일치합니다.
12-12: props 구조분해할당이 잘 적용되었습니다.컴포넌트 매개변수에서 구조분해할당을 사용하여 props를 명확하게 추출했습니다.
31-34: 상태 업데이트 로직이 조건부로 처리되고 있습니다.업로드된 이미지 URL을
onChange콜백과setEventState를 통해 상위 컴포넌트에 전달하는 방식이 적절합니다.setEventState가 제공된 경우에만 해당 함수를 호출하는 조건부 처리도 잘 구현되었습니다.
66-68: 외부 value 값과 내부 상태 동기화가 적절히 처리되었습니다.
useEffect를 사용하여 외부에서 전달된valueprop과 내부previewUrl상태를 동기화하는 방식이 올바르게 구현되었습니다. 이를 통해 컴포넌트를 완전히 제어 가능한 방식으로 사용할 수 있습니다.src/pages/dashboard/ui/EventDetailPage.tsx (5)
8-10: 이벤트 데이터 관련 훅 사용이 적절합니다.이벤트 상세 정보를 가져오고 업데이트하기 위한 커스텀 훅을 사용한 점이 좋습니다. 이는 PR 목표에 명시된 API 연동에 부합합니다.
17-19: 상태 관리가 명확하게 구현되었습니다.필요한 상태들(bannerImageUrl, description, referenceLinks)을 개별적으로 관리하는 방식이 명확합니다.
21-27: 데이터 로딩 로직이 적절합니다.
useEffect를 사용하여 API에서 가져온 데이터로 상태를 초기화하는 방식이 좋습니다.
43-50: 성공 및 오류 처리가 적절합니다.뮤테이션 성공 및 오류 처리 로직이 잘 구현되어 있습니다. 성공 시 알림과 페이지 이동, 오류 시 적절한 알림이 제공됩니다.
58-60: 제어 컴포넌트 패턴이 일관되게 적용되었습니다.FileUpload, TextEditor, LinkInput 컴포넌트에 value와 onChange props를 일관되게 전달하는 방식이 좋습니다. 이는 PR 목표에 명시된 컴포넌트 통합 접근 방식과 일치합니다.
src/pages/dashboard/ui/EventInfoPage.tsx (9)
9-12: 필요한 훅과 유틸리티 함수가 적절히 임포트되었습니다.이벤트 상세 정보를 가져오고 업데이트하는 데 필요한 커스텀 훅과 유틸리티 함수를 임포트한 것이 좋습니다. 타입 정의도 함께 가져와 타입 안전성을 확보했습니다.
24-29: 상태 관리가 체계적으로 구현되었습니다.필요한 상태들을 개별적으로 관리하는 방식이 명확하며, 주석을 통해 상태의 목적을 설명한 것이 좋습니다.
31-55: 저장 로직이 잘 구현되었습니다.
handleSave함수에서 기존 데이터를 가져와formatEventRequest유틸리티로 처리한 후, 업데이트된 필드를 병합하여 API를 호출하는 방식이 체계적입니다. 성공과 실패에 대한 처리도 적절합니다.
58-68: 데이터 초기화 로직이 명확합니다.
useEffect를 사용하여 API에서 가져온 데이터로 각 상태를 초기화하는 방식이 체계적입니다. 데이터가 있을 때만 처리하도록 조건을 추가한 것도 좋습니다.
71-71: 동적 타이틀 표시가 적절합니다.DashboardLayout의 centerContent prop에 동적으로 타이틀 상태를 전달하여 현재 이벤트 제목을 표시하는 방식이 좋습니다.
98-101: ChoiceChip 컴포넌트 옵션 형식이 개선되었습니다.ChoiceChip 컴포넌트의 옵션을 label과 value를 가진 객체 배열로 제공하는 방식이 더 명확합니다.
104-104: ChoiceChip value 설정이 적절합니다.ChoiceChip 컴포넌트의 value prop에 선택된 옵션 또는 기존 온라인 타입을 제공하는 방식(fallback 처리)이 적절합니다.
106-111: 조건부 렌더링이 잘 구현되었습니다.오프라인 이벤트인 경우에만 주소 입력 필드를 표시하는 조건부 렌더링이 적절합니다. 또한 SearchBar 컴포넌트를 제어 가능한 방식으로 사용하여 상태 관리가 일관되게 구현되었습니다.
114-114: 버튼 클릭 핸들러가 적절히 연결되었습니다.저장 버튼에 handleSave 함수가 연결되어 사용자가 변경 사항을 저장할 수 있도록 했습니다.
src/pages/event/ui/EventDetailsPage.tsx (1)
18-19: API 통합 리팩토링 잘 진행됨이벤트 상세 정보를 가져오는 로직이 로컬 구현에서 중앙화된 API 함수로 잘 리팩토링되었습니다. 이는 코드의 일관성과 유지보수성을 향상시킵니다.
Also applies to: 52-53
src/features/dashboard/hook/useEventHook.ts (1)
1-29: 커스텀 훅 분리 및 구현 잘 진행됨이벤트 정보 조회와 업데이트를 위한 커스텀 훅이 깔끔하게 분리되어 있으며, React Query를 활용하여 비동기 상태 관리를 효과적으로 구현했습니다. 이는 관심사 분리 원칙을 잘 따르고 있습니다.
src/features/event-manage/event-create/ui/TextEditor.tsx (2)
7-11: Props 인터페이스 설계 잘 진행됨컴포넌트를 제어 컴포넌트로 리팩토링하고 명확한 props 인터페이스를 정의한 점이 좋습니다. 이를 통해 컴포넌트의 재사용성과 테스트 용이성이 향상되었습니다.
Also applies to: 33-33
62-81: modules 객체 메모이제이션 잘 구현됨
modules객체를useMemo로 메모이제이션한 것은 좋은 최적화입니다. 빈 의존성 배열을 사용하여 컴포넌트가 리렌더링될 때마다 객체가 재생성되는 것을 방지합니다.src/features/event-manage/event-create/ui/LinkInput.tsx (1)
29-33: updateAll 함수 중앙화 잘 구현됨상태 업데이트 로직을
updateAll함수로 중앙화한 것은 좋은 리팩토링입니다. 이는 코드 중복을 줄이고 일관된 상태 관리를 가능하게 합니다.src/pages/dashboard/ui/DashbaordPage.tsx (1)
7-7: import 경로 및 모듈 구조 개선 적용hook 가져오는 경로가 '../../../features/dashboard/hook/useGetEventHook'에서 '../../../features/dashboard/hook/useEventHook'로 변경되었습니다. 이는 이벤트 데이터 관련 훅을 중앙화하여 코드 구조를 개선한 리팩토링의 일부로 보입니다.
src/shared/types/dashboardType.ts (1)
50-50: 이벤트 ID 기반 동적 경로로 일관성 개선티켓 옵션 관련 메뉴 항목의 경로가 정적 경로에서 이벤트 ID 기반 동적 경로(
/dashboard/${id}/ticket/option)로 변경되었습니다. 이 변경으로 다른 대시보드 메뉴 항목들과 일관된 URL 구조를 유지할 수 있게 되었습니다.src/features/dashboard/model/event.ts (1)
1-5: 이벤트 업데이트 요청 인터페이스 추가
BaseEvent타입을 확장하여 이벤트 업데이트 API 요청에 필요한UpdateEventRequest인터페이스를 추가했습니다. 이 인터페이스는 기본 이벤트 정보 외에hostChannelId속성을 추가로 포함하여 API 연동 시 필요한 데이터 구조를 명확하게 정의하고 있습니다.
| buttonClassName = '', | ||
| value, | ||
| }: ChoiceChipProps) => { | ||
| const [selected, setSelected] = useState(value || options[0]); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
빈 options 배열에 대한 방어 코드가 필요합니다.
현재 코드는 options[0]의 값을 초기 선택값으로 사용하고 있는데, options 배열이 비어있을 경우 undefined 참조 오류가 발생할 수 있습니다. 아래와 같이 방어 코드를 추가하는 것이 좋습니다.
- const [selected, setSelected] = useState(value || options[0]);
+ const [selected, setSelected] = useState(value || (options.length > 0 ? options[0].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.
| const [selected, setSelected] = useState(value || options[0]); | |
| const [selected, setSelected] = useState( | |
| value || (options.length > 0 ? options[0].value : '') | |
| ); |
| import { useQuery } from '@tanstack/react-query'; | ||
| import { eventDetail } from '../api/event'; | ||
| import { useParams } from 'react-router-dom'; | ||
|
|
||
| const useEventDetail = () => { | ||
| const { id } = useParams(); | ||
|
|
||
| const eventId = Number(id); | ||
|
|
||
| const { data } = useQuery({ | ||
| queryKey: ['eventDetail', eventId], | ||
| queryFn: () => eventDetail({ eventId }), | ||
| }); | ||
|
|
||
| return { data }; | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
이벤트 상세 정보를 가져오는 훅이 구현되었습니다. 몇 가지 개선 사항을 제안합니다.
이벤트 상세 정보를 가져오는 중앙화된 훅의 구현은 잘 되었으나, 다음과 같은 개선 사항을 고려해 보세요:
- 에러 처리와 로딩 상태 노출이 필요합니다
- 타입 안전성 향상을 위한 반환 타입 명시가 필요합니다
id파라미터 유효성 검사가 필요합니다
아래는 개선된 구현을 제안합니다:
import { useQuery } from '@tanstack/react-query';
import { eventDetail } from '../api/event';
import { useParams } from 'react-router-dom';
+ import { EventDetailResponse } from '../model/event';
- const useEventDetail = () => {
+ const useEventDetail = () => {
const { id } = useParams();
+ // id가 없거나 유효한 숫자가 아닌 경우 처리
+ if (!id || isNaN(Number(id))) {
+ console.error('Invalid event ID');
+ return { data: undefined, isLoading: false, error: new Error('Invalid event ID') };
+ }
const eventId = Number(id);
- const { data } = useQuery({
+ const { data, isLoading, error } = useQuery<EventDetailResponse, Error>({
queryKey: ['eventDetail', eventId],
queryFn: () => eventDetail({ eventId }),
});
- return { data };
+ return { data, isLoading, error };
};
export default useEventDetail;📝 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.
| import { useQuery } from '@tanstack/react-query'; | |
| import { eventDetail } from '../api/event'; | |
| import { useParams } from 'react-router-dom'; | |
| const useEventDetail = () => { | |
| const { id } = useParams(); | |
| const eventId = Number(id); | |
| const { data } = useQuery({ | |
| queryKey: ['eventDetail', eventId], | |
| queryFn: () => eventDetail({ eventId }), | |
| }); | |
| return { data }; | |
| }; | |
| import { useQuery } from '@tanstack/react-query'; | |
| import { eventDetail } from '../api/event'; | |
| import { useParams } from 'react-router-dom'; | |
| import { EventDetailResponse } from '../model/event'; | |
| const useEventDetail = () => { | |
| const { id } = useParams(); | |
| // id가 없거나 유효한 숫자가 아닌 경우 처리 | |
| if (!id || isNaN(Number(id))) { | |
| console.error('Invalid event ID'); | |
| return { data: undefined, isLoading: false, error: new Error('Invalid event ID') }; | |
| } | |
| const eventId = Number(id); | |
| const { data, isLoading, error } = useQuery<EventDetailResponse, Error>({ | |
| queryKey: ['eventDetail', eventId], | |
| queryFn: () => eventDetail({ eventId }), | |
| }); | |
| return { data, isLoading, error }; | |
| }; | |
| export default useEventDetail; |
| const formatData = formatEventRequest(data.result); | ||
|
|
||
| mutate( | ||
| { | ||
| ...formatData, | ||
| bannerImageUrl, | ||
| description, | ||
| referenceLinks, | ||
| hostChannelId: 1, |
There was a problem hiding this comment.
이벤트 업데이트 요청 데이터 구성에 문제가 있습니다.
40번 줄에서 hostChannelId가 하드코딩된 값(1)으로 설정되어 있습니다. 이는 모든 이벤트에 적합하지 않을 수 있으며, 실제 이벤트의 hostChannelId를 사용하는 것이 바람직합니다.
- hostChannelId: 1,
+ hostChannelId: data?.result.hostChannelId || 1, // 기존 hostChannelId를 사용하거나 기본값 제공| const [event, setEvent] = useState<EventDetailResponse | null>(null); | ||
| const eventId = 1; //수정 필요 |
There was a problem hiding this comment.
이벤트 ID 하드코딩 문제
현재 eventId가 1로 하드코딩되어 있고 "수정 필요" 주석이 달려 있습니다. 실제 이벤트 ID를 URL 파라미터나 props에서 가져오도록 수정해야 합니다.
- const eventId = 1; //수정 필요
+ const { id } = useParams();
+ const eventId = Number(id);Committable suggestion skipped: line range outside the PR's diff.
| export const useGetEventHook = () => { | ||
| const { id } = useParams(); | ||
|
|
||
| const eventId = Number(id); | ||
|
|
||
| const { data: eventInfo } = useQuery<dashboardData>({ | ||
| queryKey: ['eventInfo', eventId], | ||
| queryFn: () => getHostDashboard(eventId), | ||
| }); | ||
|
|
||
| return { eventInfo }; | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
캐시 설정과 오류 처리 개선 필요
useGetEventHook이 React Query를 사용하여 잘 구현되었지만, 캐시 무효화 정책과 오류 처리가 누락되어 있습니다. 이벤트 데이터가 변경될 때 적절히 갱신되도록 추가 설정이 필요합니다.
export const useGetEventHook = () => {
const { id } = useParams();
const eventId = Number(id);
- const { data: eventInfo } = useQuery<dashboardData>({
+ const { data: eventInfo, error, isLoading } = useQuery<dashboardData>({
queryKey: ['eventInfo', eventId],
queryFn: () => getHostDashboard(eventId),
+ staleTime: 5 * 60 * 1000, // 5분 동안 데이터를 '신선'하게 유지
+ retry: 1, // 실패 시 1번만 재시도
+ refetchOnWindowFocus: true, // 창이 포커스를 받을 때 데이터 새로고침
});
- return { eventInfo };
+ return { eventInfo, error, isLoading };
};📝 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.
| export const useGetEventHook = () => { | |
| const { id } = useParams(); | |
| const eventId = Number(id); | |
| const { data: eventInfo } = useQuery<dashboardData>({ | |
| queryKey: ['eventInfo', eventId], | |
| queryFn: () => getHostDashboard(eventId), | |
| }); | |
| return { eventInfo }; | |
| }; | |
| export const useGetEventHook = () => { | |
| const { id } = useParams(); | |
| const eventId = Number(id); | |
| const { data: eventInfo, error, isLoading } = useQuery<dashboardData>({ | |
| queryKey: ['eventInfo', eventId], | |
| queryFn: () => getHostDashboard(eventId), | |
| staleTime: 5 * 60 * 1000, // 5분 동안 데이터를 '신선'하게 유지 | |
| retry: 1, // 실패 시 1번만 재시도 | |
| refetchOnWindowFocus: true, // 창이 포커스를 받을 때 데이터 새로고침 | |
| }); | |
| return { eventInfo, error, isLoading }; | |
| }; |
| import { FunnelState } from '../model/FunnelContext'; | ||
| import AddButton from '../../../../../public/assets/event-manage/creation/AddBtn.svg'; | ||
| import CloseButton from '../../../../../public/assets/event-manage/creation/CloseBtn.svg'; | ||
| import Link from '../../../../../public/assets/event-manage/creation/Link.svg'; |
There was a problem hiding this comment.
'Link' 명칭 충돌 해결 필요
Link 인터페이스와 가져온 SVG 이미지 Link가 이름 충돌을 일으키고 있습니다. 둘 중 하나의 이름을 변경하여 충돌을 해결해야 합니다.
- import Link from '../../../../../public/assets/event-manage/creation/Link.svg';
+ import LinkIcon from '../../../../../public/assets/event-manage/creation/Link.svg';
export interface Link {
title: string;
url: string;
address: string;
detailAddress: string;
}그리고 JSX에서도 아이콘 사용 부분을 변경해야 합니다:
- <img src={Link} alt="링크 이미지" className="p-2" />
+ <img src={LinkIcon} alt="링크 이미지" className="p-2" />Also applies to: 13-18
| onChange={e => updateLink(index, 'title', e.target.value)} | ||
| className="w-full min-w-[3rem] md:min-w-[6rem] h-8 text-placeholderText ml-1 outline-none bg-transparent text-sm md:text-base" | ||
| placeholder="참조링크" | ||
| autoFocus={activeInput.field === link.title && activeInput.field === 'title'} |
There was a problem hiding this comment.
autoFocus 로직 오류 수정 필요
현재 autoFocus 조건 (activeInput.field === link.title && activeInput.field === 'title')에 문제가 있습니다. activeInput.field는 'title', 'url', 또는 null 중 하나인데, 이를 link.title(문자열)과 비교하면 항상 false가 반환됩니다.
- autoFocus={activeInput.field === link.title && activeInput.field === 'title'}
+ autoFocus={activeInput.field === 'title'}📝 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.
| autoFocus={activeInput.field === link.title && activeInput.field === 'title'} | |
| autoFocus={activeInput.field === 'title'} |
24a5029 to
ad7a285
Compare
✅ 주요 작업 내용 (대시보드 이벤트 정보 API 연동)
이벤트 기본 정보 페이지에 이벤트 조회 API 연동 (eventDetail)
이벤트 상세 정보 페이지에 이벤트 조회 API 연동
기본 정보 수정 API 연동 (updateEventInfo)
상세 정보 수정 API 연동
🔧 리팩토링 및 구조 정리
FunnelState 상태관리를 상위에서 관리하도록 구조 변경
FileUpload, TextEditor, LinkInput 컴포넌트에 setEventState props 전달 방식으로 통일
이벤트 공통 필드 추출 함수 formatEventRequest 생성 및 적용
Summary by CodeRabbit
신규 기능
버그 수정
리팩터
기타