feat: 마이페이지 예약 현황 API 연동#82
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthrough마이페이지 예약 현황을 API 기반으로 전환했습니다. 신규 Changes
Sequence DiagramsequenceDiagram
participant User as 사용자
participant Page as ReservationPage
participant API as bookings API
participant Server as Backend
User->>Page: 탭 선택 / 취소 클릭
Page->>Page: useEffect / handleCancel 실행
Page->>API: getBookings(status,page) / cancelBooking(id,reason)
API->>Server: GET /bookings?status=...&page=... / PATCH /bookings/{id}
Server-->>API: BookingResponse / 취소 응답
API-->>Page: 매핑된 Reservation[] / 취소 결과
Page->>Page: 상태 업데이트 (reservations, loading)
Page-->>User: 화면에 최신 예약 목록 또는 알림 표시
🎯 Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Suggested labels
Suggested reviewers
검토 포인트 (버그·안정성·성능·구조 중심, 근거 + 대안)
const fetchReservations = async () => {
setLoading(true);
try {
const status = tabStatusMap[activeTab];
const res = await getBookings(status, 1);
setReservations(res.reservations.map(mapper));
} catch (e) {
console.error('fetchReservations failed', e);
setReservations([]);
setError('예약 정보를 불러오지 못했습니다.');
} finally {
setLoading(false);
}
};
export const mapApiToUiStatus = (s: ApiBookingStatus): BookingStatus => ({
CONFIRMED: '예약 확정',
COMPLETED: '방문 완료',
CANCELED: '취소됨'
}[s]);
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In `@src/api/bookings.ts`:
- Around line 29-35: Replace the loose typing and blind assertion in
getBookings: create a small params interface (e.g., BookingParams { page:
number; status?: ApiBookingStatus }) and use it instead of params: any, and stop
using an unconditional cast to BookingResponse; instead, type the axios call
with a response shape (or check response.data.result) and validate that
response.data && response.data.result exist and match expected shape before
returning — if the result is missing or invalid, throw or return a clear
error/nullable value rather than silently casting. Target symbols: getBookings,
BookingParams (new), ApiBookingStatus, BookingResponse, and
response.data.result.
In `@src/pages/myPage/reservationPage.tsx`:
- Line 24: The two useState declarations are combined on one line which hurts
readability; split the single line "const [reservations, setReservations] =
useState<Reservation[]>([]); const [loading, setLoading] = useState(false);"
into two separate statements so that "const [reservations, setReservations] =
useState<Reservation[]>([]);" and "const [loading, setLoading] =
useState(false);" each appear on their own line (referencing the
reservations/setReservations and loading/setLoading hooks in
reservationPage.tsx).
- Around line 46-47: Remove the stray debug console.log and the unnecessary
unsafe cast: delete the console.log("fetchReservations status:", apiStatus) and
call getBookings(apiStatus) without using "as any"; apiStatus already matches
the expected union type ("CONFIRMED" | "COMPLETED" | "CANCELED" | undefined) so
pass it directly to getBookings to restore type safety in reservationPage.tsx.
- Around line 49-56: The mapping over data.bookingList can throw if bookingList
is null/undefined; update the mapped creation in reservationPage.tsx to
defensively handle missing lists by using a default empty array (e.g., replace
data.bookingList.map(...) with (data.bookingList ?? []).map(...) or
data.bookingList?.map(...) ?? []) so mapped: Reservation[] is always created
from an array and avoid runtime errors; keep the same field mappings (id ->
bookingId, shopName -> storeName, etc.) and ensure people/time/payment fall back
as before.
- Around line 74-78: Add an explicit error state and show an error UI when the
API call fails: declare a state like const [error, setError] = useState<string |
null>(null) in reservationPage.tsx, set setError(errorMessage) inside the catch
block where console.error("예약 내역 조회 실패", error) is called, clear error
(setError(null)) when starting the fetch (before setLoading(true)), and update
the component render logic to show a dedicated error view when error is non-null
instead of the empty/“no data” screen; keep setLoading(false) in finally so
loading still resolves.
🧹 Nitpick comments (7)
src/api/bookings.ts (2)
37-39:cancelBooking의 반환 타입이any로 추론됩니다.
response.data는 타입이 지정되지 않아 호출 측에서 타입 안전성이 보장되지 않습니다. 반환 타입을 명시하거나 제네릭을 활용해 주세요.♻️ 개선 제안
-export const cancelBooking = async (bookingId: number, reason: string = "사용자 취소") => { - const response = await api.patch(`/api/v1/bookings/${bookingId}/cancel`, { reason }); - return response.data; +interface CancelBookingResponse { + // API 응답 구조에 맞게 필드 정의 + isSuccess: boolean; + message: string; +} + +export const cancelBooking = async (bookingId: number, reason: string = "사용자 취소"): Promise<CancelBookingResponse> => { + const response = await api.patch<CancelBookingResponse>(`/api/v1/bookings/${bookingId}/cancel`, { reason }); + return response.data; };As per coding guidelines,
src/api/**: 응답 타입 안전성 유지.
3-3:BookingStatus타입은 사용되지 않으니 제거해도 괜찮습니다.
BookingStatus가 export되었지만 코드 전체에서 실제로 import되거나 사용되는 곳이 없네요.ApiBookingStatus는 API 응답을 위해 실제로 사용 중이니, 혼동을 피하고 코드를 깔끔하게 유지하기 위해 미사용 타입은 제거하는 것을 권장합니다.src/pages/myPage/reservationPage.tsx (5)
26-79: 들여쓰기가 일관되지 않습니다.
fetchReservations함수 내부에서try블록의 들여쓰기(4칸)와switch문의case들여쓰기(6칸 → 8칸으로 바뀜)가 혼재되어 있어요. 포맷터(Prettier 등)를 한번 돌려주시면 깔끔해질 것 같습니다.
81-83:useEffect의 의존성 배열에fetchReservations가 빠져 있어 ESLint 경고가 발생할 수 있습니다.
fetchReservations가 컴포넌트 내부에 정의되어 있고activeTab을 클로저로 캡처하므로,react-hooks/exhaustive-deps규칙에 위배됩니다.useCallback으로 감싸거나,fetchReservations를useEffect안으로 인라인하는 방법이 있습니다.다만,
fetchReservations를onCancel콜백으로도 사용하고 있으므로useCallback이 더 적합합니다.♻️ useCallback 적용 예시
const fetchReservations = useCallback(async () => { // ... 기존 로직 }, [activeTab]); useEffect(() => { fetchReservations(); }, [fetchReservations]);
4-5: 같은 모듈에서의 import를 하나로 합칠 수 있어요.♻️ 개선 제안
-import { getBookings } from "@/api/bookings"; -import { cancelBooking } from "@/api/bookings"; +import { getBookings, cancelBooking } from "@/api/bookings";
114-124: 페이지네이션 미지원 — 현재 항상 1페이지만 조회합니다.
getBookingsAPI는page파라미터를 지원하고,BookingResponse에는totalPage,isLast등 페이지네이션 정보가 있지만, 현재 UI에서는 항상 첫 페이지만 요청하고 있어요. 예약이 많은 사용자의 경우 일부 데이터만 보이게 됩니다. 당장은 아니더라도 후속 작업으로 페이지네이션이나 무한 스크롤을 고려해 주세요.
129-145: 취소 후 목록 새로고침 시 race condition 가능성이 있습니다.
handleCancel에서cancelBooking성공 후onCancel()(=fetchReservations)을 호출하는데, 컴포넌트가 unmount된 상태에서setLoading(false)가 호출될 수 있습니다 (예: 탭 전환 등). 현재로서는 React 18+에서 경고만 발생하고 실질적 오류는 없지만, 안정성을 위해AbortController나 unmount 체크를 고려할 수 있어요.또한,
confirm()과alert()는 브라우저 네이티브 다이얼로그라 UX가 제한적입니다. 프로젝트에 모달 컴포넌트가 있다면 그쪽으로 전환하는 것도 좋겠습니다.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/pages/myPage/reservationPage.tsx`:
- Line 152: The div's className uses an invalid Tailwind class
`transition-hover`; replace it with a valid transition utility such as
`transition-shadow` (or `transition`) so `hover:shadow-md` will animate smoothly
— update the div with the `className="rounded-2xl border border-gray-200 p-6
transition-shadow hover:shadow-md"` (or `transition`) in the component that
renders this div (e.g., the reservation page component).
🧹 Nitpick comments (3)
src/pages/myPage/reservationPage.tsx (3)
4-5: 같은 모듈에서 import를 두 번 하고 있어요.한 줄로 합치면 깔끔합니다.
♻️ 개선 제안
-import { getBookings } from "@/api/bookings"; -import { cancelBooking } from "@/api/bookings"; +import { getBookings, cancelBooking } from "@/api/bookings";
83-85:useEffect의 의존성 배열에fetchReservations가 누락되어 있어요.
fetchReservations는activeTab클로저를 캡처하는 함수인데, 의존성 배열에activeTab만 있어 동작은 합니다. 하지만react-hooks/exhaustive-deps린트 규칙에 걸릴 수 있고, 향후 함수에 다른 state 의존성이 추가되면 stale closure 버그로 이어질 수 있습니다.
useCallback으로 감싸거나, effect 내부에 fetch 로직을 인라인하는 방식을 권장합니다.♻️ useCallback 적용 예시
+import { useCallback } from "react"; // ... -const fetchReservations = async () => { +const fetchReservations = useCallback(async () => { // ... 기존 로직 -}; +}, [activeTab]); useEffect(() => { fetchReservations(); -}, [activeTab]); +}, [fetchReservations]);
136-149:confirm()/alert()사용은 기능상 문제없지만, 추후 커스텀 모달로 전환을 권장해요.브라우저 네이티브 다이얼로그는 스타일링이 불가능하고, 접근성(키보드 포커스 복원 등) 면에서도 제약이 있습니다. 현재 단계에서는 괜찮지만, UI 구성 태스크(이슈
#79)에서 같이 개선하시면 좋겠습니다.
💡 개요
마이페이지 예약 현황 API 연동
🔢 관련 이슈 링크
💻 작업내용
📌 변경사항PR
🤔 추가 논의하고 싶은 내용
✅ 체크리스트
Summary by CodeRabbit