Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 27 additions & 27 deletions src/app/api/search/route.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
// app/api/search/route.ts
import { NextResponse } from "next/server"; // API 응답 처리

// GET 요청 처리
export async function GET(request: Request) {
const { searchParams } = new URL(request.url); // request.url에서 searchParams를 추출해 query 값을 가져온다
const query = searchParams.get("query"); // ex. /api/search?query=하리보 → query = "하리보"

if (!query) {
return NextResponse.json({ error: "query 없음" }, { status: 400 });
}

const res = await fetch(
`https://openapi.naver.com/v1/search/shop.json?query=${encodeURIComponent(
query
)}`,
{
headers: {
"X-Naver-Client-Id": process.env.NAVER_CLIENT_ID!,
"X-Naver-Client-Secret": process.env.NAVER_CLIENT_SECRET!,
},
}
);

const data = await res.json();
return NextResponse.json(data);
}
// app/api/search/route.ts
import { NextResponse } from "next/server"; // API 응답 처리
// GET 요청 처리
export async function GET(request: Request) {
const { searchParams } = new URL(request.url); // request.url에서 searchParams를 추출해 query 값을 가져온다
const query = searchParams.get("query"); // ex. /api/search?query=하리보 → query = "하리보"
if (!query) {
return NextResponse.json({ error: "query 없음" }, { status: 400 });
}
const res = await fetch(
`https://openapi.naver.com/v1/search/shop.json?query=${encodeURIComponent(
query
)}`,
{
headers: {
"X-Naver-Client-Id": process.env.NAVER_CLIENT_ID!,
"X-Naver-Client-Secret": process.env.NAVER_CLIENT_SECRET!,
},
}
);
const data = await res.json();
return NextResponse.json(data);
}
85 changes: 64 additions & 21 deletions src/app/checkout/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,64 @@
// CheckoutPage
import { useState } from "react";
import { ProductItem } from "@/types/Product";

interface CheckoutItem {
product: ProductItem;
quantity: number;
}
// 과제 3
export default function CheckoutPage() {
const [items, setItems] = useState<CheckoutItem[]>([]);
// 3.1. 결제하기 구현
return (
<div className="p-6 max-w-3xl mx-auto bg-white rounded shadow mt-6">
<h1 className="text-2xl font-bold mb-4">✅ 결제가 완료되었습니다!</h1>
{/* 3.1. 결제하기 구현 */}
<div></div>
{/* 3.2. 홈으로 가기 버튼 구현 */}
</div>
);
}
"use client";
// CheckoutPage
import { useEffect, useState } from "react";
import Link from "next/link";

interface CheckoutItem {
productId: string;
title: string;
lprice: string;
quantity: number;
}
// 과제 3
export default function CheckoutPage() {
const [items, setItems] = useState<CheckoutItem[]>([]);

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.lprice) * item.quantity,
0
);

// 3.1. 결제하기 구현
return (
<div className="p-6 max-w-3xl mx-auto bg-white rounded shadow mt-6">
<h1 className="text-2xl font-bold mb-4">✅ 결제가 완료되었습니다!</h1>
{/* 3.1. 결제하기 구현 */}
{items.length === 0 ? (
<p className="text-gray-500">결제된 아이템이 없습니다</p>
) : (
<ul className="space-y-4">
{items.map((item) => (
<li key={item.productId} className="border p-4 rounded">
<p dangerouslySetInnerHTML={{ __html: item.title }} />
<p>수량: {item.quantity}</p>
<p>
가격: {(Number(item.lprice) * item.quantity).toLocaleString()}원{" "}
</p>
</li>
))}
<li className="text-right font-bold text-xl pt-4 border-t">
총합: {total.toLocaleString()}원
</li>
</ul>
)}

{/* 3.2. 홈으로 가기 버튼 구현 */}
<div className="mt-6 text-center">
<Link href="/">
<button className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
홈으로 가기
</button>
</Link>
</div>
</div>
);
}
142 changes: 71 additions & 71 deletions src/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,71 +1,71 @@
@import "tailwindcss";

/* global.css */

.page-container {
max-width: 800px;
margin: 40px auto;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
font-family: Arial, sans-serif;
}

.header {
padding: 20px;
font-size: 24px;
font-weight: bold;
text-align: center;
}

.content {
padding: 20px;
min-height: 150px;
font-size: 16px;
text-align: center;
}

.footer {
padding: 20px;
text-align: center;
}

.toggle-button {
margin-top: 10px;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
}

/* 다크 테마 */
.dark-bg {
background-color: #222;
color: #fff;
}

.dark-content {
background-color: #333;
color: #eee;
}

.dark-button {
background-color: #444;
color: #fff;
}

/* 라이트 테마 */
.light-bg {
background-color: #f5f5f5;
color: #333;
}

.light-content {
background-color: #fff;
color: #444;
}

.light-button {
background-color: #ddd;
color: #000;
}
@import "tailwindcss";
/* global.css */
.page-container {
max-width: 800px;
margin: 40px auto;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
font-family: Arial, sans-serif;
}
.header {
padding: 20px;
font-size: 24px;
font-weight: bold;
text-align: center;
}
.content {
padding: 20px;
min-height: 150px;
font-size: 16px;
text-align: center;
}
.footer {
padding: 20px;
text-align: center;
}
.toggle-button {
margin-top: 10px;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* 다크 테마 */
.dark-bg {
background-color: #222;
color: #fff;
}
.dark-content {
background-color: #333;
color: #eee;
}
.dark-button {
background-color: #444;
color: #fff;
}
/* 라이트 테마 */
.light-bg {
background-color: #f5f5f5;
color: #333;
}
.light-content {
background-color: #fff;
color: #444;
}
.light-button {
background-color: #ddd;
color: #000;
}
68 changes: 34 additions & 34 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});

const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}
Loading