Conversation
Walkthrough이번 변경 사항은 티켓 관련 API와 React 컴포넌트의 데이터 흐름을 전반적으로 리팩토링한 것입니다. API 함수들은 객체 내 메서드에서 독립적인 async 함수로 변경되었고, 타입 선언 파일이 통합 및 재구성되었습니다. 새로운 React Query 기반 커스텀 훅들이 도입되어 티켓 조회, 생성, 삭제 로직이 각 컴포넌트에서 분리되어 재사용 가능하게 되었습니다. 여러 컴포넌트가 이 훅을 활용하도록 리팩토링되어 데이터 상태 관리와 에러/로딩 처리 방식이 일관적으로 개선되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as 컴포넌트
participant Hook as useTickets / useCreateTicket / useDeleteTicket
participant API as API 함수
participant Server as 서버
UI->>Hook: 데이터 요청 (예: useTickets(eventId))
Hook->>API: readTicket(eventId)
API->>Server: GET /tickets?eventId
Server-->>API: 티켓 데이터 반환
API-->>Hook: { isSuccess, result }
Hook-->>UI: 데이터, 로딩/에러 상태 제공
UI->>Hook: 티켓 생성 요청 (useCreateTicket)
Hook->>API: createTicket(data)
API->>Server: POST /tickets
Server-->>API: 생성 결과 반환
API-->>Hook: TicketResponse
Hook-->>UI: 성공/실패 처리, 알림, 새로고침
UI->>Hook: 티켓 삭제 요청 (useDeleteTicket)
Hook->>API: deleteTicket(ticketId)
API->>Server: DELETE /tickets/{ticketId}
Server-->>API: 삭제 결과 반환
API-->>Hook: TicketResponse
Hook-->>UI: 성공/실패 처리, 알림, 새로고침
Suggested labels
Suggested reviewers
Poem
✨ 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: 4
🔭 Outside diff range comments (1)
src/features/ticket/model/TicketContext.tsx (1)
26-28:⚠️ Potential issueticketChannelId 속성이 CreateTicketRequest 인터페이스에 정의되어 있지 않습니다.
setTicketChannelId 함수에서 ticketChannelId 속성을 사용하고 있지만, CreateTicketRequest 인터페이스에는 이 속성이 정의되어 있지 않습니다. 이로 인해 타입 오류가 발생할 수 있습니다.
다음 두 가지 방법 중 하나로 해결할 수 있습니다:
- CreateTicketRequest 인터페이스에 ticketChannelId 속성 추가:
export interface CreateTicketRequest { eventId: number; ticketType: string; ticketName: string; ticketDescription: string; ticketPrice: number; availableQuantity: number; startDate: string; endDate: string; startTime: string; endTime: string; + ticketChannelId?: number; }
- 타입 단언 사용:
- setTicketState(prev => ({ ...prev, ticketChannelId })); + setTicketState(prev => ({ ...prev, ticketChannelId } as CreateTicketRequest));
🧹 Nitpick comments (5)
src/features/ticket/model/ticketInformation.ts (1)
14-19: result 필드의 타입을 개선하는 것이 좋겠습니다.result 필드가 문자열 타입으로 정의되어 있고, 주석으로 "ticketId: 2"와 같은 형식임을 설명하고 있습니다. 이는 프로그래밍적으로 활용하기 어려울 수 있습니다.
다음과 같이 더 명확한 구조로 변경하는 것을 고려해보세요:
export interface TicketResponse { isSuccess: boolean; code: string; message: string; - result: string; // "ticketId: 2" + result: { ticketId: number }; }src/features/ticket/api/ticket.ts (1)
16-19: deleteTicket 함수의 반환 타입을 명시하는 것이 좋겠습니다.readTicket 함수처럼 deleteTicket 함수도 명시적인 반환 타입을 지정하는 것이 일관성 있고 타입 안전성을 높일 수 있습니다.
- export const deleteTicket = async (ticketId: number) => { + export const deleteTicket = async (ticketId: number): Promise<{ isSuccess: boolean; message: string }> => { const response = await axiosClient.delete(`/tickets/${ticketId}`); return response.data; }src/pages/dashboard/ui/ticket/TicketCreatePage.tsx (1)
13-16: 이벤트 ID를 URL에서 동적으로 추출하는 방식으로 개선되었습니다.기존 하드코딩된 이벤트 ID 대신
useParams를 사용해 URL에서 동적으로 ID를 추출하는 방식이 좋습니다. 다만, 숫자가 아닌 ID에 대한 처리가 부족합니다.다음과 같이 유효성 검사를 추가하는 것이 좋겠습니다:
const { id } = useParams(); - const eventId = Number(id); + const eventId = id ? Number(id) : 0; + if (!eventId) { + navigate('/dashboard'); + return; + }src/widgets/event/ui/TicketInfo.tsx (2)
15-22: 데이터 접근 방식이 새로운 API 응답 구조에 맞게 업데이트되었습니다.데이터 구조가 변경되어
data.result를 사용하도록 수정된 점이 좋습니다. 하지만 여기서 null 체크를 더 안전하게 할 수 있습니다.선택적 체이닝(optional chaining)을 사용하여 코드를 개선해보세요:
- if (data && data.isSuccess) { + if (data?.isSuccess) { const initialQuantity: { [key: number]: number } = {}; data.result.forEach(ticket => { initialQuantity[ticket.ticketId] = 1; }); setQuantity(initialQuantity); }🧰 Tools
🪛 Biome (1.9.4)
[error] 15-15: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
59-59: 사용하지 않는 'error' 변수가 있습니다.catch 블록에서 'error' 변수가 선언되었지만 사용되지 않고 있습니다.
디버깅 정보 출력이나 로깅이 필요한 경우가 아니라면 다음과 같이 수정하세요:
- } catch (error) { + } catch (_) { alert("티켓 구매 중 오류가 발생했습니다."); }🧰 Tools
🪛 ESLint
[error] 59-59: 'error' is defined but never used.
(@typescript-eslint/no-unused-vars)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
src/features/ticket/api/ticket.ts(1 hunks)src/features/ticket/hooks/useTicketHook.ts(1 hunks)src/features/ticket/model/TicketContext.tsx(1 hunks)src/features/ticket/model/ticketCreation.ts(0 hunks)src/features/ticket/model/ticketInformation.ts(1 hunks)src/pages/dashboard/ui/ticket/TicketCreatePage.tsx(2 hunks)src/pages/dashboard/ui/ticket/TicketListPage.tsx(2 hunks)src/widgets/dashboard/ui/TicketItem.tsx(2 hunks)src/widgets/event/ui/TicketInfo.tsx(2 hunks)
💤 Files with no reviewable changes (1)
- src/features/ticket/model/ticketCreation.ts
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/pages/dashboard/ui/ticket/TicketCreatePage.tsx (3)
src/features/ticket/api/ticket.ts (1)
createTicket(4-7)src/features/ticket/hooks/useTicketHook.ts (1)
useCreateTicket(13-24)src/features/ticket/model/ticketInformation.ts (1)
CreateTicketRequest(1-12)
src/pages/dashboard/ui/ticket/TicketListPage.tsx (1)
src/features/ticket/hooks/useTicketHook.ts (1)
useTickets(5-11)
src/features/ticket/api/ticket.ts (2)
src/features/ticket/model/ticketInformation.ts (2)
CreateTicketRequest(1-12)ReadTicketResponse(21-27)src/shared/types/api/http-client.ts (1)
axiosClient(6-13)
src/widgets/event/ui/TicketInfo.tsx (2)
src/features/ticket/hooks/useTicketHook.ts (1)
useTickets(5-11)src/features/ticket/api/order.ts (1)
orderTickets(22-25)
src/features/ticket/hooks/useTicketHook.ts (2)
src/features/ticket/model/ticketInformation.ts (3)
ReadTicketResponse(21-27)TicketResponse(14-19)CreateTicketRequest(1-12)src/features/ticket/api/ticket.ts (3)
readTicket(9-14)createTicket(4-7)deleteTicket(16-19)
🪛 Biome (1.9.4)
src/widgets/event/ui/TicketInfo.tsx
[error] 15-15: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🪛 ESLint
src/widgets/event/ui/TicketInfo.tsx
[error] 59-59: 'error' is defined but never used.
(@typescript-eslint/no-unused-vars)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: storybook
🔇 Additional comments (18)
src/features/ticket/model/ticketInformation.ts (2)
1-12: 잘 정의된 티켓 생성 인터페이스입니다.티켓 생성에 필요한 모든 필드들이 적절한 타입으로 잘 정의되어 있습니다.
21-27: 읽기 응답 인터페이스에 시간 관련 필드가 누락되었습니다.CreateTicketRequest에는 startDate, endDate, startTime, endTime 필드가 있지만, ReadTicketResponse에는 이 필드들이 없습니다. 의도적인 설계라면 문제가 없지만, 클라이언트 측에서 티켓의 시간 정보가 필요할 수 있습니다.
src/features/ticket/model/TicketContext.tsx (1)
2-2: 임포트 경로 업데이트가 적절히 이루어졌습니다.ticketCreation에서 통합된 ticketInformation으로 임포트 경로를 변경한 것은 좋은 리팩토링입니다.
src/widgets/dashboard/ui/TicketItem.tsx (3)
5-6: 임포트가 잘 업데이트되었습니다.새로운 인터페이스와 커스텀 훅을 위한 임포트가 적절히 추가되었습니다.
8-10: 컴포넌트 리팩토링이 잘 이루어졌습니다.ReadTicketResponse 타입으로 변경하고 useDeleteTicket 훅을 활용하여 관심사를 분리한 것은 좋은 패턴입니다.
47-47: 삭제 로직이 간결하게 개선되었습니다.복잡한 인라인 핸들러 대신 훅에서 제공하는 mutate 함수를 호출하는 방식으로 단순화되었습니다.
src/features/ticket/api/ticket.ts (2)
2-2: 적절한 타입 임포트가 추가되었습니다.통합된 인터페이스 파일에서 필요한 타입을 가져오도록 업데이트되었습니다.
9-14: readTicket 함수가 개선되었습니다.독립적인 비동기 함수로 리팩토링되었고, 명시적인 반환 타입을 통해 타입 안전성이 향상되었습니다.
src/pages/dashboard/ui/ticket/TicketCreatePage.tsx (2)
8-10: 모듈 가져오기 업데이트가 잘 되었습니다.새로운 파일 구조로 import 경로가 잘 업데이트되었으며, 티켓 생성을 위한 커스텀 훅과 라우터 훅이 적절히 추가되었습니다.
19-19: 동적 이벤트 ID를 초기 상태에 적용한 점이 좋습니다.URL에서 추출한 이벤트 ID를 초기 상태에 적용한 것은 좋은 접근입니다.
src/widgets/event/ui/TicketInfo.tsx (2)
7-7: useTickets 커스텀 훅 통합이 잘 되었습니다.수동 데이터 페칭 로직을 React Query 기반 커스텀 훅으로 대체한 것은 좋은 개선입니다. 데이터 가져오기, 로딩 상태, 에러 처리가 모두 한 줄로 처리되어 코드가 간결해졌습니다.
Also applies to: 11-11
63-64: 로딩 및 에러 상태 처리가 추가되었습니다.React Query의
isLoading과isError상태를 활용하여 적절한 UI 피드백을 제공하는 것은 좋은 UX 개선입니다.src/pages/dashboard/ui/ticket/TicketListPage.tsx (4)
4-4: 필요한 훅과 API 모듈이 잘 임포트되었습니다.리팩토링에 필요한 라우터 관련 훅과 티켓 커스텀 훅이 적절히 임포트되었습니다.
Also applies to: 7-7
13-13: 동적 라우팅이 잘 구현되었습니다.정적 경로 대신 URL 파라미터에서 추출한 ID를 사용하여 동적 라우팅을 구현한 것이 좋습니다.
19-20: 로딩 및 에러 상태 처리가 적절히 추가되었습니다.React Query에서 제공하는 상태를 활용하여 로딩 중과 에러 상태에 대한 UI 처리를 추가한 것은 좋은 개선입니다.
48-49: 데이터 접근 및 조건부 렌더링이 적절히 업데이트되었습니다.새로운 API 응답 구조에 맞게 데이터 접근 방식이 업데이트되었고, 선택적 체이닝을 사용하여 안전하게 처리된 점이 좋습니다.
src/features/ticket/hooks/useTicketHook.ts (2)
1-4: 필요한 의존성과 타입을 적절히 임포트하고 있습니다.React Query와 티켓 관련 API 함수 및 타입들을 적절하게 임포트하고 있습니다.
5-11: useTickets 훅이 잘 구현되었습니다.티켓 목록을 조회하는
useTickets훅의 구현이 적절합니다. 다음 사항이 특히 좋습니다:
queryKey에 eventId를 포함시켜 캐싱과 무효화를 효과적으로 관리할 수 있음enabled옵션을 사용하여 eventId가 있을 때만 쿼리가 실행되도록 함- 타입 정의가 명확함
| createTicket(ticketData); | ||
| navigate(`/dashboard/${id}/ticket`); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
티켓 생성 로직이 React Query를 사용하여 개선되었습니다.
기존의 직접적인 API 호출 대신 React Query 기반 훅을 사용하여 코드가 간결해졌습니다. 하지만 다음 두 가지 개선점이 있습니다:
- 현재 방식은 API 호출 결과를 기다리지 않고 바로 페이지를 이동합니다.
useCreateTicket훅은 내부적으로window.location.reload()를 호출하는데, 페이지 이동과 충돌할 수 있습니다.
성공 여부를 확인한 후 페이지를 이동하는 방식이 더 안정적일 것입니다:
- createTicket(ticketData);
- navigate(`/dashboard/${id}/ticket`);
+ createTicket(ticketData, {
+ onSuccess: () => {
+ navigate(`/dashboard/${id}/ticket`);
+ }
+ });이 변경을 위해서는 useTicketHook.ts에서 useCreateTicket 훅도 함께 수정해야 합니다:
// useTicketHook.ts에서 수정 필요
export const useCreateTicket = () => {
return useMutation<TicketResponse, Error, CreateTicketRequest>({
mutationFn: createTicket,
onSuccess: () => {
alert('티켓이 성공적으로 저장되었습니다.');
// window.location.reload(); // 제거
},
onError: () => {
alert('티켓 저장에 실패했습니다. 다시 시도해주세요.');
},
});
};| export const useDeleteTicket = () => { | ||
| return useMutation<TicketResponse, Error, number>({ | ||
| mutationFn: deleteTicket, | ||
| onSuccess: () => { | ||
| alert("티켓이 삭제되었습니다."); | ||
| window.location.reload(); | ||
| }, | ||
| onError: () => { | ||
| alert('티켓 삭제 중 오류가 발생했습니다.'); | ||
| } | ||
| }); | ||
| }; No newline at end of file |
There was a problem hiding this comment.
🛠️ Refactor suggestion
useDeleteTicket 훅에도 동일한 개선이 필요합니다.
삭제 훅도 마찬가지로 페이지 리로드 대신 쿼리 무효화를 통한 데이터 갱신 방식으로 개선이 필요합니다.
다음과 같이 개선해보세요:
export const useDeleteTicket = () => {
+ const queryClient = useQueryClient();
return useMutation<TicketResponse, Error, number>({
mutationFn: deleteTicket,
onSuccess: () => {
alert("티켓이 삭제되었습니다.");
- window.location.reload();
+ // 모든 티켓 쿼리 무효화
+ queryClient.invalidateQueries({ queryKey: ['tickets'] });
},
onError: () => {
alert('티켓 삭제 중 오류가 발생했습니다.');
}
});
};또한 삭제 함수에 이벤트 ID를 함께 전달하여 특정 이벤트의 티켓 쿼리만 무효화하는 방식으로도 구현할 수 있습니다:
export const useDeleteTicket = () => {
const queryClient = useQueryClient();
return useMutation<TicketResponse, Error, { ticketId: number, eventId: number }>({
mutationFn: ({ ticketId }) => deleteTicket(ticketId),
onSuccess: (_, { eventId }) => {
alert("티켓이 삭제되었습니다.");
queryClient.invalidateQueries(['tickets', eventId]);
},
// ...
});
};이 방식을 사용하면 특정 이벤트의 티켓 목록만 갱신할 수 있어 더 효율적입니다.
| export const useCreateTicket = () => { | ||
| return useMutation<TicketResponse, Error, CreateTicketRequest>({ | ||
| mutationFn: createTicket, | ||
| onSuccess: () => { | ||
| alert('티켓이 성공적으로 저장되었습니다.'); | ||
| window.location.reload(); | ||
| }, | ||
| onError: () => { | ||
| alert('티켓 저장에 실패했습니다. 다시 시도해주세요.'); | ||
| }, | ||
| }); | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
useCreateTicket 훅에 개선이 필요합니다.
티켓 생성을 위한 훅이 잘 구현되었지만, 페이지 리로드 방식에 개선이 필요합니다.
현재 방식은 window.location.reload()를 사용하여 전체 페이지를 새로고침하고 있습니다. 이는 사용자 경험을 저하시키고, React Query의 장점을 충분히 활용하지 못합니다. 다음과 같이 개선해보세요:
export const useCreateTicket = () => {
return useMutation<TicketResponse, Error, CreateTicketRequest>({
mutationFn: createTicket,
onSuccess: () => {
alert('티켓이 성공적으로 저장되었습니다.');
- window.location.reload();
+ // 쿼리 무효화를 통해 데이터 갱신
+ // queryClient.invalidateQueries(['tickets']);
},
onError: () => {
alert('티켓 저장에 실패했습니다. 다시 시도해주세요.');
},
});
};이를 위해서는 QueryClient 인스턴스에 대한 접근이 필요합니다. 다음과 같이 모듈 최상단에 useQueryClient를 추가하고 훅 내부에서 사용할 수 있습니다:
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
// 훅 내부
export const useCreateTicket = () => {
const queryClient = useQueryClient();
return useMutation<TicketResponse, Error, CreateTicketRequest>({
// ...
onSuccess: (data, variables) => {
alert('티켓이 성공적으로 저장되었습니다.');
queryClient.invalidateQueries(['tickets', variables.eventId]);
},
// ...
});
};|
귀여우신 분이였군요 👍🏻 |
Summary by CodeRabbit
New Features
useTickets,useCreateTicket,useDeleteTicket)이 추가되었습니다.Refactor
Bug Fixes
Style