Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions src/app/(with-header)/mypage/reservations/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
useMyReservations,
useCancelReservation,
useCreateReview,
invalidateActivityQueries,
} from '@/hooks/useReservationQueries';
import { FilterOption } from '@/constants/reservationConstants';
import useInfiniteScroll from '@/hooks/useInfiniteScroll';
Expand Down Expand Up @@ -102,23 +103,21 @@ export default function MyReservationsPage() {
// 후기 작성 확인
const handleReviewConfirm = (rating: number, content: string) => {
if (reviewModal.reservationId) {
const reservation = allReservations.find(
(r) => r.id === reviewModal.reservationId,
);

createReviewMutation.mutate(
{
reservationId: reviewModal.reservationId,
data: { rating, content },
},
{
onSuccess: () => {
setReviewModal({
isOpen: false,
reservationId: null,
activityTitle: null,
activityImage: null,
activityDate: null,
activityTime: null,
headCount: null,
totalPrice: null,
});
// 성공 후 추가 쿼리 무효화
if (reservation?.activity.id) {
invalidateActivityQueries(reservation.activity.id);
}
Comment on lines +109 to +123
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

리뷰 생성 후 캐시 무효화 로직이 잘 구현되었습니다.

후기 작성 성공 시 관련 액티비티 쿼리들을 무효화하여 실시간 업데이트를 보장합니다. 예약 정보를 찾아 액티비티 ID를 전달하는 로직도 적절합니다.

다만 reservation을 찾지 못할 경우에 대한 방어 로직을 고려해보세요.

선택적 개선사항으로 다음과 같은 방어 로직을 추가할 수 있습니다:

  onSuccess: () => {
    // 성공 후 추가 쿼리 무효화
-   if (reservation?.activity.id) {
+   if (reservation?.activity?.id) {
      invalidateActivityQueries(reservation.activity.id);
+   } else {
+     console.warn('예약 정보를 찾을 수 없어 캐시 무효화를 건너뜁니다.');
    }
  },
📝 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.

Suggested change
const reservation = allReservations.find(
(r) => r.id === reviewModal.reservationId,
);
createReviewMutation.mutate(
{
reservationId: reviewModal.reservationId,
data: { rating, content },
},
{
onSuccess: () => {
setReviewModal({
isOpen: false,
reservationId: null,
activityTitle: null,
activityImage: null,
activityDate: null,
activityTime: null,
headCount: null,
totalPrice: null,
});
// 성공 후 추가 쿼리 무효화
if (reservation?.activity.id) {
invalidateActivityQueries(reservation.activity.id);
}
const reservation = allReservations.find(
(r) => r.id === reviewModal.reservationId,
);
createReviewMutation.mutate(
{
reservationId: reviewModal.reservationId,
data: { rating, content },
},
{
onSuccess: () => {
// 성공 후 추가 쿼리 무효화
if (reservation?.activity?.id) {
invalidateActivityQueries(reservation.activity.id);
} else {
console.warn('예약 정보를 찾을 수 없어 캐시 무효화를 건너뜁니다.');
}
},
},
);
🤖 Prompt for AI Agents
In src/app/(with-header)/mypage/reservations/page.tsx around lines 109 to 123,
the code finds a reservation by ID but lacks handling for the case when no
matching reservation is found. Add a defensive check to verify that reservation
exists before accessing its properties, such as reservation.activity.id, to
prevent runtime errors. If reservation is undefined, skip the cache invalidation
logic or handle the case appropriately.

},
},
);
Expand Down
21 changes: 20 additions & 1 deletion src/hooks/useMyActivitiesQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
deleteMyActivity,
} from '@/apis/myActivities';
import { toast } from 'sonner';
import { AxiosError } from 'axios';

export const MY_ACTIVITIES_QUERY_KEYS = {
ALL: ['my-activities'] as const,
Expand Down Expand Up @@ -54,7 +55,25 @@ export const useDeleteMyActivity = () => {
toast.success('체험이 삭제되었습니다.');
},
onError: (error) => {
toast.error(`체험 삭제 실패: ${error.message}`);
const axiosError = error as AxiosError;
const status = axiosError.response?.status;

switch (status) {
case 400:
toast.error('예약이 있는 체험은 삭제할 수 없습니다.');
break;
case 401:
toast.error('로그인이 필요합니다.');
break;
case 403:
toast.error('삭제 권한이 없습니다.');
break;
case 404:
toast.error('존재하지 않는 체험입니다.');
break;
default:
toast.error('체험 삭제에 실패했습니다. 다시 시도해주세요.');
}
},
});
};
25 changes: 23 additions & 2 deletions src/hooks/useReservationQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,36 @@ export const useCreateReview = () => {
reservationId: number;
data: CreateReviewRequest;
}) => createReview(reservationId, data),
onSuccess: () => {
// 예약 리스트 캐시 무효화하여 reviewSubmitted 상태 업데이트
onSuccess: (data, variables) => {
// 기존 쿼리 무효화
queryClient.invalidateQueries({
queryKey: RESERVATION_QUERY_KEYS.RESERVATIONS,
});

toast.success('후기가 작성되었습니다.');
},
onError: (error) => {
toast.error(`후기 작성 실패: ${error.message}`);
},
});
};

// 쿼리 무효화 함수
export const invalidateActivityQueries = (activityId: number) => {
const queryClient = useQueryClient();

queryClient.invalidateQueries({
queryKey: ['popularExperiences'],
});
queryClient.invalidateQueries({
queryKey: ['experiences'],
exact: false,
});
queryClient.invalidateQueries({
queryKey: ['activity', activityId.toString()],
});
queryClient.invalidateQueries({
queryKey: ['reviews'],
exact: false,
});
};
Loading