Skip to content

[신영미] sprint4#74

Open
youngmis wants to merge 4 commits intocodeit-sprint-fullstack:react-신영미from
youngmis:react-신영미-sprint4

Hidden character warning

The head ref may contain hidden characters: "react-\uc2e0\uc601\ubbf8-sprint4"
Open

[신영미] sprint4#74
youngmis wants to merge 4 commits intocodeit-sprint-fullstack:react-신영미from
youngmis:react-신영미-sprint4

Conversation

@youngmis
Copy link
Copy Markdown
Collaborator

@youngmis youngmis commented Mar 28, 2026

스프린트 미션4

📌 프로젝트 소개

리액트를 사용하여 페이지를 작성하라는 스프린트 요구사항을 반영하여 좋아요가 가장 많은 상품을 베스트 상품 섹션에, 전체 상품을 판매중인 상품 섹션에 나열한 페이지를 구성함
나열된 전체 상품은 정렬 기능을 통해 좋아요 수 혹은 등록 순으로 순서를 변경하여 볼 수 있음

🛠 사용 기술

  • HTML, CSS, React(jsx)

🛠 사용 협업툴

  • Git, Github

🚀 주요 기능

1. App.jsx

  • 레이아웃(Layout.jsx), 베스트 상품(BestList.jsx), 판매 중인 상품(ProductList.jsx)을 Components 폴더 내에 분리하여 컴포넌트로 생성하고, App.jsx에는 컴포넌트들을 불러와 합쳐지도록 함
  • 각각 페이지에 시멘틱 태그 적용하여 SEO를 고려함

2. Layout.jsx / App.css

  • 기존 판다 랜딩 페이지 내에서 만들었던 헤더와 푸터를 가져와서 대입함
  • 헤더와 푸터 사이 메인 부분에는 신규 작성할 섹션(베스트 상품,판매중 상품)이 대입될 수 있도록 변수 처리함
  • App.css에는 기존에 작성했던 css 내용을 기입함 (다른 jsx 파일은 인라인 스타일 적용)

3. BestList.jsx

  • Fetch로 서버 데이터 get을 명령하고, useEffect Hook을 사용하여 딱 한 번만 호출되도록 함
  • useState Hook을 사용하여 서버에서 좋아요가 많은 상품 4개를 새 배열에 저장하고 출력함(주소 파라미터 활용)
  • 그리드 레이아웃 속성을 사용하여 4개의 상품 카드를 가로로 균등하게 배치함
  • 정사각형 비율로 이미지를 불러와 깔끔하게 정렬되도록 설정함

4. ProductList.jsx

  • keyword(검색어), orderBy(정렬 상태), page(현재 페이지)를 useState Hook을 사용하여 관리함
  • useEffect 의존성 배열 칸에 page와 orderBy를 추가하여, 사용자가 페이지를 넘기거나 정렬 순서를 바꿀 때마다 조건에 맞는 fetchProducts 함수(상품 불러오기)가 실행되도록 구현함
  • 검색 시 결과 화면이 1페이지부터 로드되도록 onSubmit 이벤트에 setPage(1) 로직을 추가함
  • 그리드 레이아웃 속성을 사용하여 5개의 상품 카드를 가로로 균등하게 배치함
  • 페이지네이션을 구현하여 하단 숫자 버튼과 이전/다음(<, >) 버튼을 통해 page가 변경되도록 구현함
  • 현재 페이지 번호에 조건부 스타일링을 적용함

✔셀프 체크리스트

요구사항

  1. 기본 요구사항
    [공통]
  • Github에 스프린트 미션 PR을 만들어 주세요.
  • React를 사용해 진행합니다.

[중고마켓 페이지]

  • PC, Tablet, Mobile 디자인에 해당하는 중고마켓 페이지를 만들어 주세요.

  • 중고마켓 페이지 url path는 별도로 설정하지 않고, '/'에 보이도록 합니다.

  • 상단 네비게이션 바, 푸터는 랜딩 페이지와 동일한 스타일과 규칙으로 만들어주세요.

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

  • 상품 목록 페이지네이션 기능을 구현합니다.

  • 드롭 다운으로 "최신 순" 또는 "좋아요 순"을 선택해서 정렬을 구현하세요.

  • 상품 목록 검색 기능을 구현합니다.

  • 베스트 상품 데이터는 https://panda-market-api.vercel.app/docs/에 명세된 GET 메소드 "/products"의 정렬 기준 favorite을 사용해주세요.

  1. 심화 요구사항
  • 커스텀 hook을 만들어 필요한 곳에 활용해 보세요.
  • 중고 마켓의 카드 컴포넌트에 반응형에 따른 페이지 네이션 기능을 구현합니다.

@youngmis youngmis requested a review from greatelv March 28, 2026 08:33
@youngmis youngmis added 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. 최종제출 스프린트 미션 최종 제출 PR입니다. 코드리뷰 및 평가해주세요! labels Mar 28, 2026
Copy link
Copy Markdown
Collaborator

@greatelv greatelv left a comment

Choose a reason for hiding this comment

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

[P5] 전반적으로 기능들을 성실하게 잘 구현해 주셨습니다. 특히 상태관리를 통한 검색, 정렬을 ProductList내부에서 깔끔하게 구축한 점이 돋보입니다.
다만 인라인 스타일이 전반적으로 많이 사용되어 가독성을 저해할 수 있습니다. 차후 리팩토링 시에는 CSS(또는 모듈) 기반으로 로직과 스타일링을 분리하면 훨씬 유지보수성 높은 프로젝트가 될 것 같습니다!

(혹시 AI를 통한 바이브코딩 비중이 있었다면, 본 리뷰에서 언급된 엣지 케이스나 의존성 문제에 대해서는 직접 공식문서를 찾아가며 디버깅해 보시는 것을 권장합니다.)

<a href="/privacy">Privacy Policy</a>
<a href="/faq">FAQ</a>
</span>
<span className="sns-logo">
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P5] 부드러운 칭찬 👏

target="_blank" 속성을 사용할 때 보안을 고려하여 rel="noreferrer"를 함께 함께 적절하게 사용해 주셨네요. 좋은 습관입니다!

cursor: "pointer",
}}
>
자유게시판
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P2] 웹 표준 (시맨틱 태그 올바른 사용)

"자유게시판", "중고마켓" 메뉴에 <a> 태그를 사용하셨으나 href 속성이 누락되어 있습니다. 브라우저 접근성 측면에서 href가 없는 <a> 태그는 키보드 내비게이션(Tab) 등에서 제대로 동작하지 않을 수 있습니다. 동작을 처리하려면 <button> 요소를 사용하거나 임시 링크라도 href를 포함해 주는 것이 웹 표준에 부합합니다.

const [bestProducts, setBestProducts] = useState([]);

useEffect(() => {
fetch('https://panda-market-api.vercel.app/products?page=1&pageSize=4&orderBy=favorite')
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P4] 하드코딩 지양 및 추상화 권장

API URL이 컴포넌트 내부에 하드코딩되어 있습니다. 유지보수와 확장성을 고려하여 import.meta.env.VITE_API_URL과 같은 환경변수를 활용하거나, 별도의 API 유틸리티 함수(또는 커스텀 훅)로 로직을 분리해 보시는 것을 권장드립니다.


useEffect(() => {
fetch('https://panda-market-api.vercel.app/products?page=1&pageSize=4&orderBy=favorite')
.then((res) => res.json())
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P3] 예외 처리 로직 (방어적 프로그래밍)

현재 구조에서는 404나 500 에러가 발생해도 res.json()을 시도하다가 파싱 에러로 묻혀버리는 문제가 발생할 수 있습니다. res.ok를 체크하여 오류 상황을 명시적으로 던져주면 더욱 견고한 서비스가 될 것 같습니다.

.then((res) => {
  if (!res.ok) throw new Error("네트워크 응답이 올바르지 않습니다.");
  return res.json();
})

bestProducts.map((item) => (
<div key={item.id} style={{ cursor: 'pointer' }}>
<img
src={item.images && item.images[0] ? item.images[0] : '/이미지/default.png'}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P2] 파일명 컨벤션 (한글 경로 지양) 및 문법 간소화

/이미지/default.png와 같이 경로에 한글이 포함될 경우, 배포 환경이나 특정 OS에서 인코딩 문제로 엑스박스(이미지 깨짐)가 발생할 수 있는 잠재적 결함이 있습니다. 영문 경로(예: /images/default.png)로 변경하는 것을 강력히 추천합니다.
또한, 이 코드는 옵셔널 체이닝과 널 병합 연산자를 활용하여 item.images?.[0] ?? '/images/default.png' 처럼 간결하게 작성하실 수 있습니다.


const fetchProducts = () => {
fetch(
`https://panda-market-api.vercel.app/products?page=${page}&pageSize=10&orderBy=${orderBy}&keyword=${keyword}`,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P4] 코드 중복 (DRY 원칙 위배) 개선

BestList.jsx에서도 동일한 기본 API 경로가 반복되어 사용되고 있습니다. Axios를 통한 baseURL 설정이나 공통 fetch 래퍼 함수를 만들면 이런 중복을 우아하게 제거할 수 있어 구조가 한 단계 더 발전할 것 같습니다.


const handleSearch = (e) => {
e.preventDefault();
setPage(1);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P3] 사이드 이펙트(의존성) 및 데이터 플로우 일관성

현재 handleSubmit 이벤트에서 setPage(1)을 호출하고 바로 fetchProducts()를 직접 실행하고 있습니다. 만약 현재 page가 1이 아닐 경우 setPage(1)에 의해 렌더링 후 useEffect가 다시 트리거되면서 fetchProducts()이중 호출(Double Fetch) 될 우려가 있습니다.
검색 기능에 대한 의존성 배열 설계를 다시 고민해 보시고, 검색어 제출 시 사용할 스테이트(예: searchedKeyword)를 명시하여 useEffect 하나로 데이터 요청의 책임을 모아주시는 방식을 권장드립니다.

</h2>
<div style={{ display: "flex", gap: "12px" }}>
<form onSubmit={handleSearch}>
<input
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P4] 웹 접근성(a11y) 개선

해당 <input>에 연결된 <label> 태그가 누락되어 있습니다. 스크린 리더 사용자를 배려하기 위해, 시각적으로 감추더라도 id를 부여한 후 보이지 않는 <label>을 연결해 주시거나, aria-label 속성을 추가해 주시는 것이 좋습니다.

{products.map((item) => (
<div key={item.id}>
<img
src={item.images[0]}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P2] 결함 방지 (방어적 프로그래밍)

BestList에서는 이미지가 비어있는 경우에 대한 더미 이미지 처리를 꼼꼼하게 해주셨는데, 여기서는 item.images[0]을 바로 참조하고 있습니다. 만약 백엔드에서 images가 빈 배열([])로 오거나 프로퍼티가 누락된 채 전달된다면 에러가 발생하거나 엑스박스가 뜰 수 있습니다. BestList와 동일하게 엣지 케이스 로직을 추가해 주세요.

>
&lt;
</button>
{[1, 2, 3, 4, 5].map((num) => (
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P4] 페이징 처리 하드코딩

[1, 2, 3, 4, 5]를 배열로 고정(하드코딩)하셨습니다. 만약 데이터가 너무 적어서 전체 페이지가 2페이지밖에 없다면, 3~5번 버튼은 빈 화면을 호출하게 됩니다. 서버에서 응답해주는 총 데이터 갯수나 전체 페이지 수를 기반으로 동적으로 페이지 번호를 렌더링하는 것을 추천드립니다. 또한, 다음(Next) 버튼에도 비활성화(disabled) 조건이 필요합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. 최종제출 스프린트 미션 최종 제출 PR입니다. 코드리뷰 및 평가해주세요!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants