diff --git a/package-lock.json b/package-lock.json index 22c80ad..0b88957 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "cnu-next-week02", + "name": "cnu-next", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "cnu-next-week02", + "name": "cnu-next", "version": "0.1.0", "dependencies": { "next": "15.3.3", diff --git a/src/app/checkout/page.tsx b/src/app/checkout/page.tsx index 0d40153..91ce920 100644 --- a/src/app/checkout/page.tsx +++ b/src/app/checkout/page.tsx @@ -1,21 +1,69 @@ +"use client"; // CheckoutPage -import { useState } from "react"; +import { useEffect, useState } from "react"; +import Link from "next/link"; import { ProductItem } from "@/types/Product"; interface CheckoutItem { - product: ProductItem; - quantity: number; + product: ProductItem; + quantity: number; } -// 과제 3 + +// 과제 3 export default function CheckoutPage() { - const [items, setItems] = useState([]); - // 3.1. 결제하기 구현 - return ( -
-

✅ 결제가 완료되었습니다!

- {/* 3.1. 결제하기 구현 */} -
- {/* 3.2. 홈으로 가기 버튼 구현 */} -
- ); + const [items, setItems] = useState([]); + + useEffect(() => { + const data = localStorage.getItem("purchasedItems"); + if (data) { + setItems(JSON.parse(data)); + localStorage.removeItem("purchasedItems"); + } + }, []); + + const total = items.reduce( + (sum, item) => sum + Number(item.product.lprice) * item.quantity, + 0 + ); + + return ( +
+

✅ 결제가 완료되었습니다!

+ + {/* 3.1. 결제 정보 출력 */} + {items.length === 0 ? ( +

결제된 아이템이 없습니다.

+ ) : ( +
+ {items.map((item, idx) => ( +
+
+

+

수량: {item.quantity}

+
+

+ {(Number(item.product.lprice) * item.quantity).toLocaleString()}원 +

+
+ ))} +
+ 총 결제 금액: {total.toLocaleString()}원 +
+
+ )} + + {/* 3.2. 홈으로 가기 버튼 */} +
+ + 홈으로 돌아가기 + +
+
+ ); } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f7fa87e..4c2770d 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,9 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; +import { ReactNode } from "react"; +import "@/context/UserContext"; +import { UserProvider } from "@/context/UserContext"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -27,7 +30,7 @@ export default function RootLayout({ - {children} + {children} ); diff --git a/src/app/mypage/page.tsx b/src/app/mypage/page.tsx index 93b3ba9..18855d0 100644 --- a/src/app/mypage/page.tsx +++ b/src/app/mypage/page.tsx @@ -1,14 +1,30 @@ +"use client"; + +import { useUser } from "@/context/UserContext"; +import Header from "@/component/layout/Header"; +import Link from "next/link"; + // 과제 1: 마이페이지 구현 export default function MyPage() { - // 1.1. UserContext를 활용한 Mypage 구현 (UserContext에 아이디(userId: string), 나이(age: number), 핸드폰번호(phoneNumber: string) 추가) + // 1.1. UserContext를 활용한 Mypage 구현 + const { user } = useUser(); + + return ( +
+ {/* 1.2. Header Component를 재활용하여 Mypage Header 표기 */} +
- return ( -
- {/* 1.2. Header Component를 재활용하여 Mypage Header 표기 (title: 마이페이지) */} -

마이페이지

- {/* Mypage 정보를 UserContext 활용하여 표시 (이름, 아이디, 나이, 핸드폰번호 모두 포함) */} + {/* 1.1. Mypage 정보를 UserContext 활용하여 표시 */} +
+

이름: {user.name}

+

나이: {user.age}

+

전화번호: {user.phoneNumber}

+
- {/* 1.3. 홈으로 가기 버튼 구현(Link or Router 활용) */} -
- ); + {/* 1.3. 홈으로 가기 버튼 구현 */} + + 홈으로 가기 + +
+ ); } diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx index c3b6212..9806d81 100644 --- a/src/app/search/page.tsx +++ b/src/app/search/page.tsx @@ -14,10 +14,14 @@ export default function SearchHome() { // 페이지 최초 렌더링 될 때, setUser로 이름 설정 useEffect(() => { - // 학번 + 이름 형태로 작성 (ex. 2025***** 내이름 ) - setUser({ name: "" }); + setUser({ + name: "심정원", + age: 22, + phoneNumber: "010-1234-5678", + }); }, []); + return (
diff --git a/src/component/search/SearchInput.tsx b/src/component/search/SearchInput.tsx index aea7294..8a49c4f 100644 --- a/src/component/search/SearchInput.tsx +++ b/src/component/search/SearchInput.tsx @@ -1,10 +1,19 @@ "use client"; import { useSearch } from "@/context/SearchContext"; +import { useEffect, useRef } from "react"; + export default function SearchInput() { const { query, setQuery, setResult } = useSearch(); - // 검색 기능 + const inputRef = useRef(null); + + useEffect(() => { + inputRef.current?.focus(); + }, []); + + + // 검색 기능 const search = async () => { try { const res = await fetch(`/api/search?query=${encodeURIComponent(query)}`); @@ -19,13 +28,15 @@ export default function SearchInput() { }; // 2.2. SearchInput 컴포넌트가 최초 렌더링 될 때, input tag에 포커스 되는 기능 - const handleInputChange = () => {}; - + const handleInputChange = (e: React.ChangeEvent) => { + setQuery(e.target.value); + }; // 과제 1-2-3: 페이지 최초 렌더링 시, input에 포커스 되는 기능 (useRef) return (
void; // 삭제 핸들러 추가 + onRemoveAction: (productId: string) => void; // 삭제 핸들러 추가 } -export default function CartList({ cart, products, onRemove }: Props) { - const cartItems = Object.entries(cart) +export default function CartList({ cart, products, onRemoveAction }: Props) { + const [showCart, setShowCart] = useState(false); + const router = useRouter(); + + const cartItems = Object.entries(cart) .map(([id, quantity]) => { const product = products.find((p) => p.productId === id); return product ? { ...product, quantity } : null; }) .filter((item): item is NonNullable => item !== null); + useEffect(() => { + setShowCart(cartItems.length > 0); + }, [cartItems]); + + const total = cartItems.reduce( (sum, item) => sum + Number(item.lprice) * item.quantity, 0 ); // 2.4 결제하기: "결제하기" 버튼을 클릭하면, 현재 장바구니에 담긴 상품을 확인해 **localStorage**에 저장 후, 결제완료(/checkout) 페이지로 이동한다. - const handleCheckout = () => {}; + const handleCheckout = () => { + const cartItems = Object.entries(cart) + .map(([id, quantity]) => { + const product = products.find((p) => p.productId === id); + return product ? { title: product.title, lprice: product.lprice, quantity } : null; + }) + .filter((item): item is NonNullable => item !== null); + + localStorage.setItem("purchasedItems", JSON.stringify(cartItems)); + + router.push("/checkout"); + }; + + if (!showCart) return null; - return ( + return (

🛒 장바구니

{cartItems.length === 0 ? ( @@ -44,7 +68,7 @@ export default function CartList({ cart, products, onRemove }: Props) { {(Number(item.lprice) * item.quantity).toLocaleString()}원