diff --git a/src/api/Home.ts b/src/api/Home.ts index f6661c9..04cde6d 100644 --- a/src/api/Home.ts +++ b/src/api/Home.ts @@ -1,8 +1,5 @@ import axios from "axios"; -// 개발 환경에서는 프록시 사용, 배포 환경에서는 실제 API URL 사용 -const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || ""; - export interface ProductResponse { id: number; title: string; @@ -32,18 +29,46 @@ export interface ReviewApiResponse { } export const fetchProducts = async (): Promise => { - const response = await axios.get(`${API_BASE_URL}/api/products`); - console.log("Fetched products:", response.data); - return response.data; + try { + const response = await axios.get("api/products"); + console.log("Fetched products:", response.data); + return response.data; + } catch (error) { + console.error("Products API 에러:", error); + return { + success: false, + response: [], + error: "상품 데이터를 불러올 수 없습니다.", + }; + } }; export const fetchProductsReview1 = async (): Promise => { - const response = await axios.get(`${API_BASE_URL}/api/products/1/reviews`); - console.log("Fetched reviews for product 1:", response.data); - return response.data; + try { + const response = await axios.get("api/projects/1/reviews"); + console.log("Fetched reviews for product 1:", response.data); + return response.data; + } catch (error) { + console.error("Product 1 리뷰 API 에러:", error); + return { + success: false, + response: [], + error: "리뷰 데이터를 불러올 수 없습니다.", + }; + } }; export const fetchProductsReview2 = async (): Promise => { - const response = await axios.get(`${API_BASE_URL}/api/products/2/reviews`); - return response.data; + try { + const response = await axios.get("api/projects/2/reviews"); + console.log("Fetched reviews for product 2:", response.data); + return response.data; + } catch (error) { + console.error("Product 2 리뷰 API 에러:", error); + return { + success: false, + response: [], + error: "리뷰 데이터를 불러올 수 없습니다.", + }; + } }; diff --git a/src/components/home/ExtraCropSection.tsx b/src/components/home/ExtraCropSection.tsx index 3462e77..8051c1b 100644 --- a/src/components/home/ExtraCropSection.tsx +++ b/src/components/home/ExtraCropSection.tsx @@ -5,6 +5,96 @@ interface ExtraCrop { participants: number; } + +interface ProductType { + id: number; + title?: string; + price: string | number; + imageUrl?: string; +} + +// 더미데이터 - API 데이터로 덮어쓰지 않을 기본값들 +const defaultExtraCropData = [ + { emoji: "🥕", name: "유기농 당근", price: "15,000", participants: 12 }, + { emoji: "🥬", name: "친환경 배추", price: "25,000", participants: 8 }, + { emoji: "🥒", name: "무농약 오이", price: "18,000", participants: 20 }, + { emoji: "🌽", name: "찰옥수수", price: "22,000", participants: 15 }, + { emoji: "🥔", name: "햇감자", price: "16,000", participants: 18 }, + { emoji: "🍆", name: "가지", price: "14,000", participants: 9 }, +]; + +interface ExtraCropSectionProps { + product4?: ProductType; + product5?: ProductType; + product6?: ProductType; + product7?: ProductType; + product8?: ProductType; + product9?: ProductType; +} + +const ExtraCropSection = ({ + product4, + product5, + product6, + product7, + product8, + product9, +}: ExtraCropSectionProps) => { + // API 데이터와 더미 데이터를 병합하는 함수 + const mergeWithDefaults = ( + apiProduct: ProductType | null | undefined, + defaultData: any + ) => { + if (apiProduct) { + return { + ...defaultData, + name: apiProduct.title || defaultData.name, + price: + typeof apiProduct.price === "number" + ? apiProduct.price.toLocaleString() + : apiProduct.price, + }; + } + return defaultData; + }; + + // API 데이터와 더미 데이터를 병합 + const extraCrops = [ + mergeWithDefaults(product4, defaultExtraCropData[0]), + mergeWithDefaults(product5, defaultExtraCropData[1]), + mergeWithDefaults(product6, defaultExtraCropData[2]), + mergeWithDefaults(product7, defaultExtraCropData[3]), + mergeWithDefaults(product8, defaultExtraCropData[4]), + mergeWithDefaults(product9, defaultExtraCropData[5]), + ]; + + return ( +
+

+ 다른 농작물 둘러보기 +

+
+ {extraCrops.map((crop, idx) => ( +
+
+ 🚩 + {crop.participants}명 참여중! +
+
+ {crop.emoji} +
+
+
{crop.name}
+
+ {crop.price}원 + + 박스당 1박스 + +
+ const ExtraCropSection = ({ extraCrops }: { extraCrops: ExtraCrop[] }) => (

@@ -30,12 +120,13 @@ const ExtraCropSection = ({ extraCrops }: { extraCrops: ExtraCrop[] }) => ( 박스당 1박스 +

-
- ))} - -
-); + ))} + + + ); +}; export default ExtraCropSection; diff --git a/src/components/home/HeroSection.tsx b/src/components/home/HeroSection.tsx index 6cd6775..5f1580a 100644 --- a/src/components/home/HeroSection.tsx +++ b/src/components/home/HeroSection.tsx @@ -1,3 +1,33 @@ + +import { useNavigate } from "react-router-dom"; + +const HeroSection = () => { + const navigate = useNavigate(); + + const handleStartClick = () => { + navigate("/login"); + }; + + return ( +
+

+ 농부와 함께 키우는 +
+ 믿음직한 농작물 +

+

+ 농부의 신선한 농작물을 펀딩하고, 성장 과정을 지켜보세요 🌱 +

+ +
+ ); +}; + const HeroSection = () => (

@@ -14,4 +44,5 @@ const HeroSection = () => (

); + export default HeroSection; diff --git a/src/components/home/ReviewSection.tsx b/src/components/home/ReviewSection.tsx index 532ccfc..4d120ac 100644 --- a/src/components/home/ReviewSection.tsx +++ b/src/components/home/ReviewSection.tsx @@ -59,7 +59,7 @@ const ReviewSection = ({ reviews: apiReviews }: ReviewSectionProps) => { {reviews.map((review, idx) => (
{"⭐️".repeat(review.rating)} diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 880a1ef..f2c5538 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -17,13 +17,6 @@ import { import type { ProductResponse, ReviewResponse } from "../api/Home"; -const dummyExtraCrops = [ - { emoji: "🥕", name: "유기농 당근", price: "15,000", participants: 12 }, - { emoji: "🥬", name: "친환경 배추", price: "25,000", participants: 8 }, - { emoji: "🥒", name: "무농약 오이", price: "18,000", participants: 20 }, -]; - - const steps = [ { @@ -78,6 +71,10 @@ export default function Home() { >(null); + const [email, setEmail] = useState(""); + const [showEmailAlert, setShowEmailAlert] = useState(false); + + useEffect(() => { const loadProducts = async () => { try { @@ -109,20 +106,27 @@ export default function Home() { const loadReviews = async () => { try { - // 여러 상품의 리뷰를 가져와서 병합 - const [review1Response, review2Response] = await Promise.all([ - fetchProductsReview1(), - fetchProductsReview2(), - ]); - + // 여러 상품의 리뷰를 가져와서 병합 - 각각 개별적으로 처리 const allReviews: ReviewResponse[] = []; - if (review1Response.success && review1Response.response) { - allReviews.push(...review1Response.response); + // 첫 번째 리뷰 API 호출 + try { + const review1Response = await fetchProductsReview1(); + if (review1Response.success && review1Response.response) { + allReviews.push(...review1Response.response); + } + } catch (error) { + console.warn("Product 1 리뷰 로딩 실패:", error); } - if (review2Response.success && review2Response.response) { - allReviews.push(...review2Response.response); + // 두 번째 리뷰 API 호출 + try { + const review2Response = await fetchProductsReview2(); + if (review2Response.success && review2Response.response) { + allReviews.push(...review2Response.response); + } + } catch (error) { + console.warn("Product 2 리뷰 로딩 실패:", error); } // API 데이터를 ReviewSection 형태로 변환 @@ -151,10 +155,6 @@ export default function Home() { loadProducts(); loadReviews(); }, []); - const [email, setEmail] = useState(""); - const [showEmailAlert, setShowEmailAlert] = useState(false); - - // 더미데이터만 사용, API 호출 및 product1~product9 상태 제거 const handleEmailSubmit = (e: React.FormEvent) => { e.preventDefault(); @@ -198,7 +198,16 @@ export default function Home() { /> - + + +