diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..c3f502a --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 디폴트 무시된 파일 +/shelf/ +/workspace.xml +# 에디터 기반 HTTP 클라이언트 요청 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/cnu-next.iml b/.idea/cnu-next.iml new file mode 100644 index 0000000..9b31378 --- /dev/null +++ b/.idea/cnu-next.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..e557d17 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..388d1ac --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file 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..ebc4db2 100644 --- a/src/app/checkout/page.tsx +++ b/src/app/checkout/page.tsx @@ -1,6 +1,8 @@ +"use client"; // CheckoutPage -import { useState } from "react"; +import { useEffect, useState } from "react"; import { ProductItem } from "@/types/Product"; +import { useRouter } from "next/navigation"; interface CheckoutItem { product: ProductItem; @@ -9,13 +11,55 @@ interface CheckoutItem { // 과제 3 export default function CheckoutPage() { const [items, setItems] = useState([]); + const router = useRouter(); + + useEffect(() => { + const data = localStorage.getItem("checkoutItems"); + if (data) { + const parsedData: CheckoutItem[] = JSON.parse(data); + setItems(parsedData); + localStorage.removeItem("checkoutItems"); + } + }, []); + + const total = items.reduce( + (sum, item) => sum + Number(item.product.lprice) * item.quantity, + 0 + ); // 3.1. 결제하기 구현 return (

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

{/* 3.1. 결제하기 구현 */} + {items.length === 0 ? ( +

결제할 아이템이 없습니다

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

마이페이지

+
+

이름: {user.name}

+

아이디: {user.userId}

+

나이: {user.age}

+

핸드폰번호: {user.phoneNumber}

{/* Mypage 정보를 UserContext 활용하여 표시 (이름, 아이디, 나이, 핸드폰번호 모두 포함) */} {/* 1.3. 홈으로 가기 버튼 구현(Link or Router 활용) */} + + 홈으로 가기 +
); } diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx index c3b6212..7d4a8ee 100644 --- a/src/app/search/page.tsx +++ b/src/app/search/page.tsx @@ -15,7 +15,12 @@ export default function SearchHome() { // 페이지 최초 렌더링 될 때, setUser로 이름 설정 useEffect(() => { // 학번 + 이름 형태로 작성 (ex. 2025***** 내이름 ) - setUser({ name: "" }); + setUser({ + name: "202302635 한웅", + age: 25, + userId: "haaaanung", + phoneNumber: "010-5510-3347", + }); }, []); return ( diff --git a/src/component/search/SearchInput.tsx b/src/component/search/SearchInput.tsx index aea7294..f664924 100644 --- a/src/component/search/SearchInput.tsx +++ b/src/component/search/SearchInput.tsx @@ -1,9 +1,13 @@ "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 { @@ -19,13 +23,16 @@ 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 (
{ const product = products.find((p) => p.productId === id); @@ -21,7 +23,24 @@ export default function CartList({ cart, products, onRemove }: Props) { ); // 2.4 결제하기: "결제하기" 버튼을 클릭하면, 현재 장바구니에 담긴 상품을 확인해 **localStorage**에 저장 후, 결제완료(/checkout) 페이지로 이동한다. - const handleCheckout = () => {}; + const handleCheckout = () => { + if (cartItems.length === 0) { + alert("장바구니가 비어 있습니다."); + return; + } + + const checkoutItems = cartItems.map((item) => ({ + product: { + productId: item.productId, + title: item.title, + lprice: Number(item.lprice), + }, + quantity: item.quantity, + })); + + localStorage.setItem("checkoutItems", JSON.stringify(checkoutItems)); + router.push("/checkout"); + }; return (
diff --git a/src/component/shopping/ProductCart.tsx b/src/component/shopping/ProductCart.tsx index a66c2b3..a6d4e89 100644 --- a/src/component/shopping/ProductCart.tsx +++ b/src/component/shopping/ProductCart.tsx @@ -20,7 +20,21 @@ export default function ProductCart({ items }: { items: ProductItem[] }) { }; /* 과제 2-3: Cart 아이템 지우기 */ - const handleRemoveFromCart = () => {}; + const handleRemoveFromCart = (productId: string) => { + setCart((prevCart) => { + const updatedCart = Object.entries(prevCart) + .filter(([id]) => id !== productId) // 이게 핵심! + .reduce((acc, [id, qty]) => { + acc[id] = qty; + return acc; + }, {} as { [key: string]: number }); + + return updatedCart; + }); + }; + useEffect(() => { + setShowCart(Object.keys(cart).length > 0); + }, [cart]); return (
@@ -28,7 +42,13 @@ export default function ProductCart({ items }: { items: ProductItem[] }) { {/* 장바구니 */} {/* 2.1. 조건부 카트 보이기: 카트에 담긴 상품이 없으면 카트가 보이지 않고, 카트에 담긴 물건이 있으면 카트가 보인다 */} - + {showCart && ( + + )}
); } diff --git a/src/context/UserContext.tsx b/src/context/UserContext.tsx index e5d3f14..fb7fe10 100644 --- a/src/context/UserContext.tsx +++ b/src/context/UserContext.tsx @@ -6,8 +6,9 @@ import { createContext, ReactNode, useContext, useState } from "react"; // User interface User { name: string; - // age: number - // 추가하고 싶은 속성들 ... + userId: string; + age: number; + phoneNumber: string; } // UserContextType interface UserContextType { @@ -22,7 +23,12 @@ export const UserContext = createContext( // 2. Provider 생성 export const UserProvider = ({ children }: { children: ReactNode }) => { - const [user, setUser] = useState({ name: "" }); + const [user, setUser] = useState({ + name: "한웅", + userId: "hanung", + age: 23, + phoneNumber: "010-5510-3347", + }); return ( {children}