Skip to content

Conversation

@BaeZzi813
Copy link
Collaborator

@BaeZzi813 BaeZzi813 commented Aug 2, 2025

요구사항

  • Github에 PR(Pull Request)을 만들어서 미션을 제출합니다.
  • 피그마 디자인에 맞게 페이지를 만들어 주세요.
  • React를 사용합니다

기본

  • 중고마켓 페이지 주소는 “/items” 입니다.

  • 페이지 주소가 “/items” 일때 상단네비게이션바의 '중고마켓' 버튼의 색상은 “3692FF”입니다.

  • 상단 네비게이션 바는 이전 미션에서 구현한 랜딩 페이지와 동일한 스타일로 만들어 주세요.

  • 상품 데이터 정보는 https://panda-market-api.vercel.app/docs/#/ 에 명세된 GET 메소드 “/products” 를 사용해주세요.

  • 상품 등록하기' 버튼을 누르면 “/additem” 로 이동합니다. ( 빈 페이지 )

  • 전체 상품에서 드롭 다운으로 “최신 순” 또는 “좋아요 순”을 선택해서 정렬을 할 수 있습니다.

  • 중고마켓 반응형

  • 베스트 상품

    • Desktop: 4개
    • Tablet: 2개
    • Mobile: 1개
  • 전체 상품

    • Desktop: 10개
    • Tablet: 6개
    • Mobile: 4개

심화

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

주요 변경사항

스크린샷

localhost_5173_items (1) localhost_5173_items localhost_5173_items(2)

멘토에게

  • 전체상품의 orderBy 에 따른 최신순 좋아요순을 selectoption을 사용 하였는데 반응형 모바일 사이즈에서 이미지 변경이 궁금합니다.
  • 셀프 코드 리뷰를 통해 질문 이어가겠습니다.

@BaeZzi813 BaeZzi813 added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label Aug 2, 2025
@BaeZzi813 BaeZzi813 requested a review from kiJu2 August 2, 2025 03:20
Comment on lines +40 to +64
{isMobile ? (
<div className="total-products-container">
<div className="products-title-mobile">
<h1 className="products-title">전체 상품</h1>
<Link to="/additem" className="add-item-button">
상품 등록하기
</Link>
</div>
<div className="total-products-content">
<div className="total-products-content-input">
<img src={searchIcon} alt="검색 아이콘" />
<input
type="text"
placeholder="검색할 상품을 입력해주세요"
value={keyword}
onChange={onKeywordChange}
/>
</div>
<select onChange={onOrderChange} value={orderBy}>
<option value="recent">최신순</option>
<option value="favorite">좋아요순</option>
</select>
</div>
</div>
) : null}
Copy link
Collaborator Author

@BaeZzi813 BaeZzi813 Aug 2, 2025

Choose a reason for hiding this comment

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

질문에 올린 select 이미지 설정법에 관한 useMediaQuery 로 모바일 사이즈일 때 구조 변경 코드줄 / 반응형 CSS 는 './ItemsPage.css'

Copy link
Collaborator

Choose a reason for hiding this comment

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

전체상품의 orderBy 에 따른 최신순 좋아요순을 select와 option을 사용 하였는데 반응형 모바일 사이즈에서 이미지 변경이 궁금합니다.

답변을 드리고 싶은데 잘 이해가 안되어서요 🥲
혹시 "select, option props를 전달하여 구성하였다."까지는 이해했는데요. 혹시 select 이미지 설정법이란게 어떤걸 의미하는걸까요 ?

DM 통해서 답변 주시거나 줌 미팅을 통해서도 답변드릴 수 있습니다 !

@kiJu2
Copy link
Collaborator

kiJu2 commented Aug 4, 2025

스프리트 미션 하시느라 수고 많으셨어요.
재영님의 학습에 도움 되실 수 있게 꼼꼼히 리뷰 하도록 해보겠습니다. 😊

Comment on lines +1 to +6
export async function getProducts({
page = 1,
pageSize = 10,
orderBy = "recent",
keyword = "",
}) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

굿굿! 훌륭합니다 👍

백엔드 요구사항대로 파라메터를 잘 설계하셨네요 👍👍

orderBy = "recent",
keyword = "",
}) {
const params = new URLSearchParams({ page, pageSize, orderBy, keyword });
Copy link
Collaborator

Choose a reason for hiding this comment

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

크으 ~ 자바스크립트 임베드 객체인 URLSearchParams까지 제대로군요 ! 👍

URLSearchParams: URLSearchParams 인터페이스는 URL의 쿼리 문자열을 대상으로 작업할 수 있는 유틸리티 메서드를 정의합니다.

Comment on lines +8 to +10
const response = await fetch(
`https://panda-market-api.vercel.app/products?${params}`
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

axios를 사용해보는건 어떨까요?(제안/선택)

fetch 모듈을 잘 만든다는 것은 어렵습니다. 다음 사항들을 고려해볼 수 있어요:

  1. 만약 get이 아닌 메써드(post, patch, delete 등)일 경우는 어떻게 처리할 수 있을까요?
  2. querybody가 필요할 때는 어떻게 처리 할 수 있을까요?
  3. 로그인 인가를 위한 토큰을 request 전에 자동으로 삽입할 수는 없을까요? (인증/인가를 자동으로 할 수 없을까요?)
  4. 처음 한 번에 Base URL을 지정할 수는 없을까요?
    1. Base URL을 사용하다가 타 Domain에 보내야 될 때는 어떻게 할 수 있을까요?
      이 모든 요구사항들을 '잘 만든다는 것'은 어려워요. 따라서 이 모든걸 만들어진 fetch 모듈을 사용해보고 후에 fetch모듈을 만들어 보는 것도 좋은 학습 방법이 될 수 있어요.

axios 시작하기

Copy link
Collaborator

Choose a reason for hiding this comment

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

어떻게 세팅하면 될까? 🤔

instance를 만들어서 export를 하고 사용해보는 것 정도로 시도해보면 좋을 것 같아요. axios-instance 파일을 만들어서 instance를 생성하고 export한 후 사용해보는건 어떨까요?
다음과 같이 만들어볼 수 있어요:

const baseURL = process.env.NEXT_PUBLIC_LINKBRARY_BaseURL;

const instance = axios.create({
  baseURL: baseURL,
  headers: {
    'Content-Type': 'application/json',
  },
});

export default instance

axios instance

인가에 필요한 accessTokenlocalStorage가 있다면 axios의 인터셉터를 활용할 수 있습니다 !

인터셉터는 혼자 해결해보시는 것을 권장드립니다. 혹시 모르시겠으면 다음 위클리 미션에 질문해주세요. 😊

사용 방법 🚀

사용 방법은 정말 간단해요. 다음과 같이 사용할 수 있습니다:

instance.get(`/user/${userId}`)

딱 보니. 마이그레이션도 정말 쉽게 할 수 있겠죠? 😊

axios API

}) {
const params = new URLSearchParams({ page, pageSize, orderBy, keyword });
const response = await fetch(
`https://panda-market-api.vercel.app/products?${params}`
Copy link
Collaborator

Choose a reason for hiding this comment

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

https://panda-market-api.vercel.app는 앞으로도 계속 사용되겠군요 !

이 때 환경 변수로 지정해두시면 편리합니다 !

환경 변수(Environment Variable): process.env에 내장되며 앱이 실행될 때 적용할 수 있는 값입니다!

다음과 같이 적용할 수 있습니다:

// .env.development
REACT_APP_BASE_URL="http://localhost:3000"

// .env.production
REACT_APP_BASE_URL="http://myapi.com"

// 사용시
<a href={`${process.env.REACT_APP_BASE_URL}/myroute`}>URL</a>

throw new Error("상품을 불러오는데 실패했습니다");
}
const data = await response.json();
return [data.list, data.totalCount];
Copy link
Collaborator

Choose a reason for hiding this comment

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

배열 형태로 반환할 필요가 없어보이는군요 ! 🤔

Suggested change
return [data.list, data.totalCount];
return data;

배열로 반환하게 되면 반환되는 값이 어떤 값인지 유추하기 어려울 것으로 보입니다. 😊
이럴 때는 그냥 백엔드로부터 받은 데이터 그대로 반환해도 무방해보입니다 👍

alt={product.name}
/>
<h3 className="product-name">{product.name}</h3>
<p className="product-price">{product.price.toLocaleString()}원</p>
Copy link
Collaborator

Choose a reason for hiding this comment

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

<p> 태그는 단락을 나타냅니다 !

HTML <p> 요소는 하나의 문단을 나타냅니다. 시각적인 매체에서, 문단은 보통 인접 블록과의 여백과 첫 줄의 들여쓰기로 구분하지만, HTML에서 문단은 이미지나 입력 폼 등 서로 관련있는 콘텐츠 무엇이나 될 수 있습니다.

예를 들어 다음과 같은 값이 <p>태그에 적절할 수 있어요 😊:

Geckos are a group of usually small, usually nocturnal lizards. They are found on every continent except Antarctica.

혹은:

헌법재판소는 법관의 자격을 가진 9인의 재판관으로 구성하며, 재판관은 대통령이 임명한다. 선거에 있어서 최고득표자가 2인 이상인 때에는 국회의 재적의원 과반수가 출석한 공개회의에서 다수표를 얻은 자를 당선자로 한다.

MDN <p>


price는 어떤 태그가 적합할까?

Comment on lines +37 to +54
useEffect(() => {
async function fetchTotalProducts() {
try {
const [list, count] = await getProducts({
page,
pageSize: totalCountPerPage,
orderBy,
keyword,
});
setTotalProducts(list);
setTotalCount(count);
} catch (error) {
setError(error.message);
}
}

fetchTotalProducts();
}, [page, orderBy, keyword, totalCountPerPage]);
Copy link
Collaborator

Choose a reason for hiding this comment

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

굿굿. useEffect에 적절한 의존성이네요.

fetch 함수도 useEffect 안에서 선언함으로써 리렌더링 시 불필요한 재선언과 스코프도 명확하여 가독성도 챙길 수 있는 코드로 보입니다 👍👍

Comment on lines +3 to +8
export const useResponsive = () => {
const isTablet = useMediaQuery({ maxWidth: 1199 });
const isMobile = useMediaQuery({ maxWidth: 767 });

return isMobile ? [1, 4] : isTablet ? [2, 6] : [4, 10];
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

굿굿! 커스텀 훅을 만들어 재사용하셨군요 👍👍

@kiJu2
Copy link
Collaborator

kiJu2 commented Aug 4, 2025

수고하셨습니다 재영님~!
API 호출 로직이나 커스텀 훅 구성 등 훌륭하게 잘 구성하셨어요! 특히 파라미터 설계나 의존성 설정에 대한 이해도가 돋보였어요 👏
axios도 한 번 고려해보시고 시맨틱 마크업처럼 실무에서 자주 마주치는 요소도 더 익히시면 훨씬 탄탄한 프론트엔드 개발자로 성장하실 거예요! 😊

충분히 잘하시고 계십니다. 👍👍

질문에 대해 답변드리지 못해서 염려가 되는데요 ! 궁금하신 사항은 DM 주시면 조속히 답변드리도록 하겠습니다 😊

@kiJu2 kiJu2 merged commit b235cb8 into codeit-bootcamp-frontend:React-양재영 Aug 4, 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.

2 participants