
캡스톤에서 프로젝트를 하던 중, 해당 프로젝트를 거의 다 만들었을때 그제서야 보이던 오류가 있었고 그걸 해결했던 과정을 정리해보았다.
모바일 결제 → 리다이렉트 → 리다이렉트 페이지에서 백엔드 API 호출(imp_uid 검증) → 검증 성공 시 DB에 기록 -> 기부내역 생성 (api호출) -> 결제내역 생성(api호출) → 랜덤 보상 제공(api호출)
와 같다.

더럽게 복잡하다.
사실 이 part는 내가 맡은 부분이 아니여서 잘모르는 부분(원래 알아야하는데.. 쓰읍)도 있었는데 이번 기회에 자세히 보고 디테일하게 알고가게 되었다.
문제는 1번의 기부내역, 결제내역이 생성되면 그에 맞게 랜덤 보상 api도 1번만 호출이 되어야하는데 랜덤 보상 api가 여러번 호출되어 원래 주어져야하는 보상보다 많이 제공되고 DB에 고스란히 저장되었다.
DB를 확인해 볼수 없는 상황이라 거짓말 안하고 console로 50번 넘게 출력해본거 같다.
어떻게 해결을 할까 짱꾸를 굴려봤지만... 부트캠프에서 TanstackQuery배울때 활용할수있는 option들이 많다는 생각만 떠올랐다.
블로그로 찾아보는데 어떻게 검색해야할지도 감이 잡히지않아 결국 gemini의 도움을 빌려 해결을 했다!
export const useDonation = (): UseDonationResult => { const createDonationMutation = useMutation< DonationCreateResponse, Error, DonationCreateData ({ mutationFn: (donationData: DonationCreateData) => createDonation(donationData), onSuccess: (data: DonationCreateResponse) => { if (data?.data?.id) { console.log("기부 생성 성공", data); } else { console.warn("기부 생성 성공했지만 ID가 없습니다.", data); } }, onError: (error: Error) => { console.error("결제 생성 실패:", error); }, }) const donationRewardQuery: UseQueryResult<DonationRewardData | null, Error> = useQuery({ queryKey: ["donationReward"], queryFn: fetchDonationReward, }); return { createDonationMutation, donationRewardQuery, } }
createDonationMutation을 실행하여 기부내역을 생성한 다음 페이지 이동을 하고 donationRewardQuery를 호출하여 랜덤 보상을 받도록 코드를 작성했다.
export const useDonation = (): UseDonationResult => { const queryClient = useQueryClient(); const createDonationMutation = useMutation< DonationCreateResponse, Error, DonationCreateData ({ mutationFn: (donationData: DonationCreateData) => createDonation(donationData), onSuccess: (data: DonationCreateResponse) => { if (data?.data?.id) { console.log("기부 생성 성공", data); queryClient.invalidateQueries({ queryKey: ["donationReward"] }); } else { console.warn("기부 생성 성공했지만 ID가 없습니다.", data); } }, onError: (error: Error) => { console.error("결제 생성 실패:", error); }, }); const donationRewardQuery: UseQueryResult<DonationRewardData | null, Error> = useQuery({ queryKey: ["donationReward"], queryFn: fetchDonationReward, staleTime: Infinity, gcTime: Infinity, refetchOnWindowFocus: false, refetchOnMount: false, refetchOnReconnect: false, enabled: false,) // }); return { createDonationMutation, donationRewardQuery, }; };
// 페이지 이동후 donationRewardQuery를 호출하여 랜덤보상 받는 부분 const { data: apiResponseData, isPending, isError, error, refetch, // refetch 함수 } = donationRewardQuery; ... useEffect(() => { refetch(); }, [refetch]);
이다.

뽀인트를 설명할게요.
우선 랜덤 보상 api 호출을 할때
staleTime: Infinity와 gcTime:Infinity 때문에 캐시된 데이터는 '영원히 fresh'하고 '영원히 존재'하게 됩니다.
그래서 어떠한 이유에서 리렌더링이 되던지 api가 재호출이 되더라도 캐시된 데이터가 fresh 하기 때문에 fetchDonationReward를 또 실행하지 않게됩니다.
createDonationMutation에서 기부내역이 성공적으로 생성되었을때 랜덤보상 key["donationReward"]를 넣은 queryClient.invalidateQueries을 설정하여 해당 캐시를 stale상태로 만들면 됩니다!
그리고 더욱 확실한 방법으로 랜덤 보상을 받는 페이지로 이동을 했을때 refetch()를 호출하자!
*useEffect의 의존성 배열에 refetch만 있으므로, 이 훅은 컴포넌트가 처음 마운트될 때 단 한 번만 실행됨. refetch 함수 자체가 변경될 일은 없기 때문.

사용한 option 간략히 설명하기 staleTime: Infinity, // 캐시된 데이터를 계속'fresh' 상태를 유지하도록 설정 gcTime: Infinity, // 캐시된 데이터를 가비지 컬렉션하지 않고 계속 유지 refetchOnWindowFocus: false, // 사용자가 해당 브라우저에 포커스를 잃었다가 다시 포커스할때 자동으로 쿼리를 재요청 할지 결정 refetchOnMount: false, // 컴포넌트가 새롭게 마운트될 때 자동으로 쿼리를 요청할지 결정 refetchOnReconnect: false, // 네트워크 재연결 시 자동 리프레시 방지 enabled: false, // 초기 렌더링 시에는 쿼리를 비활성화 (뒤에서 다시 활성화)