Skip to content

[박준현] sprint5 #216

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

Conversation

llmojoll
Copy link
Collaborator

@llmojoll llmojoll commented Jul 5, 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개 보이기

스크린샷

localhost_5173_items (1)
localhost_5173_items (4)
localhost_5173_items (2)

멘토에게

@llmojoll llmojoll added the 순한맛🐑 마음이 많이 여립니다.. label Jul 5, 2025
@llmojoll llmojoll changed the title React 박준현 sprint5 [박준현] sprint5 Jul 5, 2025
@llmojoll llmojoll changed the base branch from main to React-박준현 July 7, 2025 04:55
@addiescode-sj addiescode-sj self-requested a review July 7, 2025 05:44
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.

수고하셨습니다!
첫 리액트 미션인데 조금만 다듬어보시면 많이 좋아질것같아요 :)
코멘트 남겨드린거 참고해보시고, 리팩토링해보세요!

주요 리뷰 포인트

  • 불필요한 폴더(사용하지않는 폴더&파일) 제거하기
  • 포맷팅
  • App.jsx에서 data fetching 하지 않기
  • 컴포넌트 로직 순서 유의해서 작성하기
  • 재사용 가능한 로직을 커스텀 훅으로 분리하기
  • 하드코딩된 값을 가능한 상수로 바꿔주기
  • 컴포넌트 분리하기

Copy link
Collaborator

Choose a reason for hiding this comment

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

루트에서 또 vite-project라는 폴더를 만들어서 관리하시고계시는데, 보통은 여러 프로젝트의 종속성 공유를 위해 모노레포 세팅을 하지않는 이상 이런 세팅을 하지 않아서 불필요한 폴더를 정리해주시는게 좋을것같네요!

Copy link
Collaborator

Choose a reason for hiding this comment

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

전체적으로 포맷팅이 일관적이지않아서, 규칙을 지켜서 자동으로 포맷팅하는 도구를 사용해보시면 좋을것같네요.
기본적인 linter 검사 + 포맷팅 수행을 위해 아래 아티클 참고해보시고, 자동 저장 옵션과 함께 활용해보시면 좋을것같아요!

참고

Comment on lines 32 to 48
<Navbar />
<Routes>
<Route
path="/"
element={<PandaMarketPage />} // 메인 페이지
/>
<Route
path="/items"
element={
<Products allProducts={allProducts} bestProducts={bestProducts} />
}
/>
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
<Route path="/faq" element={<Faq />} />
<Route path="/additem" element={<AddItem />} />
</Routes>
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 컴포넌트는 이 리턴문에서 리턴되는 모든 컴포넌트를 렌더링할때 쓰입니다.
즉, 이 컴포넌트에서 data fetching을 시도하게되면 Login 페이지에서는 필요하지도 않은 자원이 요청되고, 초기 로딩이 지연되는 상황이 발생하겠죠?

따라서 해당 data fetching 로직은 필요한 컴포넌트에서만 (Products 컴포넌트에서) 직접 데이터를 가져오도록 옮기시는게 더 나은 구조를 위한 관심사 분리 원칙에서도, 성능을 위해서도 좋은 선택입니다.

state 관리 & data fetching 로직을 Products 컴포넌트에서 직접 하도록 옮겨봅시다!

@@ -0,0 +1,29 @@
const BASE_URL = "https://panda-market-api.vercel.app";
Copy link
Collaborator

Choose a reason for hiding this comment

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

api base url의 경우, 외부 노출에 민감한 정보여서 환경변수로 관리하는걸 추천드립니다!

참고

* @param {object} params - 쿼리 파라미터 (기본값: 빈 객체)
* @returns {Promise<array>} - API로부터 받아온 list 데이터 반환
*/
export async function fetchData(endpoint, params = {}) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

이 함수는 jsdoc도 쓰시고 재사용성을 잘 고려해서 작성하셨네요 👍 굳굳!

Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 파일의 핵심 로직은 스타일링이 아니라 컴포넌트를 구성하는 로직이기때문에,
스타일드 컴포넌트보다 컴포넌트 작성 로직이 항상 순서가 먼저 올수있도록 염두에 두고 작성해봅시다!

Comment on lines 81 to 91
useEffect(() => {
function updateImgCount() {
const width = window.innerWidth;
if (width >= 1200) setImgCount(10);
else if (width >= 768) setImgCount(6);
else setImgCount(4);
}
updateImgCount();
window.addEventListener("resize", updateImgCount);
return () => window.removeEventListener("resize", updateImgCount);
}, []);
Copy link
Collaborator

Choose a reason for hiding this comment

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

스크린 사이즈를 감지해 이미지 갯수를 업데이트하는 로직처럼 보이네요!
아래 예시와 같이 재사용 가능한 로직 (스크린 사이즈 감지)은 커스텀 훅으로 분리해 사용해주면 코드 중복을 줄일 수 있겠죠?

import { useEffect, useState } from "react";

export const useScreenSize = () => {
  const [isDesktop, setIsDesktop] = useState(false);
  const [isTablet, setIsTablet] = useState(false);
  const [isMobile, setIsMobile] = useState(true);

  useEffect(() => {
    function updateScreenSize() {
      const width = window.innerWidth;

      setIsDesktop(width >= 1200);
      setIsTablet(width >= 768 && width < 1200);
      setIsMobile(width < 768);
    }

    updateScreenSize();
    window.addEventListener("resize", updateScreenSize);
    return () => window.removeEventListener("resize", updateScreenSize);
  }, []);

  return { isDesktop, isTablet, isMobile };
};

그리고, 이 커스텀 훅을 사용해 <AllProducts /> 컴포넌트에서는 브레이크포인트별 이미지를 몇개씩 보여줄지만 상수로 정해주고

// 상수 정의
const IMG_COUNTS = {
  desktop: 10,
  tablet: 6,
  mobile: 4,
};

const { isDesktop, isTablet } = useScreenSize();

// 화면 크기에 따른 상품 개수 계산
const imgCount = isDesktop
  ? IMG_COUNTS.desktop
  : isTablet
  ? IMG_COUNTS.tablet
  : IMG_COUNTS.mobile;

이렇게 화면 크기에 따른 이미지 개수만 계산해주면 되겠죠? :)

useEffect(() => {
function updateImgCount() {
const width = window.innerWidth;
if (width >= 1200) setImgCount(4);
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 컴포넌트에서도 위 코멘트 참고하셔서 리팩토링해보세요!
그리고 항상 하드코딩하는 값보다는 상수를 활용할수있도록 신경써주시면 좋습니다 :)

Comment on lines +53 to +66
<div className="signup-password-box">
<h4>비밀번호</h4>
<input
type="password"
className="signup-password-input SignupInput"
required
/>
<h4
className="error-msg"
style={{ color: "red", display: "none", fontSize: "12px" }}
>
비밀번호를 입력해주세요.
</h4>
</div>
Copy link
Collaborator

Choose a reason for hiding this comment

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

일정한 구조로 반복되는 코드들이 보이죠~? 이런 코드들이 단위 기능을 가지고있기도 하고요 (인풋 필드).
이런 경우, 컴포넌트 분리를 해주시면 유지보수에 훨씬 좋겠죠?

@addiescode-sj addiescode-sj merged commit c9667c1 into codeit-bootcamp-frontend:React-박준현 Aug 6, 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