diff --git a/src/components/common/winelist/WineListCard.tsx b/src/components/common/winelist/WineListCard.tsx index fb89011..f722733 100644 --- a/src/components/common/winelist/WineListCard.tsx +++ b/src/components/common/winelist/WineListCard.tsx @@ -1,7 +1,9 @@ -import { useRef } from 'react'; +import { useEffect, useRef } from 'react'; +import { useQueryClient } from '@tanstack/react-query'; import Link from 'next/link'; +import { getWineInfoForClient } from '@/api/getWineInfo'; import NextIcon from '@/assets/icons/Next.svg'; import StarIcon from '@/assets/icons/star.svg'; import { ImageCard } from '@/components/common/card/ImageCard'; @@ -25,21 +27,38 @@ export default function WineListCard() { threshold: 0.3, }); - if (isLoading) return

불러오는 중...

; - if (isError || !data) return

와인 데이터를 불러올 수 없습니다.

; - - const wineList = data.pages.flatMap( + const wineList = data?.pages.flatMap( (page) => (page as GetWinesResponse)?.list?.filter((wine) => !wine.image.includes('example.com')) ?? [], ); + const queryClient = useQueryClient(); + + const prefetchWineInfo = async (wineid: number) => { + await queryClient.prefetchQuery({ + queryKey: ['wineDetail', wineid], + queryFn: () => getWineInfoForClient(wineid), + staleTime: 1000 * 60 * 5, + }); + }; + + // // 데이터 프리패칭용 + useEffect(() => { + wineList?.forEach((wine) => { + prefetchWineInfo(wine.id); + }); + }, [wineList]); + + if (isLoading) return

불러오는 중...

; + if (isError || !data) return

와인 데이터를 불러올 수 없습니다.

; + return (
- {wineList.map((wine) => ( + {wineList?.map((wine) => (
getRecommendedWines({ teamId: TEAM_ID!, limit: RECOMMENDED_WINES_LIMIT }), }); + const queryClient = useQueryClient(); + + const prefetchWineInfo = async (wineid: number) => { + await queryClient.prefetchQuery({ + queryKey: ['wineDetail', wineid], + queryFn: () => getWineInfoForClient(wineid), + staleTime: 1000 * 60 * 5, + }); + }; + + useEffect(() => { + data?.forEach((wine) => { + prefetchWineInfo(wine.id); + }); + }, [data]); + return (
diff --git a/src/hooks/useWineListQuery.ts b/src/hooks/useWineListQuery.ts index e0aa51c..c22c51f 100644 --- a/src/hooks/useWineListQuery.ts +++ b/src/hooks/useWineListQuery.ts @@ -1,3 +1,5 @@ +import { useEffect, useRef, useState } from 'react'; + import { InfiniteData, useInfiniteQuery } from '@tanstack/react-query'; import { getWines } from '@/lib/wineApi'; @@ -8,6 +10,23 @@ import { GetWinesResponse } from '@/types/wineListType'; const PAGE_LIMIT = 8; const TEAM_ID = process.env.NEXT_PUBLIC_TEAM; +function useDebounce(value: T) { + const debounceTimer = useRef | null>(null); + const [debouncedValue, setDebouncedValue] = useState(() => value); + + useEffect(() => { + if (debounceTimer.current) clearTimeout(debounceTimer.current); + debounceTimer.current = setTimeout(() => { + setDebouncedValue(value); + }, 1000); + + return () => { + if (debounceTimer.current) clearTimeout(debounceTimer.current); + }; + }, [value]); + return debouncedValue; +} + export function useWineListQuery() { const type = useFilterStore((state) => state.type); const minPrice = useFilterStore((state) => state.minPrice); @@ -15,23 +34,36 @@ export function useWineListQuery() { const rating = useFilterStore((state) => state.rating); const searchTerm = useWineSearchKeywordStore((state) => state.searchTerm); + const debouncedType = useDebounce(type); + const debouncedMinPrice = useDebounce(minPrice); + const debouncedMaxPrice = useDebounce(maxPrice); + const debouncedSearchTerm = useDebounce(searchTerm); + const apiRating = rating === 'all' ? undefined : Number(rating); + const debouncedRating = useDebounce(apiRating); return useInfiniteQuery>({ - queryKey: ['wines', { type, minPrice, maxPrice, rating: apiRating, name: searchTerm }], + queryKey: [ + 'wines', + { + type: debouncedType, + minPrice: debouncedMinPrice, + maxPrice: debouncedMaxPrice, + rating: debouncedRating, + name: debouncedSearchTerm, + }, + ], queryFn: ({ pageParam = 0 }) => { const filters = { - type: type.toUpperCase(), - minPrice, - maxPrice, - rating: apiRating, - name: searchTerm, + type: debouncedType.toUpperCase(), + minPrice: debouncedMinPrice, + maxPrice: debouncedMaxPrice, + rating: debouncedRating, + name: debouncedSearchTerm, }; const filteredParams = Object.fromEntries( - Object.entries(filters).filter( - ([, value]) => value !== null && value !== undefined && value !== '', - ), + Object.entries(filters).filter(([, value]) => value !== null && value !== undefined), ); return getWines({