diff --git a/README.md b/README.md index 58beeacc..225b1ae2 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,24 @@ -# Getting Started with Create React App - -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). - -## Available Scripts - -In the project directory, you can run: - -### `npm start` - -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in your browser. - -The page will reload when you make changes.\ -You may also see any lint errors in the console. - -### `npm test` - -Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. - -### `npm run build` - -Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. - -The build is minified and the filenames include the hashes.\ -Your app is ready to be deployed! - -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. - -### `npm run eject` - -**Note: this is a one-way operation. Once you `eject`, you can't go back!** - -If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. - -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. - -You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. - -## Learn More - -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). - -To learn React, check out the [React documentation](https://reactjs.org/). - -### Code Splitting - -This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) - -### Analyzing the Bundle Size - -This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) - -### Making a Progressive Web App - -This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) - -### Advanced Configuration - -This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) - -### Deployment - -This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) - -### `npm run build` fails to minify - -This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) +체크리스트 [기본] +중고마켓 + +[x] 중고마켓 페이지 주소는 “/items” 입니다. +[x] 페이지 주소가 “/items” 일때 상단네비게이션바의 '중고마켓' 버튼의 색상은 “3692FF”입니다. +[x] 상단 네비게이션 바는 이전 미션에서 구현한 랜딩 페이지와 동일한 스타일로 만들어 주세요. +[x] 상품 데이터 정보는 https://panda-market-api.vercel.app/docs/#/ 에 명세된 GET 메소드 “/products” 를 사용해주세요. +[x] '상품 등록하기' 버튼을 누르면 “/additem” 로 이동합니다. ( 빈 페이지 ) +[x] 전체 상품에서 드롭 다운으로 “최신 순” 또는 “좋아요 순”을 선택해서 정렬을 할 수 있습니다. + +중고마켓 반응형 + +[x] 베스트 상품 +Desktop : 4개 보이기 +Tablet : 2개 보이기 +Mobile : 1개 보이기 + +[x] 전체 상품 +Desktop : 12개 보이기 +Tablet : 6개 보이기 +Mobile : 4개 보이기 + +체크리스트 [심화] +[x] 페이지 네이션 기능을 구현합니다. diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index a11777cc..00000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/public/logo192.png b/public/logo192.png deleted file mode 100644 index fc44b0a3..00000000 Binary files a/public/logo192.png and /dev/null differ diff --git a/public/logo512.png b/public/logo512.png deleted file mode 100644 index a4e47a65..00000000 Binary files a/public/logo512.png and /dev/null differ diff --git a/public/robots.txt b/public/robots.txt deleted file mode 100644 index e9e57dc4..00000000 --- a/public/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * -Disallow: diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 74b5e053..00000000 --- a/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/src/App.js b/src/App.js index 37845757..3c01374f 100644 --- a/src/App.js +++ b/src/App.js @@ -1,24 +1,15 @@ -import logo from './logo.svg'; -import './App.css'; +import Header from "./components/Header/Header"; +import Main from "./components/Main/Main"; +import Container from "./components/Container/Container"; function App() { return ( -
-
- logo -

- Edit src/App.js and save to reload. -

- - Learn React - -
-
+ <> + +
+
+ + ); } diff --git a/src/App.test.js b/src/App.test.js deleted file mode 100644 index 1f03afee..00000000 --- a/src/App.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/src/components/Container/Container.css b/src/components/Container/Container.css new file mode 100644 index 00000000..b9d205a8 --- /dev/null +++ b/src/components/Container/Container.css @@ -0,0 +1,5 @@ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 100px; +} diff --git a/src/components/Container/Container.jsx b/src/components/Container/Container.jsx new file mode 100644 index 00000000..db997cc1 --- /dev/null +++ b/src/components/Container/Container.jsx @@ -0,0 +1,7 @@ +import "./Container.css"; + +const Container = ({ children }) => { + return
{children}
; +}; + +export default Container; diff --git a/src/components/Footer/Footer.css b/src/components/Footer/Footer.css new file mode 100644 index 00000000..1041b299 --- /dev/null +++ b/src/components/Footer/Footer.css @@ -0,0 +1,34 @@ +.footer { + display: flex; + justify-content: center; + align-items: center; + gap: 8px; + margin-top: 24px; + margin-bottom: 30px; +} + +.footer__btn { + width: 40px; + height: 40px; + border-radius: 50%; + border: 1px solid gray; + background-color: white; + color: #6b7280; + font-size: 20px; + cursor: pointer; +} + +.footer__btn:hover:not(:disabled) { + background-color: #f0f0f0; +} + +.footer__btn:disabled { + cursor: not-allowed; + opacity: 0.4; +} + +.active { + background-color: #2f80ed; + color: white; + border: none; +} diff --git a/src/components/Footer/Footer.jsx b/src/components/Footer/Footer.jsx new file mode 100644 index 00000000..4b7a6fa4 --- /dev/null +++ b/src/components/Footer/Footer.jsx @@ -0,0 +1,86 @@ +import { useEffect, useState } from "react"; +import "./Footer.css"; + +const Footer = ({ currentPage, setCurrentPage }) => { + const [totalPages, setTotalPages] = useState(1); + const pageSize = 10; // 표시할 상품 개수 + const maxVisible = 5; + + useEffect(() => { + const fetchTotalPages = async () => { + try { + const res = await fetch( + `https://panda-market-api.vercel.app/products?page=1&pageSize=${pageSize}&orderBy=recent` + ); + const data = await res.json(); + const count = data.totalCount || 1; //보호 처리 + setTotalPages(Math.ceil(count / pageSize)); + } catch (err) { + console.error("페이지 수 불러오기 실패:", err); + } + }; + + fetchTotalPages(); + }, []); + + const currentGroup = Math.floor((currentPage - 1) / maxVisible); + const maxGroup = Math.floor((totalPages - 1) / maxVisible); + const startPage = currentGroup * maxVisible + 1; + const endPage = Math.min(startPage + maxVisible - 1, totalPages); + + const handleClick = (page) => { + if (page >= 1 && page <= totalPages) { + setCurrentPage(page); + } + }; + + const handlePrevGroup = () => { + const prevStart = (currentGroup - 1) * maxVisible + 1; + if (currentGroup > 0) setCurrentPage(prevStart); + }; + + const handleNextGroup = () => { + const nextStart = (currentGroup + 1) * maxVisible + 1; + if (currentGroup < maxGroup) setCurrentPage(nextStart); + }; + + const pages = []; + for (let i = startPage; i <= endPage; i++) { + pages.push(i); // 현재 그룹에 해당하는 페이지만 저장 + } + + return ( +
+ {/*이전 페이지 버튼*/} + + + {/* 페이지 숫자 버튼 */} + {pages.map((pageNum) => ( + + ))} + + {/* 다음 페이지 버튼 */} + +
+ ); +}; + +export default Footer; diff --git a/src/components/Header/Header.css b/src/components/Header/Header.css new file mode 100644 index 00000000..7713a4f6 --- /dev/null +++ b/src/components/Header/Header.css @@ -0,0 +1,38 @@ +.Header { + display: flex; + align-items: center; + justify-content: space-between; +} + +.Header__menu .Header__menu__items.active { + color: #3692ff; + font-weight: bold; + background-color: transparent; +} + +.Header__logo { + width: 153px; + height: 51px; + display: flex; + justify-content: center; + align-items: center; +} + +.Header__menu__items { + color: #000000; + font-weight: 500; + text-decoration: none; +} + +.Header__menu { + display: flex; + align-items: center; + gap: 30px; + flex: 1; + margin: 0 50px; +} + +.Header__user { + width: 30px; + height: 30px; +} diff --git a/src/components/Header/Header.jsx b/src/components/Header/Header.jsx new file mode 100644 index 00000000..3027de77 --- /dev/null +++ b/src/components/Header/Header.jsx @@ -0,0 +1,42 @@ +import pandaMarket from "../../images/PandaMarket.png"; +import UserLogo from "../../images/UserLogo.svg"; +import "./Header.css"; + +const Header = () => { + const isMarketPage = window.location.pathname === "/items"; + + return ( +
+ + 판마다켓 홈페이지 로고 + + + + 유저 아이콘 + +
+ ); +}; + +export default Header; diff --git a/src/components/Main/Main.css b/src/components/Main/Main.css new file mode 100644 index 00000000..e69de29b diff --git a/src/components/Main/Main.jsx b/src/components/Main/Main.jsx new file mode 100644 index 00000000..1427e955 --- /dev/null +++ b/src/components/Main/Main.jsx @@ -0,0 +1,18 @@ +import { useState } from "react"; +import BestProducts from "./Products/BestProducts"; +import AllProducts from "./Products/AllProducts"; +import Footer from "../Footer/Footer"; + +const Main = () => { + const [currentPage, setCurrentPage] = useState(1); //페이지 번호 상태 관리 + + return ( + <> + + +