diff --git a/src/app/(with-header)/wines/_components/RecommendWine.tsx b/src/app/(with-header)/wines/_components/RecommendWine.tsx new file mode 100644 index 0000000..baf2db3 --- /dev/null +++ b/src/app/(with-header)/wines/_components/RecommendWine.tsx @@ -0,0 +1,74 @@ +'use client'; + +import { useState } from 'react'; +import Image from 'next/image'; +import Slider from 'react-slick'; +import { Wine } from '@/types/wine'; +import RecommendWineCard from './RecommendWineCard'; +import rightArrowIcon from '@/assets/icons/right_arrow.svg'; +import leftArrowIcon from '@/assets/icons/left_arrow.svg'; +import 'slick-carousel/slick/slick.css'; +import 'slick-carousel/slick/slick-theme.css'; + +interface RecommendWineProps { + recommendedList: Wine[]; +} + +export default function RecommendWine({ recommendedList }: RecommendWineProps) { + const [currentIndex, setCurrentIndex] = useState(0); + + const settings = { + dots: false, + infinite: false, + speed: 500, + slidesToScroll: 1, + variableWidth: true, + afterChange: (index: number) => setCurrentIndex(index), + nextArrow: currentIndex < recommendedList.length - 1 ? : undefined, + prevArrow: , + }; + + return ( +
+
+

이번 달 추천 와인

+
+ + {recommendedList.map((wine, idx) => ( +
+ +
+ ))} +
+
+
+
+ ); +} + +interface ArrowProps { + onClick?: () => void; + disabled?: boolean; +} + +function NextArrowButton({ onClick }: ArrowProps) { + return ( + + ); +} + +function PrevArrowButton({ onClick, disabled }: ArrowProps) { + return ( + + ); +} diff --git a/src/app/(with-header)/wines/_components/RecommendWineContainer.tsx b/src/app/(with-header)/wines/_components/RecommendWineContainer.tsx index 06c706f..32e935c 100644 --- a/src/app/(with-header)/wines/_components/RecommendWineContainer.tsx +++ b/src/app/(with-header)/wines/_components/RecommendWineContainer.tsx @@ -1,88 +1,34 @@ 'use client'; import { useEffect, useState } from 'react'; -import Image from 'next/image'; -import Slider from 'react-slick'; +import { fetchRecommendWine } from '@/lib/fetchRecommendWine'; +import RecommendWine from './RecommendWine'; +import RecommendWineListSkeleton from './skeleton/RecommendWineListSkeleton'; import { Wine } from '@/types/wine'; -import RecommendWineCard from './RecommendWineCard'; -import rightArrowIcon from '@/assets/icons/right_arrow.svg'; -import leftArrowIcon from '@/assets/icons/left_arrow.svg'; -import 'slick-carousel/slick/slick.css'; -import 'slick-carousel/slick/slick-theme.css'; export default function RecommendWineContainer() { const [recommendedList, setRecommendedList] = useState([]); - const [currentIndex, setCurrentIndex] = useState(0); + const [loading, setLoading] = useState(true); useEffect(() => { - async function getRecommendWines() { + const getRecommendedWines = async () => { + setLoading(true); try { - const response = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/wines/recommended?limit=8`, { - next: { revalidate: 1800 }, - }); - const wines: Wine[] = await response.json(); + const wines = await fetchRecommendWine(); setRecommendedList(wines); } catch (error) { - console.error('추천 와인 목록을 가져오는 중 오류 발생:', error); + console.error('추천 와인 목록을 가져오는 데 실패했습니다.', error); + } finally { + setLoading(false); } - } - getRecommendWines(); - }, []); - - const settings = { - dots: false, - infinite: false, - speed: 500, - slidesToScroll: 1, - variableWidth: true, - afterChange: (index: number) => setCurrentIndex(index), - nextArrow: currentIndex < recommendedList.length - 1 ? : undefined, - prevArrow: , - }; - - return ( -
-
-
-

이번 달 추천 와인

-
- - {recommendedList.map((wine, idx) => ( -
- -
- ))} -
-
-
-
-
- ); -} + }; -interface ArrowProps { - onClick?: () => void; - disabled?: boolean; -} + getRecommendedWines(); + }, []); -function NextArrowButton({ onClick }: ArrowProps) { - return ( - - ); -} + if (loading) { + return ; + } -function PrevArrowButton({ onClick, disabled }: ArrowProps) { - return ( - - ); + return ; } diff --git a/src/app/(with-header)/wines/_components/skeleton/RecommendWineCardSkeleton.tsx b/src/app/(with-header)/wines/_components/skeleton/RecommendWineCardSkeleton.tsx new file mode 100644 index 0000000..7037dfc --- /dev/null +++ b/src/app/(with-header)/wines/_components/skeleton/RecommendWineCardSkeleton.tsx @@ -0,0 +1,12 @@ +export default function RecommendWineCardSkeleton() { + return ( +
+
+
+
+
+
+
+
+ ); +} diff --git a/src/app/(with-header)/wines/_components/skeleton/RecommendWineListSkeleton.tsx b/src/app/(with-header)/wines/_components/skeleton/RecommendWineListSkeleton.tsx new file mode 100644 index 0000000..97c2783 --- /dev/null +++ b/src/app/(with-header)/wines/_components/skeleton/RecommendWineListSkeleton.tsx @@ -0,0 +1,16 @@ +import RecommendWineCardSkeleton from './RecommendWineCardSkeleton'; + +export default function RecommendWineListSkeleton({ count }: { count: number }) { + return ( +
+

이번 달 추천 와인

+
+
+ {new Array(count).fill(0).map((_, idx) => ( + + ))} +
+
+
+ ); +} diff --git a/src/lib/fetchRecommendWine.ts b/src/lib/fetchRecommendWine.ts new file mode 100644 index 0000000..2863225 --- /dev/null +++ b/src/lib/fetchRecommendWine.ts @@ -0,0 +1,18 @@ +import { Wine } from '@/types/wine'; + +export async function fetchRecommendWine(): Promise { + try { + const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/wines/recommended?limit=8`, { + next: { revalidate: 1800 }, + }); + + if (!res.ok) { + throw new Error('추천 와인 목록을 가져오는 데 실패했습니다.'); + } + + return res.json(); + } catch (error) { + console.error(error); + return []; + } +}