Skip to content
Merged
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
94 changes: 24 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
@@ -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] 페이지 네이션 기능을 구현합니다.
Binary file removed public/favicon.ico
Binary file not shown.
Binary file removed public/logo192.png
Binary file not shown.
Binary file removed public/logo512.png
Binary file not shown.
3 changes: 0 additions & 3 deletions public/robots.txt

This file was deleted.

38 changes: 0 additions & 38 deletions src/App.css

This file was deleted.

27 changes: 9 additions & 18 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -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 (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
<>
<Container>
<Header />
<Main />
</Container>
</>
);
}

Expand Down
8 changes: 0 additions & 8 deletions src/App.test.js

This file was deleted.

5 changes: 5 additions & 0 deletions src/components/Container/Container.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 100px;
}
7 changes: 7 additions & 0 deletions src/components/Container/Container.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import "./Container.css";

const Container = ({ children }) => {
return <div className="container">{children}</div>;
};

export default Container;
34 changes: 34 additions & 0 deletions src/components/Footer/Footer.css
Original file line number Diff line number Diff line change
@@ -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;
}
86 changes: 86 additions & 0 deletions src/components/Footer/Footer.jsx
Original file line number Diff line number Diff line change
@@ -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); // 현재 그룹에 해당하는 페이지만 저장
}
Comment on lines +5 to +50
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 컴포넌트는 Footer보단 Pagination에 가까워보이네요! 네이밍을 바꿔주세요. 그리고 UI를 재사용할뿐만 아니라 로직만 재사용할 일이 있다면 이런 로직들을 커스텀 훅 단위로 분리해주시면 도움이 되겠죠? :)


return (
<div className="footer">
{/*이전 페이지 버튼*/}
<button
className="footer__btn"
onClick={handlePrevGroup}
disabled={currentGroup === 0}
>
&lt;
</button>

{/* 페이지 숫자 버튼 */}
{pages.map((pageNum) => (
<button
key={pageNum}
onClick={() => handleClick(pageNum)}
className={`footer__btn ${pageNum === currentPage ? "active" : ""}`} //현재 페이지 조건부 클래스
>
{pageNum}
</button>
))}

{/* 다음 페이지 버튼 */}
<button
className="footer__btn"
onClick={handleNextGroup}
disabled={currentGroup === maxGroup}
>
&gt;
</button>
</div>
);
};

export default Footer;
38 changes: 38 additions & 0 deletions src/components/Header/Header.css
Original file line number Diff line number Diff line change
@@ -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;
}
42 changes: 42 additions & 0 deletions src/components/Header/Header.jsx
Original file line number Diff line number Diff line change
@@ -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";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 Header 컴포넌트에서 window.location.pathname을 직접 체크하고있는데, 이는 React의 선언적 방식과 맞지 않습니다.

React Router의 NavLink를 사용해서, 현재 경로와 일치할때 클래스이름이 바뀌게끔 개선해보면 어떨까요?
예시)

  • React Router를 사용해 라우팅 구조화한 App.jsx
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Header from "./components/Header/Header";
import Main from "./components/Main/Main";
import Container from "./components/Container/Container";

function App() {
  return (
    <Router>
      <Container>
        <Header />
        <Routes>
          <Route path="/" element={<Main />} />
          <Route path="/items" element={<Main />} />
          {/* 다른 라우트들도 추가 */}
        </Routes>
      </Container>
    </Router>
  );
}

export default App;
  • Header.jsx
import { NavLink } from 'react-router-dom';
import pandaMarket from "../../images/PandaMarket.png";
import UserLogo from "../../images/UserLogo.svg";
import "./Header.css";

const Header = () => {
  return (
    <div className="Header">
      <NavLink to="/items">
        <img
          className="Header__logo"
          src={pandaMarket}
          alt="판마다켓 홈페이지 로고"
        />
      </NavLink>
      <div className="Header__menu">
        <NavLink
          className={({ isActive }) => 
            `Header__menu__items ${isActive ? "active" : ""}`
          }
          to="/board"
        >
          자유게시판
        </NavLink>
        <NavLink
          className={({ isActive }) => 
            `Header__menu__items ${isActive ? "active" : ""}`
          }
          to="/items"
        >
          중고마켓
        </NavLink>
      </div>
      <NavLink to="/items">
        <img className="Header__user" src={UserLogo} alt="유저 아이콘" />
      </NavLink>
    </div>
  );
};

export default Header;

NavLink는 현재 경로와 일치할 때 isActive Prop이 true로 바뀌는데, 이를 활용해 active 클래스를 추가하면 될 것 같습니다.


return (
<div className="Header">
<a href="./items" target="_blank" rel="noopener noreferrer">
<img
className="Header__logo"
src={pandaMarket}
alt="판마다켓 홈페이지 로고"
/>
</a>
<div className="Header__menu">
<a
className="Header__menu__items"
href="/items"
target="_blank"
rel="noopener noreferrer"
>
자유게시판
</a>
<a
className={`Header__menu__items ${isMarketPage ? "active" : ""}`}
href="/items"
target="_blank"
rel="noopener noreferrer"
>
중고마켓
</a>
</div>
<a href="./items" target="_blank" rel="noopener noreferrer">
<img className="Header__user" src={UserLogo} alt="유저 아이콘" />
</a>
</div>
);
};

export default Header;
Empty file added src/components/Main/Main.css
Empty file.
Loading
Loading