diff --git a/package-lock.json b/package-lock.json
index a1e590ee6..6245c1335 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,10 +11,15 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
+ "axios": "^1.7.9",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-router-dom": "^7.0.2",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
+ },
+ "devDependencies": {
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -630,9 +635,18 @@
}
},
"node_modules/@babel/plugin-proposal-private-property-in-object": {
- "version": "7.21.0-placeholder-for-preset-env.2",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
- "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
+ "version": "7.21.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz",
+ "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==",
+ "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-create-class-features-plugin": "^7.21.0",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
+ },
"engines": {
"node": ">=6.9.0"
},
@@ -1875,6 +1889,18 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.21.0-placeholder-for-preset-env.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
+ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/preset-env/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -3953,6 +3979,12 @@
"@types/node": "*"
}
},
+ "node_modules/@types/cookie": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
+ "license": "MIT"
+ },
"node_modules/@types/eslint": {
"version": "8.44.2",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz",
@@ -5285,6 +5317,31 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "1.7.9",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
+ "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/axios/node_modules/form-data": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
+ "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/axobject-query": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@@ -8294,15 +8351,16 @@
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ=="
},
"node_modules/follow-redirects": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
- "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+ "version": "1.15.9",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+ "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
+ "license": "MIT",
"engines": {
"node": ">=4.0"
},
@@ -14375,6 +14433,12 @@
"node": ">= 0.10"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "license": "MIT"
+ },
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -14671,6 +14735,55 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-router": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.0.2.tgz",
+ "integrity": "sha512-m5AcPfTRUcjwmhBzOJGEl6Y7+Crqyju0+TgTQxoS4SO+BkWbhOrcfZNq6wSWdl2BBbJbsAoBUb8ZacOFT+/JlA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/cookie": "^0.6.0",
+ "cookie": "^1.0.1",
+ "set-cookie-parser": "^2.6.0",
+ "turbo-stream": "2.4.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.0.2.tgz",
+ "integrity": "sha512-VJOQ+CDWFDGaWdrG12Nl+d7yHtLaurNgAQZVgaIy7/Xd+DojgmYLosFfZdGz1wpxmjJIAkAMVTKWcvkx1oggAw==",
+ "license": "MIT",
+ "dependencies": {
+ "react-router": "7.0.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/react-router/node_modules/cookie": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
+ "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/react-scripts": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
@@ -15480,6 +15593,12 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
+ "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
+ "license": "MIT"
+ },
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -16512,6 +16631,12 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
+ "node_modules/turbo-stream": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
+ "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==",
+ "license": "ISC"
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/package.json b/package.json
index 7ff0d6b58..11437aad4 100644
--- a/package.json
+++ b/package.json
@@ -6,8 +6,10 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
+ "axios": "^1.7.9",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-router-dom": "^7.0.2",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
@@ -34,5 +36,8 @@
"last 1 firefox version",
"last 1 safari version"
]
+ },
+ "devDependencies": {
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11"
}
}
diff --git a/public/images/arrow_next.svg b/public/images/arrow_next.svg
new file mode 100644
index 000000000..4261e52df
--- /dev/null
+++ b/public/images/arrow_next.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/images/arrow_prev.svg b/public/images/arrow_prev.svg
new file mode 100644
index 000000000..6b3286f2f
--- /dev/null
+++ b/public/images/arrow_prev.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/images/detail_like_icon.svg b/public/images/detail_like_icon.svg
new file mode 100644
index 000000000..7b994fbe7
--- /dev/null
+++ b/public/images/detail_like_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/images/noinquiry_img.svg b/public/images/noinquiry_img.svg
new file mode 100644
index 000000000..5444cbbbc
--- /dev/null
+++ b/public/images/noinquiry_img.svg
@@ -0,0 +1,17 @@
+
diff --git a/public/images/return_icon.svg b/public/images/return_icon.svg
new file mode 100644
index 000000000..014359ce4
--- /dev/null
+++ b/public/images/return_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/images/setting_icon.svg b/public/images/setting_icon.svg
new file mode 100644
index 000000000..85b6a28f4
--- /dev/null
+++ b/public/images/setting_icon.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/App.js b/src/App.js
index 1c433f75f..d953960ff 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,15 +1,18 @@
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './components/page/Home';
-import ProductReg from './components/page/ProductReg';
-import ProductList from './components/page/ProductList';
+import ProductListPage from './components/page/ProductListPage';
+import ProductRegPage from './components/page/ProductRegPage';
+import ProductDetailPage from './components/page/ProductDetailPage';
+
function App() {
return (
} />
- } />
- } />
+ } />
+ } />
+ } />
);
diff --git a/src/asset/css/style.css b/src/asset/css/style.css
index 32d0f058e..d1e82539a 100644
--- a/src/asset/css/style.css
+++ b/src/asset/css/style.css
@@ -44,7 +44,6 @@
/* 공통-레이아웃 */
main {
margin-top: 70px;
- min-height: 600px;
}
p.title {
@@ -208,7 +207,7 @@ p.title {
#header nav .depth01 li h2 a {
display: block;
- padding: 0 15px;
+ padding: 24px 15px;
font-size: 18px;
font-weight: 700;
color: var(--gray03);
@@ -272,8 +271,53 @@ p.title {
}
*/
+@media (max-width: 1199px) {
+
+ #header nav .depth01 li h2 a {
+ padding: 25px 15px;
+ font-size: 16px;
+ }
+
+}
+
+@media (max-width: 767px) {
+
+ #header .logo h1 a img {
+ width: 120px;
+ }
+
+ #header .logo {
+ margin-right: 20px;
+ }
+
+ #header nav .depth01 li h2 a {
+ padding: 27px 10px;
+ font-size: 14px;
+ }
+
+}
+
+@media (max-width: 414px) {
+
+ #header .logo h1 a img {
+ width: 100px;
+ }
+
+ #header nav .depth01 li h2 a {
+ padding: 28px 8px;
+ font-size: 12px;
+ }
+
+ #header .logo {
+ margin-right: 10px;
+ }
+
+}
+
+
/* 공통-푸터 */
#footer {
+ position: relative;
padding: 33px 0 0;
height: 160px;
background-color: #111827;
@@ -620,7 +664,8 @@ p.title {
}
#sub {
- padding: 24px 0 58px;
+ padding: 24px 0 100px;
+ min-height: 1000px;
background-color: #fcfcfc;
}
@@ -737,6 +782,7 @@ p.title {
}
#sub .productListBox .productBox {
+ position: relative;
margin-bottom: 49px;
}
@@ -788,24 +834,23 @@ p.title {
top: -9px;
right: 0;
height: 42px;
+ border-radius: 12px;
}
.searchWrap .searchBox {
+ position: relative;
width: 325px;
+ display: flex;
}
-.searchWrap .searchBox .searchBtnBox {
+.searchWrap .searchBox::after {
+ content: '';
position: absolute;
- top: 14px;
- left: 20px;
-}
-
-.searchWrap .searchBox .searchBtnBox .searchBtn {
- display: block;
+ top: 13px;
+ left: 17px;
width: 15px;
height: 15px;
- background: url(/public/images/search_icon.svg)no-repeat center;
- background-size: cover;
+ background: url(/public/images/search_icon.svg)no-repeat center center;
}
.searchWrap .searchBox input {
@@ -864,10 +909,11 @@ p.title {
.searchWrap .selectBox .select.active+.viewList {
opacity: 1;
- z-index: 11;
+ z-index: 99;
}
.searchWrap .selectBox .viewList {
+ position: relative;
opacity: 0;
margin-top: 8px;
border: 1px solid #E5E7EB;
@@ -875,6 +921,7 @@ p.title {
overflow: hidden;
z-index: -1;
transition: all .3s;
+ background-color: var(--white01);
}
.searchWrap .selectBox .viewList li {
@@ -910,20 +957,586 @@ p.title {
}
/* 상품등록 */
-.productRegWrap .productRegBox{position: relative;}
-.productRegWrap .LayoutBox{display: flex; gap: 0 24px;}
-.productRegWrap .productUploadeImg{position: relative; width: 282px; height: 282px; border-radius: 12px; overflow: hidden;}
-.productRegWrap .productUploadeImg img{display: block; width: 100%;}
-.productRegWrap .productUploadeImg .delBtn{position: absolute; top:12px; right:12px; width: 20px; height: 24px; background: url(/public/images/del_btn_icon.png)no-repeat center; background-size: cover;}
+.productRegWrap .productRegBox {
+ position: relative;
+}
+
+.productRegWrap .LayoutBox {
+ display: flex;
+ gap: 0 24px;
+}
+
+.productRegWrap .productUploadeImg {
+ position: relative;
+ width: 282px;
+ height: 282px;
+ border-radius: 12px;
+ overflow: hidden;
+}
+
+.productRegWrap .productUploadeImg img {
+ display: block;
+ width: 100%;
+}
+
+.productRegWrap .productUploadeImg .delBtn {
+ position: absolute;
+ top: 12px;
+ right: 12px;
+ width: 20px;
+ height: 24px;
+ background: url(/public/images/del_btn_icon.png)no-repeat center;
+ background-size: cover;
+}
/* 폼 */
-.formBox{margin: 0 0 32px;}
-.formBox .inputFileBox label{display: flex; align-items: center; justify-content: center; width: 282px; height: 282px; background-color: var(--gray04); border-radius: 12px; cursor: pointer;}
-.formBox .inputFileBox label span{position: relative; display: flex; flex-direction: column; align-items: center; gap: 15px 0; color: var(--gray01); line-height: 1;}
-.formBox .inputFileBox label span::before{content: ''; width: 48px; height: 48px; background: url(/public/images/add_btn_icon.png)no-repeat center; background-size: cover;}
-.formBox .inputTextBox input{padding: 20px 20px 20px 24px; width: 100%; border-radius: 12px; background-color: var(--gray04); font-size: 16px;}
-.formBox .inputTextBox input::placeholder{color: var(--gray01); font-size: 16px;}
-.formBox .textAreaBox textarea{padding: 20px 20px 20px 24px; width: 100%; height: 282px; border: 0; background-color: var(--gray04); font-family: 'pretendard', sans-serif; font-size: 16px; border-radius: 12px;}
-.formBox .textAreaBox textarea::placeholder{color: var(--gray01);}
-.productRegBox .addBtn{position: absolute; top:0; right:0; display: block; width: 74px; height: 42px; line-height: 42px; text-align: center; background-color: var(--blue); border-radius: 8px; color: #fff; color: var(--gray04); font-size: 16px;}
-.productRegBox .addBtn:disabled{background-color: var(--gray01); cursor: auto;}
\ No newline at end of file
+.formBox {
+ margin: 0 0 32px;
+}
+
+.formBox .inputFileBox label {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 282px;
+ height: 282px;
+ background-color: var(--gray04);
+ border-radius: 12px;
+ cursor: pointer;
+}
+
+.formBox .inputFileBox label span {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 15px 0;
+ color: var(--gray01);
+ line-height: 1;
+}
+
+.formBox .inputFileBox label span::before {
+ content: '';
+ width: 48px;
+ height: 48px;
+ background: url(/public/images/add_btn_icon.png)no-repeat center;
+ background-size: cover;
+}
+
+.formBox .inputTextBox input {
+ padding: 20px 20px 20px 24px;
+ width: 100%;
+ border-radius: 12px;
+ background-color: var(--gray04);
+ font-size: 16px;
+}
+
+.formBox .inputTextBox input::placeholder {
+ color: var(--gray01);
+ font-size: 16px;
+}
+
+.formBox .textAreaBox textarea {
+ padding: 20px 20px 20px 24px;
+ width: 100%;
+ height: 282px;
+ border: 0;
+ background-color: var(--gray04);
+ font-family: 'pretendard', sans-serif;
+ font-size: 16px;
+ border-radius: 12px;
+}
+
+.formBox .textAreaBox textarea::placeholder {
+ color: var(--gray01);
+}
+
+.productRegBox .addBtn {
+ position: absolute;
+ top: 0;
+ right: 0;
+}
+
+.addBtn {
+ display: block;
+ width: 74px;
+ height: 42px;
+ line-height: 42px;
+ text-align: center;
+ background-color: var(--blue);
+ border-radius: 8px;
+ color: var(--white01);
+ font-size: 16px;
+}
+
+.addBtn:disabled {
+ background-color: var(--gray01);
+ cursor: auto;
+}
+
+/* 페이지네이션 */
+.paginationBox {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ align-items: center;
+ gap: 0 4px;
+}
+
+.paginationBox .pagination {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0 4px;
+}
+
+.paginationBox .pageLink {
+ padding-top: 2px;
+}
+
+.paginationBox a {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 40px;
+ height: 40px;
+ border-radius: 50px;
+ color: #6B7280;
+ border: 1px solid #E5E7EB;
+ font-weight: 600;
+}
+
+.paginationBox .pageLinkPrev::before {
+ content: '';
+ width: 6px;
+ height: 10px;
+ background: url(/public/images/arrow_prev.svg)no-repeat center;
+}
+
+.paginationBox .pageLinkNext::before {
+ content: '';
+ width: 6px;
+ height: 10px;
+ background: url(/public/images/arrow_next.svg)no-repeat center;
+}
+
+.paginationBox .active {
+ background-color: var(--blue);
+ color: var(--white01);
+ border: 0;
+}
+
+/* 상세페이지 */
+.detail {
+ padding-bottom: 40px;
+ display: flex;
+ gap: 0 24px;
+ border-bottom: 1px solid #E5E7EB;
+}
+
+.detail>.imgBox {
+ width: 486px;
+ height: 486px;
+ border-radius: 40PX;
+ overflow: hidden;
+ flex-shrink: 0;
+ box-shadow: 0 0 10px rgba(0, 0, 0, .1);
+}
+
+.detail>.imgBox img {
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.detail .textBox {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ width: 100%;
+}
+
+.detail .textBox .text {
+ margin-bottom: 16px;
+ display: block;
+ color: var(--gray03);
+ line-height: 1.6;
+ font-weight: 600;
+}
+
+.detail .textBox .productTop {
+ margin-bottom: 24px;
+ padding-bottom: 16px;
+ border-bottom: 1px solid #E5E7EB;
+}
+
+.detail .textBox .title {
+ margin-bottom: 16px;
+ color: var(--gray02);
+ font-size: 24px;
+ font-weight: 600;
+}
+
+.detail .textBox .price {
+ display: block;
+ font-size: 40px;
+ font-weight: 600;
+ color: var(--gray02);
+}
+
+.detail .textBox .productIntroduction {
+ margin-bottom: 24px;
+}
+
+.detail .textBox .productIntroduction p {
+ color: var(--gray03);
+ line-height: 1.6;
+}
+
+.detail .textBox .productTag {
+ margin-bottom: 62px;
+}
+
+.detail .textBox .productTagList {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.detail .textBox .productTagList em {
+ padding: 5px 16px;
+ border-radius: 50px;
+ color: var(--gray02);
+ line-height: 1.6;
+ background-color: var(--gray04);
+ font-style: normal;
+}
+
+.detail .textBox .userBox {
+ margin-top: auto;
+ display: flex;
+ align-items: center;
+ gap: 0 16px
+}
+
+.detail .textBox .userBox .imgBox {
+ width: 40px;
+ height: 40px;
+ flex-shrink: 0;
+}
+
+.detail .textBox .userBox .imgBox img {
+ display: block;
+ width: 100%;
+}
+
+.detail .textBox .userBox .textBox .userId {
+ margin-bottom: 2px;
+ color: var(--gray03);
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 1.7;
+}
+
+.detail .textBox .userBox .textBox .regDate {
+ color: var(--gray01);
+ font-size: 14px;
+ line-height: 1.7;
+}
+
+.detail .textBox .likeBox {
+ padding: 6px 15px;
+ display: flex;
+ align-items: center;
+ gap: 0 7px;
+ border-radius: 50px;
+ border: 1px solid #E5E7EB;
+ background-color: var(--white01);
+}
+
+.detail .textBox .likeBox img {
+ margin-top: 2px;
+}
+
+.detail .textBox .likeBox span {
+ font-weight: 500;
+ color: #6B7280;
+ line-height: 1.6;
+}
+
+@media (max-width: 1199px) {
+
+ .detail {
+ padding-bottom: 32px;
+ }
+
+ .detail>.imgBox {
+ width: 340px;
+ height: 340px;
+ }
+
+ .detail .textBox .productTop {
+ margin-bottom: 16px;
+ }
+
+ .detail .textBox .productTag {
+ margin-bottom: 40px;
+ }
+
+ .detail .textBox .title {
+ margin-bottom: 8px;
+ font-size: 20px;
+ }
+
+ .detail .textBox .price {
+ font-size: 32px;
+ }
+
+ .detail .textBox .text {
+ margin-bottom: 8px;
+ font-size: 14px;
+ }
+
+ .detail .textBox .likeBox {
+ padding: 2px 15px;
+ }
+
+ .detail .textBox .likeBox img {
+ width: 20px;
+ margin: 0;
+ }
+
+}
+
+@media (max-width: 576px) {
+
+ .detail {
+ padding-bottom: 24px;
+ flex-direction: column;
+ }
+
+ .detail>.imgBox {
+ margin-bottom: 16px;
+ width: 100%;
+ border-radius: 12px;
+ }
+
+ .detail .textBox .title {
+ font-size: 16px;
+ }
+
+ .detail .textBox .price {
+ font-size: 24px;
+ }
+
+}
+
+/* 문의하기 */
+.inquiryBox {
+ padding: 40px 0 0;
+ margin-bottom: 100px;
+}
+
+.inquiryBox .text {
+ display: block;
+ margin-bottom: 9px;
+ color: #111827;
+ line-height: 1.6;
+ font-weight: 600;
+}
+
+.inquiryBox form textarea {
+ margin-bottom: 16px;
+ padding: 16px 24px;
+ width: 100%;
+ height: 104px;
+ overflow-y: auto;
+ background-color: var(--gray04);
+ border-radius: 12px;
+ border: 0;
+ font-size: 16px;
+ font-family: 'pretendard', sans-serif;
+}
+
+.inquiryBox form textarea::placeholder {
+ color: var(--gray01);
+}
+
+.inquiryBox form .btnBox {
+ display: flex;
+ justify-content: flex-end;
+}
+
+/* 댓글 */
+.inquiryInfoList .box .comment {
+ margin-bottom: 24px;
+ font-size: 14px;
+ line-height: 1.7;
+ color: var(--gray02);
+}
+
+.inquiryInfoList .box {
+ position: relative;
+ padding: 24px 0 12px;
+ border-bottom: 1px solid #E5E7EB;
+}
+
+.inquiryInfoList .box .productInquiryInfo {
+ display: flex;
+ align-items: center;
+ gap: 0 8px;
+}
+
+.inquiryInfoList .box .selectBox {
+ position: absolute;
+ top: 29px;
+ right: 10px;
+}
+
+.inquiryInfoList .box .selectBox .selectBtn {
+ display: block;
+ width: 3px;
+ height: 13px;
+ background: url(/public/images/setting_icon.svg)no-repeat center;
+}
+
+.inquiryInfoList .box .selectBox .selectBtn.active+.view {
+ opacity: 1;
+ z-index: 999;
+}
+
+.inquiryInfoList .box .selectBox .view {
+ opacity: 0;
+ position: absolute;
+ top: 29px;
+ right: -10px;
+ width: 139px;
+ height: 92px;
+ border-radius: 8px;
+ border: 1px solid #D1D5DB;
+ background-color: var(--white01);
+ overflow: hidden;
+ z-index: -999;
+ transition: all .3s;
+}
+
+.inquiryInfoList .box .selectBox .view .select {
+ display: block;
+ width: 100%;
+ height: 46px;
+ color: #6B7280;
+ text-align: center;
+ background-color: var(--white01);
+ font-size: 14px;
+}
+
+.inquiryInfoList .box .selectBox .view .select:hover {
+ font-weight: 500;
+}
+
+.inquiryInfoList .box .productInquiryInfo .imgBox {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ overflow: hidden;
+}
+
+.inquiryInfoList .box .productInquiryInfo .imgBox img {
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.inquiryInfoList .box .productInquiryInfo .textBox .writer {
+ margin-bottom: 4px;
+ font-size: 12px;
+ color: var(--gray03);
+ line-height: 1.5;
+}
+
+.inquiryInfoList .box .productInquiryInfo .textBox .time {
+ font-size: 12px;
+ color: var(--gray03);
+ line-height: 1.5;
+}
+
+.inquiryInfoList .noInquiry {
+ margin-top: 10px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ line-height: 1.6;
+ color: var(--gray01);
+}
+
+.inquiryInfoList .noInquiry::before {
+ content: '';
+ width: 196px;
+ height: 196px;
+ background: url(/public/images/noinquiry_img.svg)no-repeat center;
+ background-size: cover;
+}
+
+@media (max-width: 1199px) {
+
+ .inquiryInfoList .box .selectBox .view {
+ width: 102px;
+ }
+
+}
+
+/* 공통-버튼 */
+.productDetailBox .btnBox {
+ display: flex;
+ justify-content: center;
+}
+
+.productDetailBox .btnBox a {
+ position: relative;
+ padding: 11px 40px;
+ display: block;
+ border-radius: 50px;
+ background-color: var(--gray01);
+ overflow: hidden;
+}
+
+.productDetailBox .btnBox a span {
+ display: flex;
+ align-items: center;
+ gap: 0 8px;
+ position: relative;
+ font-size: 18px;
+ font-weight: 600;
+ color: var(--gray04);
+ line-height: 1.4;
+}
+
+.productDetailBox .btnBox a span::after {
+ content: '';
+ margin-top: 3px;
+ width: 20px;
+ height: 18px;
+ background: url(/public/images/return_icon.svg)no-repeat center;
+}
+
+.productDetailBox .btnBox a::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 0;
+ bottom: 0;
+ transition: all .3s;
+ background-color: var(--blue);
+ border-radius: 50px;
+
+}
+
+.productDetailBox .btnBox a:hover::before {
+ width: 100%;
+
+}
\ No newline at end of file
diff --git a/src/components/Pagination.jsx b/src/components/Pagination.jsx
new file mode 100644
index 000000000..ea2d991b0
--- /dev/null
+++ b/src/components/Pagination.jsx
@@ -0,0 +1,64 @@
+import { Link } from "react-router-dom";
+
+export default function Pagination({ currentPage, totalPage, setPage }) {
+
+ const pageNumber = [];
+ for (let i = 1; i <= totalPage; i++) {
+ pageNumber.push(i);
+ }
+
+ const btnRange = 5 //보여질 버튼의 개수
+ const currentSet = Math.ceil(currentPage / btnRange); //현재 버튼이 몇 번째 세트인 지 나타내는 수
+ const startPage = (currentSet - 1) * btnRange + 1; //현재 페이지에 보여지는 버튼의 첫번째 수
+ const endPage = Math.min(startPage + btnRange - 1, totalPage); // 현재 세트의 마지막 페이지 번호
+
+ // 이전 페이지로 이동
+ const handlePrev = () => {
+ if (currentPage > 1) {
+ setPage(currentPage - 1);
+ }
+ };
+
+ // 다음 페이지로 이동
+ const handleNext = () => {
+ if (currentPage < totalPage) {
+ setPage(currentPage + 1);
+ }
+ };
+
+ // 페이지 번호 클릭 시
+ const handlePageChange = (page) => {
+ setPage(page);
+ };
+
+ return (
+
+
+
+ )
+
+}
\ No newline at end of file
diff --git a/src/components/ProductBestList.jsx b/src/components/ProductBestList.jsx
new file mode 100644
index 000000000..8c3063e22
--- /dev/null
+++ b/src/components/ProductBestList.jsx
@@ -0,0 +1,72 @@
+import { useEffect, useState } from "react";
+import axios from "axios";
+import { Link } from "react-router-dom";
+
+export default function ProductBestList() {
+
+ const [bestItem, setBestItem] = useState([]);
+ const noImage = 'https://via.placeholder.com/222?text=No+Image';
+
+ useEffect(() => {
+
+ async function bestItems() {
+ try {
+ let allItems = [];
+ const totalPages = Math.ceil(175 / 10);
+
+ for (let page = 1; page <= totalPages; page++) {
+ const response = await axios.get(`https://panda-market-api.vercel.app/products?page=${page}&limit=10`);
+ allItems = allItems.concat(response.data.list);
+ }
+
+ const sortedItem = allItems.sort((a, b) => b.favoriteCount - a.favoriteCount);
+ const likeItem = sortedItem.slice(0, 4);
+
+ setBestItem(likeItem);
+
+ } catch (error) {
+ console.log(error);
+ }
+ };
+
+ bestItems()
+
+ }, [])
+
+
+ return (
+
+ )
+
+}
\ No newline at end of file
diff --git a/src/components/ProductComment.jsx b/src/components/ProductComment.jsx
new file mode 100644
index 000000000..6656945fd
--- /dev/null
+++ b/src/components/ProductComment.jsx
@@ -0,0 +1,86 @@
+import { useEffect, useState } from "react"
+import axios from "axios";
+
+export default function ProductComment({ productId }) {
+
+ const [ProductComment, setProductComment] = useState([]);
+ const [selectViewToggle, setSelectViewToggle] = useState('');
+ const limit = 10; // 페이지당 댓글 수 (기본값 예시)
+ //const [cursor, setCursor] = useState(null); 다음 페이지를 위한 커서
+
+ function selectToggle(commentId) {
+ setSelectViewToggle(selectViewToggle === commentId ? '' : commentId);
+ }
+
+ useEffect(() => {
+
+ async function ProductInquiryInfo() {
+ try {
+ const response = await axios.get(`https://panda-market-api.vercel.app/products/${productId}/comments`, {
+ params: {
+ limit: limit, // 페이지당 댓글 수
+ },
+ });
+ setProductComment(response.data.list);
+ } catch (error) {
+ console.error("error:", error);
+ }
+ }
+
+ ProductInquiryInfo();
+
+ }, [productId])
+
+ return (
+
+
+ {
+ ProductComment.length > 0 ? (
+ ProductComment.map((data, i) => {
+ return (
+
+
+ {data.content}
+
+
+
+
+
+
+
+ {data.writer.nickname}
+
+
+ {new Date(data.updatedAt).toLocaleDateString()}
+
+
+
+
+
+ {
+ selectViewToggle === data.id && (
+
+
+
+
+ )
+ }
+
+
+ )
+ })
+ ) : (
+
+ 아직 문의가 없어요
+
+ )
+ }
+
+
+ )
+
+}
\ No newline at end of file
diff --git a/src/components/ProductDetail.jsx b/src/components/ProductDetail.jsx
new file mode 100644
index 000000000..473d7dab0
--- /dev/null
+++ b/src/components/ProductDetail.jsx
@@ -0,0 +1,75 @@
+
+export default function ProductDetail({ productDetail }) {
+
+ if (!productDetail) {
+ return 상품을 찾을 수 없습니다.
;
+ }
+
+ return (
+
+
+
+
+
+
+
+ {productDetail.name}
+
+
+ {productDetail.price}원
+
+
+
+
+
+ 상품소개
+
+
+ {productDetail.description}
+
+
+
+
+
+ 상품태그
+
+
+
+ {
+ (productDetail.tags || []).map((tag, i) => {
+ return (
+
+ #{tag}
+
+ )
+ })
+ }
+
+
+
+
+
+
+
+
+
+
+ {productDetail.ownerNickname}
+
+
+ {new Date(productDetail.createdAt).toLocaleDateString()}
+
+
+
+
+
+ {productDetail.favoriteCount}
+
+
+
+
+
+
+ )
+
+}
\ No newline at end of file
diff --git a/src/components/ProductInquiry.jsx b/src/components/ProductInquiry.jsx
new file mode 100644
index 000000000..e36630e36
--- /dev/null
+++ b/src/components/ProductInquiry.jsx
@@ -0,0 +1,39 @@
+import { useState } from "react"
+import ProductComment from "./ProductComment";
+
+export default function ProductInquiry({ productId }) {
+
+ const disabled = useState('disabled');
+
+ return (
+
+ )
+
+}
\ No newline at end of file
diff --git a/src/components/ProductList.jsx b/src/components/ProductList.jsx
new file mode 100644
index 000000000..40fcde0fc
--- /dev/null
+++ b/src/components/ProductList.jsx
@@ -0,0 +1,44 @@
+import { Link } from "react-router-dom";
+
+export default function ProductList({ filterItems, sortedItems }) {
+
+ const noImage = 'https://via.placeholder.com/222?text=No+Image';
+
+ return (
+
+
+
+ )
+
+}
\ No newline at end of file
diff --git a/src/components/ProductSearch.jsx b/src/components/ProductSearch.jsx
new file mode 100644
index 000000000..9784366b9
--- /dev/null
+++ b/src/components/ProductSearch.jsx
@@ -0,0 +1,18 @@
+export default function ProductSearch({ productSearch, getProductData }) {
+
+ return (
+
+
+
+
+ )
+
+}
\ No newline at end of file
diff --git a/src/components/page/ProductDetailPage.jsx b/src/components/page/ProductDetailPage.jsx
new file mode 100644
index 000000000..4020972d7
--- /dev/null
+++ b/src/components/page/ProductDetailPage.jsx
@@ -0,0 +1,60 @@
+import { Link, useParams } from "react-router-dom";
+import { useEffect, useState } from "react";
+import axios from "axios";
+import Footer from "../section/Footer";
+import Header from "../section/Header";
+import ProductDetail from "../ProductDetail";
+import ProductInquiry from "../ProductInquiry";
+
+export default function ProductDetailPage() {
+
+ const { productId } = useParams();
+ const [productDetail, setProductDetail] = useState([]); // 상세 페이지 데이터 상태
+
+ useEffect(() => {
+ // 상세 페이지 상품 데이터 요청
+ async function ProductDetailItems() {
+ try {
+ const response = await axios.get(`https://panda-market-api.vercel.app/products/${productId}`);
+ setProductDetail(response.data); // 해당 상품의 상세 정보 설정
+ } catch (error) {
+ console.error("error:", error);
+ }
+ }
+
+ ProductDetailItems();
+ }, [productId]); // 상품 ID가 변경될 때마다 상세 정보 요청
+
+ return (
+ <>
+
+
+
+
+
+
+
+ {productDetail ? (
+
+ ) : (
+
상품을 찾을 수 없습니다.
+ )}
+
+
+
+
+
+ 목록으로 돌아가기
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/components/page/ProductList.jsx b/src/components/page/ProductList.jsx
deleted file mode 100644
index 8ad62e072..000000000
--- a/src/components/page/ProductList.jsx
+++ /dev/null
@@ -1,258 +0,0 @@
-import { useEffect, useState } from "react";
-import Footer from "../section/Footer";
-import Header from "../section/Header";
-import axios from "axios";
-import { Link } from "react-router-dom";
-
-export default function ProductList() {
-
- const [item, setItem] = useState([]);
- const [bestItem, setBestItem] = useState([]);
- const [productSearch, setProductSearch] = useState('');
- const [filterItem, setFilterItem] = useState([]);
- const [selectView, setSelectView] = useState(false);
- const [sortOrder, setSortOrder] = useState(false);
- const [loading, setLoading] = useState(true);
- const noImage = 'https://via.placeholder.com/222?text=No+Image';
-
- useEffect(() => {
-
- async function allItem() {
- try {
- let allItems = [];
- const totalPages = Math.ceil(175 / 10);
-
- for (let page = 1; page <= totalPages; page++) {
- const response = await axios.get(`https://panda-market-api.vercel.app/products?page=${page}&limit=10`);
- allItems = allItems.concat(response.data.list);
- }
- setItem(allItems)
- setLoading(false);
- } catch (error) {
- console.log(error);
- setLoading(false);
- }
-
- };
-
- async function BestItem() {
- try {
- let allItems = [];
- const totalPages = Math.ceil(175 / 10);
-
- for (let page = 1; page <= totalPages; page++) {
- const response = await axios.get(`https://panda-market-api.vercel.app/products?page=${page}&limit=10`);
- allItems = allItems.concat(response.data.list);
- }
-
- const sortedItem = allItems.sort((a, b) => b.favoriteCount - a.favoriteCount);
- const likeItem = sortedItem.slice(0, 4);
-
- setBestItem(likeItem);
- setLoading(false);
- } catch (error) {
- console.log(error);
- setLoading(false);
- }
- };
-
- allItem();
- BestItem();
-
- }, [])
-
- useEffect(() => {
- const filter = item.filter((data) =>
- data.name.toLowerCase().includes(productSearch.toLowerCase())
- );
- setFilterItem(filter);
- }, [productSearch, item]);
-
- function sortItems(items) {
- if (!Array.isArray(items)) return [];
- if (sortOrder === 'latest') {
-
- return items.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)).slice(0, 10);
-
- } else if (sortOrder === 'like') {
-
- return items.sort((a, b) => b.favoriteCount - a.favoriteCount).slice(0, 10);
-
- }
- return items;
- }
-
- function getProductData(e) {
- setProductSearch(e.target.value);
- };
-
- function toggleSelect(e) {
- e.preventDefault();
- setSelectView(!selectView);
- };
-
- function handelSortChange(e, order) {
- e.preventDefault();
- setSortOrder(order);
- setSelectView(false);
- }
-
- return (
- <>
-
-
-
-
-
-
- {loading ? (
-
-
로딩중입니다.
-
- ) : (
-
- <>
-
-
-
-
-
-
- 전체 상품
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 상품 등록하기
-
-
-
-
-
- {sortOrder === 'latest' ? "최신순" : "좋아요순"}
-
-
- -
- handelSortChange(e, 'latest')}>
- 최신순
-
-
- -
- handelSortChange(e, 'like')}>
- 좋아요순
-
-
-
-
-
-
-
-
- >
- )
- }
-
-
-
-
-
-
-
-
- >
- )
-
-}
\ No newline at end of file
diff --git a/src/components/page/ProductListPage.jsx b/src/components/page/ProductListPage.jsx
new file mode 100644
index 000000000..173d9053f
--- /dev/null
+++ b/src/components/page/ProductListPage.jsx
@@ -0,0 +1,181 @@
+import { useEffect, useState } from "react";
+import axios from "axios";
+import Footer from "../section/Footer";
+import Header from "../section/Header";
+import ProductBestList from "../ProductBestList";
+import ProductList from "../ProductList";
+import Pagination from "../Pagination";
+import ProductSearch from "../ProductSearch";
+import { Link } from "react-router-dom";
+
+export default function ProductListPage() {
+
+ //상품 목록 상태 관리
+ const [items, setItems] = useState([]);
+
+ //최신순, 좋아요순 토글 상태 관리
+ const [selectView, setSelectView] = useState(false);
+
+ //최신순, 좋아요순 상태 관리
+ const [sortOrder, setSortOrder] = useState('최신순');
+
+ //최신순, 좋아요순 상태 변경 함수
+ function handleSortChange(newSortOrder) {
+ setSortOrder(newSortOrder);
+ setSelectView(!selectView);
+ }
+
+ //최신순, 좋아요순 토글 상태 변경 함수
+ function toggleSelect(e) {
+ e.preventDefault();
+ setSelectView(!selectView);
+ };
+
+ //최신순, 좋아요순 정렬 함수
+ function sortedItems() {
+ if (sortOrder === '최신순') {
+ return [...items].sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
+ } else if (sortOrder === '좋아요순') {
+ return [...items].sort((a, b) => b.favoriteCount - a.favoriteCount);
+ }
+ }
+
+ //페이지네이션 상태 관리
+ const [totalPage, setTotalPage] = useState(0); //전체페이지
+ const [page, setPage] = useState(1); //현재페이지
+ const itemsPerPage = 10; //한 페이지 상품 수
+
+ //검색 상태 관리
+ const [productSearch, setProductSearch] = useState('');
+
+ //검색 상태 변경 함수
+ function getProductData(e) {
+ setProductSearch(e.target.value);
+ };
+
+ //검색어 필터 변수
+ const filterItems = items.filter((items) =>
+ items.name.toLowerCase().includes(productSearch.toLowerCase())
+ );
+
+ useEffect(() => {
+
+ async function allItem() {
+
+ try {
+
+ const response = await axios.get('https://panda-market-api.vercel.app/products', {
+ params: {
+ page: page, // 현재 페이지에 해당하는 데이터 요청
+ limit: itemsPerPage, // 한 페이지당 데이터 수
+ }
+ });
+ setItems(response.data.list);
+ setTotalPage(Math.ceil(response.data.totalCount / itemsPerPage));
+
+ } catch (error) {
+
+ console.log('error :', error);
+
+ }
+ }
+
+ allItem();
+
+ }, [page])
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ 전체 상품
+
+
+
+
+
+
+ 상품 등록하기
+
+
+
+
+
+ {sortOrder}
+
+
+ -
+ handleSortChange('최신순')}
+ >
+ 최신순
+
+
+ -
+ handleSortChange('좋아요순')}
+ >
+ 좋아요순
+
+
+
+
+
+
+ {
+ filterItems.length === 0 ? (
+
+ 검색된 상품이 없습니다.
+
+ ) : (
+
+ )
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )
+
+}
\ No newline at end of file
diff --git a/src/components/page/ProductReg.jsx b/src/components/page/ProductRegPage.jsx
similarity index 99%
rename from src/components/page/ProductReg.jsx
rename to src/components/page/ProductRegPage.jsx
index f6e722924..eb1bf15b9 100644
--- a/src/components/page/ProductReg.jsx
+++ b/src/components/page/ProductRegPage.jsx
@@ -1,7 +1,7 @@
import { useEffect, useRef, useState } from "react";
import Header from "../section/Header";
-export default function ProductReg() {
+export default function ProductRegPage() {
//이미지 상태관리
const [productImg, setProductImg] = useState(null);