Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[진성진] Sprint 5 #64

Conversation

cozy-ito
Copy link
Collaborator

@cozy-ito cozy-ito commented Feb 24, 2025

요구사항

기본

중고마켓

  • 중고마켓 페이지 주소는 /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개 보이기

체크리스트 [심화]

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

주요 변경사항

  • 상품 페이지 구현 (반응형 포함)
  • 페이지네이션 구현

스크린샷

  • 데스크탑
    image

  • 태블릿
    image

  • 모바일
    image

멘토에게

  1. 참고ItemsPage 마켓 페이지의 로직들이 길게 나열되는 것이 지저분해 보여서 하나의 useItemPageState 커스텀 훅 통으로 로직을 분리해서 필요한 변수나 메서드를 반환하는 방식으로 처리를 했는데 더 나은 방향이 있을까요? 🥲
  2. 참고(ItemsPage)전체적으로 CSS module 을 활용해서 스타일링을 진행했습니다. 그런데 ItemPage 컴포넌트에서 개별적으로 스타일을 적용하려다 보니 *클래스명에도 의미를 구체적으로 부여하는 것이 나은 지, 아니면 어차피 컴포넌트 별로 스코프가 나뉘어지니 간단하게 클래스명을 부여해도 될 지가 고민이 됩니다. 전자로 적용하자니 스타일을 적용하려는 요소들에 각각 클래스명을 부여하자니 클래스명을 매번 분리해서 작성하기도 번거롭고, 후자로 작성을 하자니 너무 간단한 클래스명으로 표현하려면 동일 컴포넌트 내 중복의 가능성이 높아져서요! 🤔 (ex: all_items_container <-> container, search_button <-> button)
  3. 참고(Select)Select 컴포넌트에서 Children API를 활용하여 children prop으로 전달되는 요소들을 Select 박스의 요소들로 나타나도록 한번 구현해보았습니다. 그런데 이렇게 구현하니 Select 컴포넌트의 children props로 어떤 값을 전달해야 할 지 한 번에 알기 어렵고, 컴포넌트 내부 로직을 살펴보아야 알 수 있는 것 같아서 구현 후에나 고민이 되었습니다. 합성 컴포넌트 패턴을 사용하는 게 더 나은 방식일지 궁금합니다.
  4. 참고(Pagination)Pagination 컴포넌트를 구현하다보니 내부 로직이 많이 복잡하게 구현이 된 것 같은 생각이 들었습니다 🥹. offset 방식으로 페이지네이션 구현 시, 본래 이렇게 복잡하게 구현되는 것이 맞는 지 궁금합니다.
  5. 참고(App)MainLayout에서 header 컴포넌트를 주입받아 렌더링하도록 구현을 해보았습니다. 메인 페이지(/, Header)와 상품 페이지(/items, NavHeader)간의 헤더 형태의 차이가 있어서 주입 받도록 구현했는데 괜찮은 방식인지, 더 나은 방식도 있을 지 궁금합니다.
  6. 참고(useForm), 참고(Validator)로그인/회원가입 페이지의 유효성 검사를 좀 더 재사용 가능하도록 만들기 위해서 react-hook-form을 사용해본 경험을 토대로 모방해서 Validator 클래스와 useForm 커스텀 훅을 구현해 적용해봤습니다! 하지만 페이지 최상단에서 커스텀 훅을 적용하다보니 유효성 검사를 위한 이벤트 발생 시에 페이지 전체가 리렌더링 되고 있습니다. useForm을 이용하면서 유효성 검사는 실제 필요한 부분만 리렌더링 되려면 어떻게 개선해 볼 수 있을까요? 🥲
  • 셀프 코드 리뷰를 통해 질문 이어가겠습니다.

hanseulhee and others added 30 commits October 10, 2023 14:15
[Fix] delete merged branch github action
cozy-ito and others added 21 commits January 25, 2025 11:42
@cozy-ito cozy-ito changed the title React 진성진 sprint14 [진성진] Spring 5 Feb 24, 2025
@cozy-ito cozy-ito changed the title [진성진] Spring 5 [진성진] Sprint 5 Feb 24, 2025
@cozy-ito cozy-ito self-assigned this Feb 24, 2025
@cozy-ito cozy-ito changed the base branch from main to React-진성진 February 24, 2025 06:11
@cozy-ito cozy-ito added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label Feb 24, 2025
@cozy-ito cozy-ito marked this pull request as ready for review February 24, 2025 06:19
@cozy-ito cozy-ito requested a review from dongqui February 24, 2025 06:51
Copy link
Collaborator

@dongqui dongqui left a comment

Choose a reason for hiding this comment

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

성진님 이번 미션도 고생 많으셨습니다! 💯

기본적인 리엑트 개념 이해도 충분히 잘 하고 계신 거 같고 어떻게 하면 더 나은 코드를 작성할 지 고민하시는 모습이 너무 좋습니다 👍 👍 👍 여유가 되신다면 컴포넌트 재사용성에 대해 고민해보시면 더욱 좋을 거 같아요!

return (
<div className={styles.main_center}>
<article className={styles.best_items_container}>
<p className={styles.title}>베스트 상품</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 tag는 단락을 나타내므로 여기서는 맞지 않겠네요 :)

},
};

const useItemPageState = () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

벌써 커스텀 훅을 활용하시는군요! 👍 👍

?.sort((a, b) => b.favoriteCount - a.favoriteCount)
.slice(0, currentBestItemsAmount) ?? [];

const typingKeywordChangeHandler = debounce((e) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

debounce도 적용해 주셨네요! 😮

const sortBy = searchParams.get("sortBy") || "recent";
const keyword = searchParams.get("keyword") || "";

const bestItemList =
Copy link
Collaborator

Choose a reason for hiding this comment

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

정렬, 분류 등은 보통 서버를 활용합니다. 지금은 전체 상품에서 가장 인기 있는 제품이 아니라 현재 페이지에서 가장 인기 있는 상품을 렌더링하고 있습니다! 😢

};

const useItemPageState = () => {
const [searchParams, setSearchParams] = useSearchParams();
Copy link
Collaborator

@dongqui dongqui Feb 25, 2025

Choose a reason for hiding this comment

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

참고ItemsPage 마켓 페이지의 로직들이 길게 나열되는 것이 지저분해 보여서 하나의 useItemPageState 커스텀 훅 통으로 로직을 분리해서 필요한 변수나 메서드를 반환하는 방식으로 처리를 했는데 더 나은 방향이 있을까요? 🥲

-> 지금도 나쁘지는 않지만, 하나의 훅이 너무 많은 일을 하고 있습니다! 하나의 훅이 여러 책임을 가지면 코드가 길어지고 유지보수가 어려워지며, 재사용성이 떨어집니다.
실무에서 생길 수 있는 시나리오를 예로 들면, 베스트 상품에 다른 정렬 옵션이 생기면 어떨까요? 이 훅은 더 커지고 서로 의존되지 않는 로직들이 뒤섞이게 되고 읽기는 더 힘들어질 거에요. 베스트 상품 영역이 커져도 훅에 묶여있으니 컴포넌트로 분리하기도 힘들겠죠!
저라면 훅을 쪼갤 거 같습니다.

const [pageSize] = usePageSize({
    desktop: 10,
    tablet: 6,
    mobile: 4,
  });
const [keyword, setKeyword] = useKeyword('');
const { items, loading, error } = useItems(keyword, pageSize);

대충 이런 느낌일 거 같아요. (수도 코드니 참고만 해주세요 🤣 )

</div>
<BrowserRouter>
<Routes>
<Route element={<MainLayout header={<Header />} />}>
Copy link
Collaborator

Choose a reason for hiding this comment

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

참고(App)MainLayout에서 header 컴포넌트를 주입받아 렌더링하도록 구현을 해보았습니다. 메인 페이지(/, Header)와 상품 페이지(/items, NavHeader)간의 헤더 형태의 차이가 있어서 주입 받도록 구현했는데 괜찮은 방식인지, 더 나은 방식도 있을 지 궁금합니다.
-> 좋습니다~! 👍 👍 재사용성이나 유지보수를 잘 고려하신 거 같아요!

@@ -0,0 +1,86 @@
import { useCallback, useMemo, useState } from "react";

const useForm = ({ defaultValue = {}, resolver, mode = "onBlur" }) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

참고(useForm), 참고(Validator)로그인/회원가입 페이지의 유효성 검사를 좀 더 재사용 가능하도록 만들기 위해서 react-hook-form을 사용해본 경험을 토대로 모방해서 Validator 클래스와 useForm 커스텀 훅을 구현해 적용해봤습니다! 하지만 페이지 최상단에서 커스텀 훅을 적용하다보니 유효성 검사를 위한 이벤트 발생 시에 페이지 전체가 리렌더링 되고 있습니다. useForm을 이용하면서 유효성 검사는 실제 필요한 부분만 리렌더링 되려면 어떻게 개선해 볼 수 있을까요? 🥲
->
우선, 리렌더링이 반드시 나쁜 것은 아닙니다. 🙂 필요할 때는 당연히 렌더링이 이루어져야 하죠!
그래도 렌더링을 줄이고 싶다면 필드별 상태 관리나 컴포넌트 범위 축소를 고려하거나, uncontrolled 방식으로 접근해볼 수 있을 것 같습니다.
(controlled, uncontrolled component를 키워드로 찾아보시면 이해에 도움이 될 거예요!)

다만, 원하시는 것처럼 uncontrolled 방식으로 세밀한 제어를 동시에 구현하는 것은 다소 어려울 수 있습니다.
이 때문에 react-hook-form이 많은 사랑을 받는 이유이기도 합니다. 😊

<BrowserRouter>
<Routes>
<Route element={<MainLayout header={<Header />} />}>
<Route index path="/" element={<HomePage />} />
Copy link
Collaborator

Choose a reason for hiding this comment

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

basic sprint 작업도 다 가져오셨군요! 😮 💯

return { status: result.status, ...data };
} catch (error) {
console.error(error);
return { status: 520, list: [], totalCount: 0 };
Copy link
Collaborator

Choose a reason for hiding this comment

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

status code는 의미 있게 다루셔야 합니다~!
https://developer.mozilla.org/ko/docs/Web/HTTP/Status

또한 매번 try catch로 감싸면 오히려 에러 핸들링이 힘들어 질 수 있어요.
상단에서 ErrorBoundary를 쓰시거나, 아니면 실제로 에러 UI를 핸들링 할 수 있는 컴포넌트 단에서 try catch로 잡는 것도 좋습니다!

Copy link
Collaborator

Choose a reason for hiding this comment

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

오.. 깔끔하게 구조화 하셨네요! 👍

@dongqui dongqui merged commit e182b8f into codeit-bootcamp-frontend:React-진성진 Feb 25, 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