diff --git a/next.config.mjs b/next.config.mjs
index 690d2d0d..0b29d3f6 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -1,7 +1,22 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
- /* config options here */
- reactCompiler: true,
+ images: {
+ domains: [
+ "example.com",
+ "sprint-fe-project.s3.ap-northeast-2.amazonaws.com",
+ "st.depositphotos.com",
+ "i.pinimg.com",
+ "upload.wikimedia.org",
+ "cdn.wccftech.com",
+ "encrypted-tbn0.gstatic.com",
+ "image.hanatour.com",
+ "cdn.choicenews.co.kr",
+ "health.chosun.com",
+ "via.placeholder.com",
+ "i.imgur.com",
+ "images.samsung.com",
+ ],
+ },
};
export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
index 7e70fe8d..1ab633a4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "sprint8",
"version": "0.1.0",
"dependencies": {
+ "@tanstack/react-query": "^5.90.7",
"clsx": "^2.1.1",
"dayjs": "^1.11.19",
"next": "16.0.1",
@@ -17,6 +18,7 @@
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
+ "@tanstack/eslint-plugin-query": "^5.91.2",
"autoprefixer": "^10.4.21",
"babel-plugin-react-compiler": "1.0.0",
"eslint": "^9",
@@ -1477,6 +1479,49 @@
"tailwindcss": "4.1.16"
}
},
+ "node_modules/@tanstack/eslint-plugin-query": {
+ "version": "5.91.2",
+ "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.91.2.tgz",
+ "integrity": "sha512-UPeWKl/Acu1IuuHJlsN+eITUHqAaa9/04geHHPedY8siVarSaWprY0SVMKrkpKfk5ehRT7+/MZ5QwWuEtkWrFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/utils": "^8.44.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ }
+ },
+ "node_modules/@tanstack/query-core": {
+ "version": "5.90.7",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.7.tgz",
+ "integrity": "sha512-6PN65csiuTNfBMXqQUxQhCNdtm1rV+9kC9YwWAIKcaxAauq3Wu7p18j3gQY3YIBJU70jT/wzCCZ2uqto/vQgiQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.90.7",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.7.tgz",
+ "integrity": "sha512-wAHc/cgKzW7LZNFloThyHnV/AX9gTg3w5yAv0gvQHPZoCnepwqCMtzbuPbb2UvfvO32XZ46e8bPOYbfZhzVnnQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/query-core": "5.90.7"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19"
+ }
+ },
"node_modules/@tybys/wasm-util": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
diff --git a/package.json b/package.json
index 2718077b..65abc780 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"lint": "eslint"
},
"dependencies": {
+ "@tanstack/react-query": "^5.90.7",
"clsx": "^2.1.1",
"dayjs": "^1.11.19",
"next": "16.0.1",
@@ -18,6 +19,7 @@
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
+ "@tanstack/eslint-plugin-query": "^5.91.2",
"autoprefixer": "^10.4.21",
"babel-plugin-react-compiler": "1.0.0",
"eslint": "^9",
diff --git a/src/Components/common/Header.jsx b/src/Components/common/Header.jsx
index 1967c469..62cade4e 100644
--- a/src/Components/common/Header.jsx
+++ b/src/Components/common/Header.jsx
@@ -1,8 +1,16 @@
+"use client";
import Image from "next/image";
import PandaLogo from "@/public/images/Group19.png";
import Link from "next/link";
+import { useAuth } from "@/providers/AuthProvider";
+import { useRouter } from "next/navigation";
export default function Header() {
+ const { user, logout, isInitialized } = useAuth();
+ const router = useRouter();
+
+ if (!isInitialized) return null;
+
return (
@@ -16,25 +24,48 @@ export default function Header() {
-
- 로그인
-
+
+
+ {user ? (
+
+
+
+ {user.nickname} 님
+
+
+
+ ) : (
+
+ )}
+
);
}
diff --git a/src/Components/ui/ProductsList/Pagination.jsx b/src/Components/ui/ProductsList/Pagination.jsx
new file mode 100644
index 00000000..7c6641e9
--- /dev/null
+++ b/src/Components/ui/ProductsList/Pagination.jsx
@@ -0,0 +1,39 @@
+"use client";
+import { IoIosArrowBack } from "react-icons/io";
+import { IoIosArrowForward } from "react-icons/io";
+
+export default function Pagination({ page, totalPages, onChange }) {
+ const pages = Array.from({ length: totalPages }, (_, i) => i + 1);
+
+ return (
+
+
+
+ {pages.map((num) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/Components/ui/ProductsList/ProductsBest.jsx b/src/Components/ui/ProductsList/ProductsBest.jsx
new file mode 100644
index 00000000..16119074
--- /dev/null
+++ b/src/Components/ui/ProductsList/ProductsBest.jsx
@@ -0,0 +1,47 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { getProducts } from "@/lib/services/products";
+import ProductsCard from "./ProductsCard";
+
+export default function ProductsBest() {
+ const [bestProducts, setBestProducts] = useState([]);
+ const [loading, setLoading] = useState(false);
+
+ useEffect(() => {
+ async function fetchBest() {
+ try {
+ setLoading(true);
+ const data = await getProducts({
+ orderBy: "favorite",
+ page: 1,
+ pageSize: 4,
+ });
+ setBestProducts(data.list);
+ } catch (error) {
+ console.error("베스트 상품 불러오기 실패:", error);
+ } finally {
+ setLoading(false);
+ }
+ }
+ fetchBest();
+ }, []);
+
+ return (
+
+
+ 베스트 상품
+
+
+ {loading ? (
+ 로딩 중...
+ ) : (
+
+ {bestProducts.map((item) => (
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/src/Components/ui/ProductsList/ProductsCard.jsx b/src/Components/ui/ProductsList/ProductsCard.jsx
new file mode 100644
index 00000000..bf18ac1d
--- /dev/null
+++ b/src/Components/ui/ProductsList/ProductsCard.jsx
@@ -0,0 +1,57 @@
+"use client";
+import Image from "next/image";
+import React, { useState } from "react";
+import heart from "@/public/images/ic_heart.png";
+import defaultImg from "@/public/images/eximg.png";
+import Link from "next/link";
+
+export default function ProductsCard({
+ id,
+ name,
+ description,
+ images,
+ favoriteCount,
+ price,
+}) {
+ console.log("상품 ID:", id, "이미지 데이터:", images);
+ const [currentImg, setCurrentImg] = useState(images?.[0] || defaultImg);
+
+ return (
+
+
+
+
setCurrentImg(defaultImg)}
+ // unoptimized Image 도메인 최적화 제한 해제 !
+ />
+
+
+ {description}
+
+
+ {price ? Number(price).toLocaleString() : 0}원
+
+
+
+
+ {favoriteCount}
+
+
+
+
+
+
+ );
+}
diff --git a/src/Components/ui/ProductsList/ProductsDetailPage.jsx b/src/Components/ui/ProductsList/ProductsDetailPage.jsx
new file mode 100644
index 00000000..968ebd7a
--- /dev/null
+++ b/src/Components/ui/ProductsList/ProductsDetailPage.jsx
@@ -0,0 +1,113 @@
+"use client";
+import Image from "next/image";
+import OptionsMenu from "../button/OptionsMenu";
+import { useAuth } from "@/providers/AuthProvider";
+import defaultImg from "@/public/images/profile.png";
+import heart from "@/public/images/heart.png";
+import ReserveButton from "../button/ReserveButton";
+import ProductsComment from "../comments/ProductsComment";
+
+export default function ProductsDetailPage({ product }) {
+ const {
+ name,
+ description,
+ images,
+ price,
+ tags,
+ ownerId,
+ favoriteCount,
+ ownerNickname,
+ date,
+ } = product;
+
+ const { user } = useAuth();
+
+ console.log("title:", name);
+ return (
+ <>
+
+
+
+
+
+
+ {name}
+
+
+
+
+ {price ? Number(price).toLocaleString() : 0}원
+
+
+
+ 상품소개
+
+
+ {description}
+
+
+
+
상품태그
+
+ {tags.map((tag, i) => {
+ return (
+
+ #{tag}
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+ {ownerNickname}
+
+
+ {date}
+
+
+
+
+
+
+ {favoriteCount}
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/Components/ui/ProductsList/ProductsList.jsx b/src/Components/ui/ProductsList/ProductsList.jsx
new file mode 100644
index 00000000..a17a84d4
--- /dev/null
+++ b/src/Components/ui/ProductsList/ProductsList.jsx
@@ -0,0 +1,52 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import ProductsSearchBar from "./ProductsSearchBar";
+import { getProducts } from "@/lib/services/products";
+import ProductsCard from "./ProductsCard";
+import Pagination from "./Pagination";
+
+export default function ProductsList() {
+ const [products, setProducts] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [orderBy, setOrderBy] = useState("recent");
+ const [keyword, setKeyword] = useState("");
+ const [page, setPage] = useState(1);
+ const totalPages = 5;
+
+ useEffect(() => {
+ console.log("현재 검색 키워드:", keyword);
+ async function fetchData() {
+ if (page > totalPages) return;
+ try {
+ setLoading(true);
+ const data = await getProducts({ orderBy, keyword, page });
+ console.log("api 데이타", data);
+ setProducts(data.list);
+ } catch (error) {
+ console.error("상품 불러오기 실패:", error);
+ } finally {
+ setLoading(false);
+ }
+ }
+ fetchData();
+ }, [orderBy, keyword, page]);
+
+ return (
+
+
+
+ {loading ? (
+ 로딩중...
+ ) : (
+
+ {products.map((item) => (
+
+ ))}
+
+ )}
+
+
+
+ );
+}
diff --git a/src/Components/ui/ProductsList/ProductsSearchBar.jsx b/src/Components/ui/ProductsList/ProductsSearchBar.jsx
new file mode 100644
index 00000000..32273b8d
--- /dev/null
+++ b/src/Components/ui/ProductsList/ProductsSearchBar.jsx
@@ -0,0 +1,86 @@
+import Image from "next/image";
+import React, { useState } from "react";
+import { IoMdArrowDropdown } from "react-icons/io";
+import SearchIcon from "@/public/images/search.png";
+import Link from "next/link";
+
+export default function ProductsSearchBar({ onSearch, onOrderChange }) {
+ const [isOpen, setIsOpen] = useState(false);
+ const [inputValue, setInputValue] = useState("");
+ const [select, setSelect] = useState("최신순");
+
+ const handleSearch = (e) => {
+ const inputItem = e.target.value;
+ setInputValue(inputItem);
+ console.log("검색 입력:", inputItem);
+ onSearch(inputItem);
+ };
+
+ const handleSelect = (order, label) => {
+ setSelect(label);
+ onOrderChange(order);
+ setIsOpen(false);
+ };
+
+ return (
+ <>
+
+
+
+ 판매 중인 상품
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {isOpen && (
+
+ - handleSelect("recent", "최신순")}
+ className="flex items-center justify-start px-5 py-3 cursor-pointer "
+ >
+ 최신순
+
+ - handleSelect("favorite", "좋아요순")}
+ className="flex items-center justify-start px-5 py-3 cursor-pointer border-t border-gray-200 "
+ >
+ 좋아요순
+
+
+ )}
+
+
+
+ >
+ );
+}
diff --git a/src/Components/ui/button/LoginBtn.jsx b/src/Components/ui/button/LoginBtn.jsx
deleted file mode 100644
index 53af4984..00000000
--- a/src/Components/ui/button/LoginBtn.jsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from "react";
-
-export default function LoginBtn() {
- return (
-
- );
-}
diff --git a/src/Components/ui/button/OptionsMenu.jsx b/src/Components/ui/button/OptionsMenu.jsx
new file mode 100644
index 00000000..43c292ed
--- /dev/null
+++ b/src/Components/ui/button/OptionsMenu.jsx
@@ -0,0 +1,16 @@
+import React from "react";
+import { BsThreeDotsVertical } from "react-icons/bs";
+
+export default function OptionsMenu() {
+ return (
+
+ );
+}
diff --git a/src/Components/ui/button/ReserveButton.jsx b/src/Components/ui/button/ReserveButton.jsx
index 185d864e..d01f6863 100644
--- a/src/Components/ui/button/ReserveButton.jsx
+++ b/src/Components/ui/button/ReserveButton.jsx
@@ -1,12 +1,12 @@
import Link from "next/link";
import React from "react";
-export default function ReserveButton() {
+export default function ReserveButton({ href = "/" }) {
return (
목록으로 돌아가기
diff --git a/src/Components/ui/comments/ProductsComment.jsx b/src/Components/ui/comments/ProductsComment.jsx
new file mode 100644
index 00000000..1b0cd2d6
--- /dev/null
+++ b/src/Components/ui/comments/ProductsComment.jsx
@@ -0,0 +1,102 @@
+"use client";
+import React, { useState } from "react";
+import OptionsMenu from "../button/OptionsMenu";
+import Image from "next/image";
+import profile from "@/public/images/profile.png";
+import { useAuth } from "@/providers/AuthProvider";
+import { timeAgo } from "@/lib/utils/time";
+
+export default function ProductsComment() {
+ const [content, setContent] = useState("");
+ const [comments, setComments] = useState([]);
+
+ const { user } = useAuth();
+
+ const handleInput = (e) => {
+ setContent(e.target.value);
+ };
+
+ const handleKeyDown = (e) => {
+ if (e.key === "Enter") {
+ e.preventDefault(); // ✔ 줄바꿈 방지
+ handleSubmit();
+ }
+ };
+
+ const handleSubmit = () => {
+ const cleanContent = content.trim();
+
+ if (cleanContent === "") {
+ console.log("공백은 등록할 수 없습니다!");
+ return;
+ }
+
+ setComments((prev) => [...prev, { text: cleanContent, date: new Date() }]);
+
+ setContent("");
+ };
+
+ return (
+ <>
+
+
+
+ {comments.map((comment, i) => (
+ -
+
+
+
+
+
+
+
+
+
+
+ {user?.nickname || "비로그인 사용자!"}
+
+
+
+ {timeAgo(comment.date)}
+
+
+
+
+
+ ))}
+
+
+ >
+ );
+}
diff --git a/src/Components/ui/modal/Modal.jsx b/src/Components/ui/modal/Modal.jsx
new file mode 100644
index 00000000..127ca50e
--- /dev/null
+++ b/src/Components/ui/modal/Modal.jsx
@@ -0,0 +1,21 @@
+"use client";
+import React from "react";
+
+export default function Modal({ isOpen, message, onClose }) {
+ if (!isOpen) return null;
+
+ return (
+
+ );
+}
diff --git a/src/app/(auth)/_components/_login/LoginInput.jsx b/src/app/(auth)/_components/_login/LoginInput.jsx
index 8edbaa1d..1118c16e 100644
--- a/src/app/(auth)/_components/_login/LoginInput.jsx
+++ b/src/app/(auth)/_components/_login/LoginInput.jsx
@@ -1,8 +1,13 @@
"use client";
+import { authService } from "@/lib/services/auth";
import clsx from "clsx";
import Image from "next/image";
+import { useRouter } from "next/navigation";
import React, { useState } from "react";
import { AiOutlineEye, AiOutlineEyeInvisible } from "react-icons/ai";
+import EasyLogin from "./EasyLogin";
+import { useAuth } from "@/providers/AuthProvider";
+import Modal from "@/components/ui/modal/Modal";
export default function LoginInput() {
const [showEye, setShowEye] = useState(false);
@@ -10,13 +15,20 @@ export default function LoginInput() {
const [emailError, setEmailError] = useState("");
const [password, setPassword] = useState("");
const [passwordError, setPasswordError] = useState("");
+ const [error, setError] = useState(null);
+ const [loading, setLoading] = useState(false);
+ const [modalOpen, setModalOpen] = useState(false);
+ const [modalMessage, setModalMessage] = useState("");
+ const { login } = useAuth();
+ const router = useRouter();
const handleEmail = (e) => {
- setEmail(e.target.value);
+ const emailValue = e.target.value;
+ setEmail(emailValue);
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
- if (e.target.value && !emailRegex.test(e.target.value)) {
+ if (emailValue && !emailRegex.test(emailValue)) {
setEmailError("잘못된 이메일입니다!");
} else {
setEmailError("");
@@ -24,12 +36,13 @@ export default function LoginInput() {
};
const handlePassword = (e) => {
- setPassword(e.target.value);
+ const passwordValue = e.target.value;
+ setPassword(passwordValue);
const passwordRegEx =
/^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/;
- if (e.target.value && !passwordRegEx.test(e.target.value)) {
+ if (passwordValue && !passwordRegEx.test(passwordValue)) {
setPasswordError(
"비밀번호는 8자 이상, 영문·숫자·특수문자를 포함해야 합니다!"
);
@@ -38,8 +51,31 @@ export default function LoginInput() {
}
};
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ if (emailError || passwordError) {
+ setError("정보확인요망!");
+ return;
+ }
+
+ try {
+ setLoading(true);
+
+ const result = await login(email, password);
+ setModalMessage("로그인 성공!");
+ setModalOpen(true);
+ // router.push("/items");
+ } catch (error) {
+ setModalMessage("로그인에 실패했습니다!");
+ setModalOpen(true);
+ } finally {
+ setLoading(false);
+ }
+ };
+
return (
-
+
+
+ {error &&
{error}
}
+
setModalOpen(false)}
+ />
);
}
diff --git a/src/app/(auth)/_components/_signup/Signp.jsx b/src/app/(auth)/_components/_signup/Signp.jsx
index 42f98960..260c3370 100644
--- a/src/app/(auth)/_components/_signup/Signp.jsx
+++ b/src/app/(auth)/_components/_signup/Signp.jsx
@@ -1,23 +1,123 @@
"use client";
+import { authService } from "@/lib/services/auth";
+import clsx from "clsx";
import Image from "next/image";
+import { useRouter } from "next/navigation";
import React, { useState } from "react";
import { AiOutlineEye, AiOutlineEyeInvisible } from "react-icons/ai";
export default function SignInput() {
const [showEye, setShowEye] = useState(false);
+ const [email, setEmail] = useState("");
+ const [nickname, setNickname] = useState("");
+ const [emailError, setEmailError] = useState("");
+ const [password, setPassword] = useState("");
+ const [passwordError, setPasswordError] = useState("");
+ const [passwordConfirm, setPasswordConfirm] = useState("");
+ const [confirmError, setConfirmError] = useState("");
+ const [error, setError] = useState("");
+ const [loading, setLoading] = useState(false);
+ const router = useRouter();
+
+ //email 검증
+ const handleEmail = (e) => {
+ const emailValue = e.target.value;
+ setEmail(emailValue);
+
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+
+ if (emailValue && !emailRegex.test(emailValue)) {
+ setEmailError("잘못된 이메일입니다.");
+ } else {
+ setEmailError("");
+ }
+ };
+
+ const handleNickName = (e) => {
+ const nickName = e.target.value;
+ setNickname(nickName);
+ };
+
+ //password 검증
+ const handlePassword = (e) => {
+ const passwordValue = e.target.value;
+ setPassword(passwordValue);
+
+ const passwordRegEx =
+ /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/;
+
+ if (passwordValue && !passwordRegEx.test(passwordValue)) {
+ setPasswordError(
+ "비밀번호는 8자 이상, 영문·숫자·특수문자를 포함해야 합니다!"
+ );
+ } else {
+ setPasswordError("");
+ }
+ };
+
+ //password 확인
+ const handlePasswordConfirm = (e) => {
+ const confirmValue = e.target.value;
+ setPasswordConfirm(confirmValue);
+
+ if (confirmValue !== password) {
+ setConfirmError("비밀번호가 일치하지 않습니다.");
+ } else {
+ setConfirmError("");
+ }
+ };
+
+ const handleSignup = async (e) => {
+ e.preventDefault();
+
+ if (emailError || passwordError || confirmError) {
+ setError("입력하신 정보를 다시 확인해주세요.");
+ return;
+ }
+ if (!email || !password || !nickname || !passwordConfirm) {
+ setError("입력해주세요!");
+ return;
+ }
+ try {
+ setError(null);
+ setLoading(true);
+
+ const result = await authService.signUp(
+ email,
+ nickname,
+ password,
+ passwordConfirm
+ );
+ console.log("회원가입 완료", result);
+ alert("회원가입 완료!");
+ router.push("/login");
+ } catch (error) {
+ setError(error.message || "회원가입에 실패했습니다.");
+ } finally {
+ setLoading(false);
+ }
+ };
return (
-
+
);
}
diff --git a/src/app/(auth)/login/page.jsx b/src/app/(auth)/login/page.jsx
index 21e798db..8d723f5b 100644
--- a/src/app/(auth)/login/page.jsx
+++ b/src/app/(auth)/login/page.jsx
@@ -1,6 +1,5 @@
import React from "react";
import LoginInput from "../_components/_login/LoginInput";
-import LoginBtn from "../../../components/ui/button/LoginBtn";
import PandaLogo from "../../../components/ui/logo/PandaLogo";
import EasyLogin from "../_components/_login/EasyLogin";
@@ -10,7 +9,6 @@ export default function LoginPage() {
diff --git a/src/app/(main)/articles/[id]/page.jsx b/src/app/(main)/articles/[id]/page.jsx
index 9597edc1..7c4fc251 100644
--- a/src/app/(main)/articles/[id]/page.jsx
+++ b/src/app/(main)/articles/[id]/page.jsx
@@ -1,12 +1,10 @@
"use client";
import React, { useEffect, useState } from "react";
import { useParams, useRouter } from "next/navigation";
-import { BsThreeDotsVertical } from "react-icons/bs";
import { getArticleById } from "@/lib/services/articles";
import Image from "next/image";
-import { Flamenco } from "next/font/google";
-import Link from "next/link";
import ReserveButton from "@/components/ui/button/ReserveButton";
+import OptionsMenu from "@/components/ui/button/OptionsMenu";
export default function ArticleDetail() {
const { id } = useParams();
@@ -36,13 +34,7 @@ export default function ArticleDetail() {
@@ -104,13 +96,7 @@ export default function ArticleDetail() {
혹시 사용기간이 어떻게 되실까요!?
-
+
@@ -132,7 +118,7 @@ export default function ArticleDetail() {
-
+
>
);
}
diff --git a/src/app/(main)/items/[id]/page.jsx b/src/app/(main)/items/[id]/page.jsx
new file mode 100644
index 00000000..b20eacf0
--- /dev/null
+++ b/src/app/(main)/items/[id]/page.jsx
@@ -0,0 +1,16 @@
+import ProductsDetailPage from "@/components/ui/ProductsList/ProductsDetailPage";
+import { getProductById } from "@/lib/services/products";
+import React from "react";
+
+export default async function ItemPage({ params }) {
+ const { id } = await params;
+ const product = await getProductById(id);
+
+ return (
+ <>
+
+
+
+ >
+ );
+}
diff --git a/src/app/(main)/items/page.jsx b/src/app/(main)/items/page.jsx
new file mode 100644
index 00000000..1cfedf77
--- /dev/null
+++ b/src/app/(main)/items/page.jsx
@@ -0,0 +1,15 @@
+import ProductsBest from "@/components/ui/ProductsList/ProductsBest";
+import ProductsList from "@/components/ui/ProductsList/ProductsList";
+
+export default function ItemsPage() {
+ return (
+
+
+
+
+ );
+}
diff --git a/src/app/(main)/items/write/page.jsx b/src/app/(main)/items/write/page.jsx
new file mode 100644
index 00000000..f479db09
--- /dev/null
+++ b/src/app/(main)/items/write/page.jsx
@@ -0,0 +1,5 @@
+import React from "react";
+
+export default function page() {
+ return
페이지 글쓰기
;
+}
diff --git a/src/app/(main)/layout.jsx b/src/app/(main)/layout.jsx
index 523850be..8ef1f882 100644
--- a/src/app/(main)/layout.jsx
+++ b/src/app/(main)/layout.jsx
@@ -1,13 +1,16 @@
+"use client";
+
import Footer from "@/components/common/Footer";
import Header from "@/components/common/Header";
+// import AuthProvider from "@/providers/AuthProvider";
import React from "react";
export default function MainLayout({ children }) {
return (
<>
-
- {children}
+
+ {children}
>
diff --git a/src/app/layout.jsx b/src/app/layout.jsx
index 049a6490..a5324249 100644
--- a/src/app/layout.jsx
+++ b/src/app/layout.jsx
@@ -2,6 +2,7 @@ import { Geist, Geist_Mono } from "next/font/google";
import "@/app/styles/globals.css";
import Header from "@/components/common/Header";
import Footer from "@/components/common/Footer";
+import Providers from "@/providers/Providers";
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -25,7 +26,7 @@ export default function RootLayout({ children }) {
className={`${geistSans.variable} ${geistMono.variable} flex flex-col justify-center items-center min-h-screen
`}
>
- {children}
+ {children}