diff --git a/src/components/Tours/ToursCategory.tsx b/src/components/Tours/ToursCategory.tsx index 6a7d74d6..c3507ad5 100644 --- a/src/components/Tours/ToursCategory.tsx +++ b/src/components/Tours/ToursCategory.tsx @@ -1,20 +1,34 @@ import { RegionTypes, ToursCategoryProps } from '@/@types/tours.types'; import ToursCategoryItem from './ToursCategoryItem'; import { getPopularRegion } from '@api/region'; -import { useQuery } from '@tanstack/react-query'; import { Swiper, SwiperSlide } from 'swiper/react'; import 'swiper/css'; +import ToursCategoryItemSkeleton from './ToursCategoryItemSkeleton'; +import { v4 as uuidv4 } from 'uuid'; +import { useQuery } from '@tanstack/react-query'; +import { useEffect, useState } from 'react'; const ToursCategory = ({ selectedRegion, setSelectedRegion, }: ToursCategoryProps) => { - const regionsQuery = useQuery({ + const { data, isLoading, error } = useQuery({ queryKey: ['regions'], queryFn: () => getPopularRegion(), }); - if (regionsQuery.error) { + const [showSkeleton, setShowSkeleton] = useState(isLoading); + + useEffect(() => { + if (isLoading) { + setShowSkeleton(true); + } else { + const timer = setTimeout(() => setShowSkeleton(false), 200); + return () => clearTimeout(timer); + } + }, [isLoading]); + + if (error) { console.log('error - 예외 처리'); } @@ -28,18 +42,32 @@ const ToursCategory = ({ }; // '전체' 항목 추가 - const regionsData = regionsQuery.data?.data.data.regions ?? []; + const regionsData = data?.data.data.regions ?? []; const regions = [ - { name: '전체', areaCode: 0, subAreaCode: 0 }, + { name: '전체', areaCode: uuidv4(), subAreaCode: 0 }, ...regionsData, ]; + if (showSkeleton) { + return ( +
+ + {Array.from({ length: 10 }, (_, index) => ( + + + + ))} + +
+ ); + } + return (
- {regions.map((region: RegionTypes, index: number) => { + {regions.map((region: RegionTypes) => { return ( - + { + return ( +
+
+
+ ); +}; + +export default ToursCategoryItemSkeleton; diff --git a/src/components/Tours/ToursItemSkeleton.tsx b/src/components/Tours/ToursItemSkeleton.tsx new file mode 100644 index 00000000..ca91d5d4 --- /dev/null +++ b/src/components/Tours/ToursItemSkeleton.tsx @@ -0,0 +1,16 @@ +const ToursItemSkeleton = () => { + return ( +
+
+
+
+

+
+
+
+
+
+ ); +}; + +export default ToursItemSkeleton; diff --git a/src/components/Tours/ToursList.tsx b/src/components/Tours/ToursList.tsx index 10a2f6cc..700c343f 100644 --- a/src/components/Tours/ToursList.tsx +++ b/src/components/Tours/ToursList.tsx @@ -1,26 +1,40 @@ import { TourType, ToursListProps } from '@/@types/tours.types'; -import ToursItem from './ToursItem'; import { getTours } from '@api/tours'; import { useInfiniteQuery } from '@tanstack/react-query'; +import React, { useEffect, useState } from 'react'; import InfiniteScroll from 'react-infinite-scroller'; -import React from 'react'; +import { v4 as uuidv4 } from 'uuid'; +import ToursItem from './ToursItem'; +import ToursItemSkeleton from './ToursItemSkeleton'; const ToursList = ({ selectedRegion }: ToursListProps) => { - const { fetchNextPage, hasNextPage, data, error } = useInfiniteQuery({ - queryKey: ['tours', selectedRegion], - queryFn: ({ pageParam = 0 }) => getTours(selectedRegion, pageParam, 10), - initialPageParam: 0, - getNextPageParam: (lastPage) => { - const currentPage = lastPage?.data.data.pageable.pageNumber; - const totalPages = lastPage?.data.data.totalPages; - - if (currentPage < totalPages - 1) { - return currentPage + 1; - } - - return undefined; - }, - }); + const { fetchNextPage, hasNextPage, data, isLoading, error } = + useInfiniteQuery({ + queryKey: ['tours', selectedRegion], + queryFn: ({ pageParam = 0 }) => getTours(selectedRegion, pageParam, 10), + initialPageParam: 0, + getNextPageParam: (lastPage) => { + const currentPage = lastPage?.data.data.pageable.pageNumber; + const totalPages = lastPage?.data.data.totalPages; + + if (currentPage < totalPages - 1) { + return currentPage + 1; + } + + return undefined; + }, + }); + + const [showSkeleton, setShowSkeleton] = useState(isLoading); + + useEffect(() => { + if (isLoading) { + setShowSkeleton(true); + } else { + const timer = setTimeout(() => setShowSkeleton(false), 200); + return () => clearTimeout(timer); + } + }, [isLoading]); if (error) { return
데이터를 불러오는 중 오류가 발생했습니다.
; @@ -32,18 +46,27 @@ const ToursList = ({ selectedRegion }: ToursListProps) => { loadMore={() => fetchNextPage()} hasMore={hasNextPage} loader={ -
- Loading ... +
+
+
Loading...
+
}> -
- {data?.pages.map((group, index) => ( - - {group?.data.data.content.map((tour: TourType) => ( - +
+ {showSkeleton + ? Array.from({ length: 10 }, (_, index) => ( + + )) + : data?.pages.map((group) => ( + + {group?.data.data.content.map((tour: TourType) => ( + + ))} + ))} - - ))}
); diff --git a/src/components/common/nav/Nav.tsx b/src/components/common/nav/Nav.tsx index cf531adb..96a76421 100644 --- a/src/components/common/nav/Nav.tsx +++ b/src/components/common/nav/Nav.tsx @@ -16,13 +16,13 @@ const Nav = () => {
-

+

navigate('/')} className="cursor-pointer flex-col items-center justify-center px-2"> -

일정

+

일정

navigate('/')} @@ -30,7 +30,7 @@ const Nav = () => {
-

+

navigate('/signin')} @@ -38,7 +38,7 @@ const Nav = () => {
-

내정보

+

내정보