Skip to content

Conversation

@summerDev96
Copy link
Collaborator

요구사항

기본

중고마켓

  • 중고마켓 페이지 주소는 “/items” 입니다.
  • 페이지 주소가 “/items” 일때 상단네비게이션바의 '중고마켓' 버튼의 색상은 “3692FF”입니다.
  • 상단 네비게이션 바는 이전 미션에서 구현한 랜딩 페이지와 동일한 스타일로 만들어 주세요.
  • 상품 데이터 정보는 https://panda-market-api.vercel.app/docs/#/ 에 명세된 GET 메소드 “/products” 를 사용해주세요.
  • '상품 등록하기' 버튼을 누르면 “/additem” 로 이동합니다. ( 빈 페이지 )
  • 전체 상품에서 드롭 다운으로 “최신 순” 또는 “좋아요 순”을 선택해서 정렬을 할 수 있습니다.

중고마켓 반응형

  • 베스트 상품
    Desktop : 4개 보이기
    Tablet : 2개 보이기
    Mobile : 1개 보이기
  • 전체 상품
    Desktop : 12개 보이기
    Tablet : 6개 보이기
    Mobile : 4개 보이기

심화

  • 페이지 네이션 기능을 구현합니다.

주요 변경사항

  • 네비게이션, 카드, 드롭다운, 버튼, 검색, 페이징 컴포넌트 추가

배포 사이트 링크

https://sprint-misson5-suminbae.netlify.app/items

스크린샷

피씨버전

멘토에게

  • useEffect에서 API 호출하고, 화면 사이즈가 바뀔 때 이벤트 핸들러를 등록하였는데 이런 방식으로 useEffect를 사용하면 되는 것이 맞는지 모르겠습니다.
  • API 호출 시 엑박이 뜨는 이미지들이 있는데 따로 에러 발생 시 처리가 필요할까요?

hanseulhee and others added 30 commits October 10, 2023 14:15
…ithub-actions

[Fix] delete merged branch github action
@summerDev96 summerDev96 requested a review from addiescode-sj May 26, 2025 14:21
@summerDev96 summerDev96 added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label May 26, 2025
Copy link
Collaborator

@addiescode-sj addiescode-sj left a comment

Choose a reason for hiding this comment

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

수고하셨습니다!
첫 리액트 미션인데 어렵지않게 잘 해주셨네요 ㅎㅎ

주요 리뷰 포인트

  • URLSearchParams 사용하기
  • svg 컴포넌트화해서 사용하기
  • key 역할 이해하기
  • 유지보수를 고려한 개발

src/api/Items.js Outdated
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 사용하는 방식으로 변경하였습니다!

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.

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

Comment on lines 3 to 4
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.

넵 추가하였습니다!

</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.

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

Comment on lines 29 to 33
{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 형식으로 방어코드로 변경하였습니다!

Comment on lines 35 to 37
<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.

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

Comment on lines 119 to 133
const bestPageSize = devicePageSize[deviceType]["best"];

const allPageSize = devicePageSize[deviceType]["all"];

const onClickNextPage = () => {
setPaginationCurrentPage(paginationCurrentPage + 1);
};

const onClickPrevPage = () => {
setPaginationCurrentPage(paginationCurrentPage - 1);
};

const onClickPage = (page) => {
setPaginationCurrentPage(page);
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

아이템 목록을 보여주는 역할의 컴포넌트에 페이지네이션 로직까지 결합되어있으니 복잡해보여요. 해당 로직(페이지네이션 관련)은 페이지네이션을 사용하는 컴포넌트라면 꾸준히 재사용될거예요. 로직을 재사용하기위해 usePagination과 같은 커스텀 훅으로 분리해보면 어떨까요? :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

usePagination 훅으로 분리하였습니다! 분리했는데도 코드가 생각보다 많네요

@addiescode-sj
Copy link
Collaborator

addiescode-sj commented May 28, 2025

질문에 대한 답변

멘토에게

  • useEffect에서 API 호출하고, 화면 사이즈가 바뀔 때 이벤트 핸들러를 등록하였는데 이런 방식으로 useEffect를 사용하면 되는 것이 맞는지 모르겠습니다.

네, 지금은 useEffect를 사용해 API를 호출하는 방식으로 작성하시는게 맞습니다.
화면 사이즈가 바뀔때 새로운 요청 파라미터를 보내서 응답 받아오는게 맞다면, deps list에 변경되는 값을 포함해주시면 됩니다 :)

  • API 호출 시 엑박이 뜨는 이미지들이 있는데 따로 에러 발생 시 처리가 필요할까요?

에러가 난 상황을 그대로 사용자에게 보여주는것보단 따로 처리를 해주는게 낫겠죠?

@addiescode-sj addiescode-sj merged commit e293fdd into codeit-bootcamp-frontend:React-배수민 Jun 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants