From b9e47e52f224e7868701f95006db9680db0bf709 Mon Sep 17 00:00:00 2001 From: goorm Date: Sun, 3 Aug 2025 03:27:25 +0900 Subject: [PATCH 1/2] fix: resolve duplicate identifiers and improve error handling in API calls - Remove duplicate imports and function declarations in Home.tsx - Add proper error handling for API calls in Home.ts - Update API calls to handle individual failures gracefully - Ready for deployment merge --- src/api/Home.ts | 47 +++++++++++++++++++++++++++++++++++----------- src/pages/Home.tsx | 27 ++++++++++++++++---------- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/api/Home.ts b/src/api/Home.ts index f6661c9..91929c0 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/products/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/products/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/pages/Home.tsx b/src/pages/Home.tsx index 30b0441..bbb5ce4 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -100,20 +100,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 형태로 변환 From bf9a576f371b26dae8c3750b52b097719d879045 Mon Sep 17 00:00:00 2001 From: goorm Date: Sun, 3 Aug 2025 04:56:51 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/Home.ts | 4 +- src/components/home/ExtraCropSection.tsx | 123 +++++++++++++++++------ src/components/home/HeroSection.tsx | 43 +++++--- src/components/home/ReviewSection.tsx | 2 +- src/pages/Home.tsx | 15 +-- 5 files changed, 132 insertions(+), 55 deletions(-) diff --git a/src/api/Home.ts b/src/api/Home.ts index 91929c0..04cde6d 100644 --- a/src/api/Home.ts +++ b/src/api/Home.ts @@ -45,7 +45,7 @@ export const fetchProducts = async (): Promise => { export const fetchProductsReview1 = async (): Promise => { try { - const response = await axios.get("api/products/1/reviews"); + const response = await axios.get("api/projects/1/reviews"); console.log("Fetched reviews for product 1:", response.data); return response.data; } catch (error) { @@ -60,7 +60,7 @@ export const fetchProductsReview1 = async (): Promise => { export const fetchProductsReview2 = async (): Promise => { try { - const response = await axios.get("api/products/2/reviews"); + const response = await axios.get("api/projects/2/reviews"); console.log("Fetched reviews for product 2:", response.data); return response.data; } catch (error) { diff --git a/src/components/home/ExtraCropSection.tsx b/src/components/home/ExtraCropSection.tsx index 0a83bde..e01d918 100644 --- a/src/components/home/ExtraCropSection.tsx +++ b/src/components/home/ExtraCropSection.tsx @@ -5,37 +5,100 @@ interface ExtraCrop { participants: number; } -const ExtraCropSection = ({ extraCrops }: { extraCrops: ExtraCrop[] }) => ( -
-

- 다른 농작물 둘러보기 -

-
- {extraCrops.map((crop, idx) => ( -
-
- 🚩 - {crop.participants}명 참여중! -
-
- {crop.emoji} -
-
-
{crop.name}
-
- {crop.price}원 - - 박스당 1박스 - +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박스 + +
-
- ))} -
-
-); + ))} + + + ); +}; export default ExtraCropSection; diff --git a/src/components/home/HeroSection.tsx b/src/components/home/HeroSection.tsx index 05094f0..2fb3520 100644 --- a/src/components/home/HeroSection.tsx +++ b/src/components/home/HeroSection.tsx @@ -1,17 +1,30 @@ -const HeroSection = () => ( -
-

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

-

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

- -
-); +import { useNavigate } from "react-router-dom"; + +const HeroSection = () => { + const navigate = useNavigate(); + + const handleStartClick = () => { + navigate("/login"); + }; + + return ( +
+

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

+

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

+ +
+ ); +}; export default HeroSection; diff --git a/src/components/home/ReviewSection.tsx b/src/components/home/ReviewSection.tsx index cd6745b..66098f4 100644 --- a/src/components/home/ReviewSection.tsx +++ b/src/components/home/ReviewSection.tsx @@ -58,7 +58,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 059a938..ec5b972 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -15,12 +15,6 @@ import { } from "../api/Home"; import type { ProductResponse, ReviewResponse } from "../api/Home"; -const extraCrops = [ - { emoji: "🥕", name: "유기농 당근", price: "15,000", participants: 12 }, - { emoji: "🥬", name: "친환경 배추", price: "25,000", participants: 8 }, - { emoji: "🥒", name: "무농약 오이", price: "18,000", participants: 20 }, -]; - const steps = [ { number: 1, @@ -195,7 +189,14 @@ export default function Home() { /> - +