Skip to content
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b2e37bd
reset
hanseulhee Oct 10, 2023
6f8bbb0
Merge branch 'codeit-bootcamp-frontend:main' into main
hanseulhee Oct 10, 2023
e11e25f
fix: 머지 후 브랜치 삭제 github action 수정
hanseulhee Oct 10, 2023
212e864
env: workflows 폴더로 이동
hanseulhee Oct 10, 2023
4dc5dd0
Merge pull request #237 from hanseulhee/fix-github-actions
withyj-codeit Nov 6, 2023
efca996
스프린트 미션 작업
Apr 2, 2025
eaf83f9
코드리뷰 수정사항 반영
Apr 4, 2025
c07e11a
common.css 추가
Apr 4, 2025
1616a04
feat: 스프린트2 작업사항 반영
Apr 8, 2025
dffa4ab
fix: 이미지 경로 수정, 주석 삭제
Apr 10, 2025
26d0953
fix: div태그 section으로 변경
Apr 10, 2025
1b78b7a
fix: 스프린트2 수정사항 반영
Apr 19, 2025
8061b81
feat: 홈화면 반응형 작업
Apr 21, 2025
7f62fff
feat: 로그인 회원가입 화면 반응형 작업
Apr 21, 2025
61f616e
fix: 메타태그 링크 수정
Apr 21, 2025
a3078eb
fix: 스프린트2 수정사항 추가
Apr 21, 2025
1a489d0
fix: 스프린트3 css 파일 분리
Apr 26, 2025
440da35
feat: 스프린트4 미션 JS 추가
Apr 29, 2025
8f60c74
fix: 비밀번호 확인 유효성체크 수정
May 2, 2025
40c3112
fix: 유효성 검사 공통화, 이미지 최적화 반영
May 22, 2025
1f08632
Merge branch 'React-배수민' of https://github.com/codeit-bootcamp-fronte…
May 22, 2025
fb689b6
feat: 첫 커밋
May 22, 2025
2c97ce3
feat: 불필요한 파일 삭제
May 22, 2025
69e8454
feat: 라우터 및 기본화면 구성 추가
May 22, 2025
a7d3e44
feat: 네비게이션 컴포넌트 추가
May 22, 2025
06c5c80
fix: reset.css 충돌 해결
May 22, 2025
63ad325
feat: 상단 네비게이션 바 이미지 추가
May 22, 2025
ad48df5
feat: 네비게이션바 활성화 시 색상 변경
May 23, 2025
ec79f34
카드 컴포넌트 수정
suminbae96 May 24, 2025
fb1ead5
feat: 상품 페이지 반응형 레이아웃 추가
May 26, 2025
2dd2492
fix: 전체 상품 헤더 레이아웃 수정
May 26, 2025
9dd0261
feat: 디바이스 크기 변경 시 페이지 사이즈 변경 추가
May 26, 2025
f3e79fd
feat: 페이지네이션 추가
May 26, 2025
39c7747
fix: 오류 수정
May 26, 2025
594db4f
fix: css variable 적용
May 26, 2025
5de1c0b
fix: 상품 페이지 css 수정
May 26, 2025
d6eea78
fix: 빌드 시 의존성 배열에 따른 오류 수정
May 26, 2025
8b8d521
feat: 상품 등록 페이지 빈화면 추가
May 26, 2025
4ae4164
fix: 카드 컴포넌트 이미지 에러처리 추가
May 27, 2025
84b64b4
fix: 정렬 순서 변경 시 페이지 1로 초기화
May 27, 2025
92a7001
fix: useEffect 분리 및 handleResize 수정
May 27, 2025
c111d87
fix: 검색어 키워드 오류 수정
May 27, 2025
7330acd
feat: 스프린트 미션5 수정사항 반영, 페이지네이션 훅 분리
Jun 18, 2025
c702f43
fix: 하트 아이콘 컴포넌트 추가/
Jun 18, 2025
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.eslint.config.js
.prettierrc.js
73 changes: 73 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^7.6.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
Expand All @@ -34,5 +35,8 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"eslint-config-prettier": "^10.1.5"
}
}
26 changes: 8 additions & 18 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
import logo from './logo.svg';
import './App.css';
import { Outlet } from "react-router-dom";
import Nav from "./components/Nav";

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>
<>
<Nav />
<div>
<Outlet />
</div>
</>
);
}

Expand Down
24 changes: 24 additions & 0 deletions src/Main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { BrowserRouter, Routes, Route } from "react-router-dom";
import App from "./App";
import "./css/base/reset.css";
import "./css/base/variables.css";
import "./css/base/common.css";
import ItemsPage from "./pages/ItemsPage";
import ItemRegisterPage from "./pages/ItemRegisterPage";

function Main() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route path="items">
<Route index element={<ItemsPage />}></Route>
<Route path="addItem" element={<ItemRegisterPage />}></Route>
</Route>
</Route>
</Routes>
</BrowserRouter>
);
}

export default Main;
30 changes: 30 additions & 0 deletions src/api/Items.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const BASEURL = "https://panda-market-api.vercel.app";

export async function getFavoriteItems({
page = 1,
pageSize = 4,
orderBy = "favorite",
}) {
const query = `page=${page}&pageSize=${pageSize}&orderBy=${orderBy}`;
Copy link
Collaborator

Choose a reason for hiding this comment

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

URLSearchParams 객체 사용해서 여러개의 파라미터를 관리해보는 방법으로 바꾸면, 쿼리 스트링의 파싱, 조작, 인코딩 등의 작업을 간편하게 처리할 수 있으면서도 실수를 줄일 수 있겠죠? :)

아래 아티클 참고해보세요!
참고

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

URLSearchParams 사용하는 방식으로 변경하였습니다!

const response = await fetch(`${BASEURL}/products?${query}`);
if (!response.ok) {
throw new Error("상품 목록을 불러오는데 오류가 발생했습니다");
}
const body = await response.json();
return body;
}

export async function getAllItems({
page = 1,
pageSize = 10,
orderBy = "recent",
keyword = "",
}) {
const query = `page=${page}&pageSize=${pageSize}&orderBy=${orderBy}&keyword=${keyword}`;
const response = await fetch(`${BASEURL}/products?${query}`);
if (!response.ok) {
throw new Error("상품 목록을 불러오는데 오류가 발생했습니다");
}
const body = await response.json();
return body;
}
17 changes: 17 additions & 0 deletions src/components/Button.js
Copy link
Collaborator

Choose a reason for hiding this comment

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

굳굳! 스타일링에 관련된 props를 늘리지않고 type을 받는 방식으로 구조화하셨네요. 이렇게되면 명확한 props만 남게되어 유지보수에도 좋고 파일을 쉽게 확장하는것도 가능해지겠죠?

왜 props의 종류가 많아지는게 좋지 않은지는 아래 아티클 참고해보세요!

참고

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

확장성 고려해서 컴포넌트를 만들어야 겠다는 생각이 드네요

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Link } from "react-router-dom";
import "../css/components/Button.css";

function Button({ className, type, to }) {
const btnStyleClass = {
register: "btn-register",
small: "btn-small",
large: "btn-large",
};
return (
<button className={`btn ${btnStyleClass[type]} ${className}`}>
<Link to={to}>상품 등록하기</Link>
</button>
);
}

export default Button;
31 changes: 31 additions & 0 deletions src/components/Card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import "../css/components/Card.css";
import heartIcon from "../img/heart.svg";
import defaultImg from "../img/img_default.svg";
function Card({ data }) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

import 구문과 컴포넌트 시작 사이에 공백 한칸 넣어주세요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

넵 추가하였습니다!

return (
<div>
<img
className="card__image"
alt="중고상품 이미지"
src={data.images[0] ?? defaultImg}
onError={(e) => {
e.target.onError = null;
e.target.src = defaultImg;
}}
/>
<div className="card__info">
<div className="card__info__title">{data.name}</div>
<div className="card__info__price">
{data.price.toLocaleString("ko-KR")}원
</div>

<div className="card__icon__group">
<img src={heartIcon} alt="하트 아이콘" className="heart__icon" />
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 하트 아이콘 클릭하면 사용자에게 피드백을 주기 위해 색이 채워지거나 border 두께가 두꺼워지거나 색깔이 진해지지않을까요? ㅎㅎ 이런 인터렉션을 구현하려면 svg를 컴포넌트화하여 props를 보내주는게 좋을것같아요.

예시를 들어드릴게요!

Copy link
Collaborator

Choose a reason for hiding this comment

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

  • HeartIcon 컴포넌트 만들기 (예시)
import React from 'react';

function HeartIcon({ isActive = false }) {
  return (
    <svg
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill={isActive ? "#FF0000" : "none"}
      stroke={isActive ? "#FF0000" : "#000000"}
      strokeWidth="2"
      strokeLinecap="round"
      strokeLinejoin="round"
    >
      <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
    </svg>
  );
}

export default HeartIcon;
  • 사용할때
    <HeartIcon isActive={} />

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

하트 아이콘 컴포넌트로 변경하였습니다!

<span className="heart__icon__count">{data.favoriteCount}</span>
</div>
</div>
</div>
);
}

export default Card;
40 changes: 40 additions & 0 deletions src/components/Dropdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import "../css/components/Dropdown.css";
import sortIcon from "../img/sort.svg";
import arrowDownIcon from "../img/arrow_down.svg";

function Dropdown({
className,
onClickDropdown,
onClickDropdownItem,
dropdownList,
showDropdown,
value,
}) {
return (
<div
className={`dropdown__container ${className}`}
onClick={onClickDropdown}
>
<div className="dropdown__button">
<span className="dropdown__text">{value.name}</span>
<img
src={arrowDownIcon}
alt="드롭다운 아이콘"
className="dropdown__button__icon"
/>
<img src={sortIcon} alt="분류아이콘" className="dropdown__sort__icon" />
</div>
{showDropdown && (
<ul className="dropdown__list">
{dropdownList?.map((item) => (
<li key={item.value} onClick={() => onClickDropdownItem(item)}>
{item.name}
</li>
))}
Copy link
Collaborator

Choose a reason for hiding this comment

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

key는 형제 컴포넌트들 사이에서 식별자 역할을 하기때문에, key에 쓰인 값에 중복된 값이 있을 시 비교 알고리즘이 제대로 작동하지않아 렌더링 최적화가 안디고, 상태 및 이벤트 바인딩또한 의도치 않은 사이드 이펙트를 발생시킬수있어요. item.value가 절대 중복되지않는 값이 맞는지 확인해보고, 다른 값을 사용하거나 방어 코드를 써줄까요? :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

key-index 형식으로 방어코드로 변경하였습니다!

</ul>
)}
</div>
);
}

export default Dropdown;
55 changes: 55 additions & 0 deletions src/components/Nav.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Link, NavLink } from "react-router-dom";
import textLogoIcon from "../img/logo_text.jpg";
import logoIcon from "../img/logo.svg";
import userIcon from "../img/user.svg";
import "../css/components/Nav.css";

function getLinkStyle({ isActive }) {
return {
color: isActive ? `var(--color-primary-100)` : undefined,
};
}

function Nav() {
return (
<header className="header">
<div className="header__content">
<div className="header__content__navigation__group">
<Link to="/">
<img
src={textLogoIcon}
srcSet={`${textLogoIcon} 103w, ${logoIcon} 153w`}
sizes={"(min-width: 768px) 153px, 103px"}
alt="판다마켓 로고"
className="header__content__logo"
/>
</Link>
<ul>
<li>
<NavLink to="board" style={getLinkStyle}>
자유게시판
</NavLink>
</li>

<li>
<NavLink to="items" style={getLinkStyle}>
중고마켓
</NavLink>
Copy link
Collaborator

Choose a reason for hiding this comment

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

이정도는 함수를 따로 만들 필요없이 인라인으로 작성해주셔도 무방할것같아요.
만약 조건이 더 추가되는것을 고려하거나, 보다 일관된 방식으로 클래스이름 조합을 관리하고싶다면 라이브러리의 도움을 받아보는건 어떨까요?

https://www.npmjs.com/package/clsx

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

인라인으로 수정하였습니다.

</li>
</ul>
</div>
<div className="header__content__user">
<Link to="user">
<img
src={userIcon}
alt="사용자 아이콘"
className="header__content__user__icon"
/>
</Link>
</div>
</div>
</header>
);
}

export default Nav;
Loading