From a20475223bbecbbdb6897e5c4a04678b588a9905 Mon Sep 17 00:00:00 2001 From: jaehyun429 Date: Sun, 15 Jun 2025 20:08:32 +0900 Subject: [PATCH 1/2] =?UTF-8?q?202302616=20=EC=A0=95=EC=9E=AC=ED=98=84=20?= =?UTF-8?q?=EA=B3=BC=EC=A0=9C=EC=A0=9C=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 4 +-- src/app/checkout/page.tsx | 42 ++++++++++++++++++++++++-- src/app/layout.tsx | 3 +- src/app/mypage/page.tsx | 36 ++++++++++++++++++++-- src/app/search/page.tsx | 11 +++++-- src/component/search/SearchInput.tsx | 11 ++++++- src/component/shopping/CartList.tsx | 17 ++++++++++- src/component/shopping/ProductCart.tsx | 25 +++++++++++++-- src/context/UserContext.tsx | 29 ++++++++++-------- tsconfig.json | 2 +- 10 files changed, 152 insertions(+), 28 deletions(-) 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..7e23b37 100644 --- a/src/app/checkout/page.tsx +++ b/src/app/checkout/page.tsx @@ -1,6 +1,7 @@ -// CheckoutPage -import { useState } from "react"; + +import { useEffect, useState } from "react"; import { ProductItem } from "@/types/Product"; +import { useRouter } from "next/router"; interface CheckoutItem { product: ProductItem; @@ -9,13 +10,48 @@ interface CheckoutItem { // 과제 3 export default function CheckoutPage() { const [items, setItems] = useState([]); + const router = useRouter(); + + useEffect(() => { + const data = localStorage.getItem("checkoutItems"); + if (data) { + const parsed = JSON.parse(data) as CheckoutItem[]; + setItems(parsed); + 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 ? ( + <> +
    + {items.map((item, index) => ( +
  • + {item.product.mallName} (x{item.quantity}) + {Number(item.product.lprice).toLocaleString()}원 +
  • + ))} +
  • + 총 결제 금액 + {total.toLocaleString()}원 +
  • +
+ + ) + : +
장바구니에 상품이 없습니다.
+ }
{/* 3.2. 홈으로 가기 버튼 구현 */} +
); -} + } \ No newline at end of file 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..a95207c 100644 --- a/src/app/mypage/page.tsx +++ b/src/app/mypage/page.tsx @@ -1,14 +1,44 @@ -// 과제 1: 마이페이지 구현 +"use client"; + +import Header from "@/component/layout/Header"; +import { useUser } from "@/context/UserContext"; +import Link from "next/link"; + export default function MyPage() { // 1.1. UserContext를 활용한 Mypage 구현 (UserContext에 아이디(userId: string), 나이(age: number), 핸드폰번호(phoneNumber: string) 추가) + const { user } = useUser(); + return (
{/* 1.2. Header Component를 재활용하여 Mypage Header 표기 (title: 마이페이지) */} -

마이페이지

+
+
+

마이페이지

+
+
+ 이름: {user.name} +
+
+ 아이디: {user.userId} +
+
+ 나이: {user.age}세 +
+
+ 핸드폰번호: {user.phoneNumber} +
+
+ 이메일: {user.email} +
+
+
{/* Mypage 정보를 UserContext 활용하여 표시 (이름, 아이디, 나이, 핸드폰번호 모두 포함) */} {/* 1.3. 홈으로 가기 버튼 구현(Link or Router 활용) */} + + 홈으로 가기 +
); -} +} \ No newline at end of file diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx index c3b6212..d253d55 100644 --- a/src/app/search/page.tsx +++ b/src/app/search/page.tsx @@ -12,13 +12,20 @@ export default function SearchHome() { const { user, setUser } = useUser(); const { result } = useSearch(); + // 페이지 최초 렌더링 될 때, setUser로 이름 설정 // 페이지 최초 렌더링 될 때, setUser로 이름 설정 useEffect(() => { // 학번 + 이름 형태로 작성 (ex. 2025***** 내이름 ) - setUser({ name: "" }); + setUser({ + name: "202302616 정재현", + age : 26, + userId : "mhgrid", + email: "mhgrid@naver.com", + phoneNumber: "010-1234-5678" + }); }, []); - return ( + return (
diff --git a/src/component/search/SearchInput.tsx b/src/component/search/SearchInput.tsx index aea7294..e342a72 100644 --- a/src/component/search/SearchInput.tsx +++ b/src/component/search/SearchInput.tsx @@ -1,8 +1,14 @@ "use client"; import { useSearch } from "@/context/SearchContext"; +import React, { useEffect, useRef } from "react"; export default function SearchInput() { const { query, setQuery, setResult } = useSearch(); + const inputRef = useRef(null); + + useEffect(() => { + inputRef.current?.focus(); + }, []) // 검색 기능 const search = async () => { @@ -19,13 +25,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 (
sum + Number(item.lprice) * item.quantity, 0 ); + const router = useRouter(); // 2.4 결제하기: "결제하기" 버튼을 클릭하면, 현재 장바구니에 담긴 상품을 확인해 **localStorage**에 저장 후, 결제완료(/checkout) 페이지로 이동한다. - const handleCheckout = () => {}; + const handleCheckout = () => { + 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..bd73e90 100644 --- a/src/component/shopping/ProductCart.tsx +++ b/src/component/shopping/ProductCart.tsx @@ -8,6 +8,12 @@ export default function ProductCart({ items }: { items: ProductItem[] }) { const [cart, setCart] = useState<{ [id: string]: number }>({}); // {"88159814281" : 1} const [showCart, setShowCart] = useState(false); // 과제 2.1 + useEffect(() => { + const hasItems = Object.keys(cart).length > 0; + setShowCart(hasItems); + }, [cart]) + + // 카트에 담기 const handleAddToCart = (item: ProductItem, quantity: number) => { setCart((prev) => ({ @@ -20,7 +26,16 @@ export default function ProductCart({ items }: { items: ProductItem[] }) { }; /* 과제 2-3: Cart 아이템 지우기 */ - const handleRemoveFromCart = () => {}; + const handleRemoveFromCart = (productId: string) => { + setCart((prev) => { + const updated = Object.fromEntries( + Object.entries(prev).filter(([id]) => id !== productId) + ); + return updated; + }); + + localStorage.removeItem(productId); + }; return (
@@ -28,7 +43,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..a97c04a 100644 --- a/src/context/UserContext.tsx +++ b/src/context/UserContext.tsx @@ -1,28 +1,34 @@ "use client"; import { createContext, ReactNode, useContext, useState } from "react"; -// 과제 1.1 UserContext 구현 - -// User +// User 타입 정의 interface User { name: string; - // age: number - // 추가하고 싶은 속성들 ... + userId: string; + age: number; + phoneNumber: string; } -// UserContextType + +// Context 타입 정의 interface UserContextType { user: User; setUser: (user: User) => void; } -// 1. createContext +// Context 생성 export const UserContext = createContext( undefined ); -// 2. Provider 생성 +// Provider 컴포넌트 export const UserProvider = ({ children }: { children: ReactNode }) => { - const [user, setUser] = useState({ name: "" }); + const [user, setUser] = useState({ + name: "홍길동", + userId: "hong123", + age: 25, + phoneNumber: "010-1234-5678", + }); + return ( {children} @@ -30,12 +36,11 @@ export const UserProvider = ({ children }: { children: ReactNode }) => { ); }; -// 3. user 정보를 사용하기 위한 custom hook +// custom hook export const useUser = () => { const context = useContext(UserContext); - // 에러처리 if (!context) { - throw new Error("error"); + throw new Error("UserContext 내부에서만 사용해야 합니다."); } return context; }; diff --git a/tsconfig.json b/tsconfig.json index 49e8453..10e7795 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, "plugins": [ { From df8e60e41ca2fe102656f664f6b4268cf070fe0b Mon Sep 17 00:00:00 2001 From: jaehyun429 Date: Sun, 15 Jun 2025 22:02:12 +0900 Subject: [PATCH 2/2] =?UTF-8?q?202302616=20=EC=A0=95=EC=9E=AC=ED=98=84=20?= =?UTF-8?q?=EA=B3=BC=EC=A0=9C=EC=A0=9C=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/context/UserContext.tsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/context/UserContext.tsx b/src/context/UserContext.tsx index a97c04a..9e6c862 100644 --- a/src/context/UserContext.tsx +++ b/src/context/UserContext.tsx @@ -9,23 +9,20 @@ interface User { phoneNumber: string; } -// Context 타입 정의 interface UserContextType { user: User; setUser: (user: User) => void; } -// Context 생성 export const UserContext = createContext( undefined ); -// Provider 컴포넌트 export const UserProvider = ({ children }: { children: ReactNode }) => { const [user, setUser] = useState({ - name: "홍길동", - userId: "hong123", - age: 25, + name: "정재현", + userId: "mhgrid", + age: 26, phoneNumber: "010-1234-5678", }); @@ -36,7 +33,6 @@ export const UserProvider = ({ children }: { children: ReactNode }) => { ); }; -// custom hook export const useUser = () => { const context = useContext(UserContext); if (!context) {