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 [];
+ }
+}