[신영미] sprint4#74
Hidden character warning
Conversation
greatelv
left a comment
There was a problem hiding this comment.
[P5] 전반적으로 기능들을 성실하게 잘 구현해 주셨습니다. 특히 상태관리를 통한 검색, 정렬을 ProductList내부에서 깔끔하게 구축한 점이 돋보입니다.
다만 인라인 스타일이 전반적으로 많이 사용되어 가독성을 저해할 수 있습니다. 차후 리팩토링 시에는 CSS(또는 모듈) 기반으로 로직과 스타일링을 분리하면 훨씬 유지보수성 높은 프로젝트가 될 것 같습니다!
(혹시 AI를 통한 바이브코딩 비중이 있었다면, 본 리뷰에서 언급된 엣지 케이스나 의존성 문제에 대해서는 직접 공식문서를 찾아가며 디버깅해 보시는 것을 권장합니다.)
| <a href="/privacy">Privacy Policy</a> | ||
| <a href="/faq">FAQ</a> | ||
| </span> | ||
| <span className="sns-logo"> |
There was a problem hiding this comment.
[P5] 부드러운 칭찬 👏
target="_blank" 속성을 사용할 때 보안을 고려하여 rel="noreferrer"를 함께 함께 적절하게 사용해 주셨네요. 좋은 습관입니다!
| cursor: "pointer", | ||
| }} | ||
| > | ||
| 자유게시판 |
There was a problem hiding this comment.
[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') |
There was a problem hiding this comment.
[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()) |
There was a problem hiding this comment.
[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'} |
There was a problem hiding this comment.
[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}`, |
There was a problem hiding this comment.
[P4] 코드 중복 (DRY 원칙 위배) 개선
BestList.jsx에서도 동일한 기본 API 경로가 반복되어 사용되고 있습니다. Axios를 통한 baseURL 설정이나 공통 fetch 래퍼 함수를 만들면 이런 중복을 우아하게 제거할 수 있어 구조가 한 단계 더 발전할 것 같습니다.
|
|
||
| const handleSearch = (e) => { | ||
| e.preventDefault(); | ||
| setPage(1); |
There was a problem hiding this comment.
[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 |
There was a problem hiding this comment.
[P4] 웹 접근성(a11y) 개선
해당 <input>에 연결된 <label> 태그가 누락되어 있습니다. 스크린 리더 사용자를 배려하기 위해, 시각적으로 감추더라도 id를 부여한 후 보이지 않는 <label>을 연결해 주시거나, aria-label 속성을 추가해 주시는 것이 좋습니다.
| {products.map((item) => ( | ||
| <div key={item.id}> | ||
| <img | ||
| src={item.images[0]} |
There was a problem hiding this comment.
[P2] 결함 방지 (방어적 프로그래밍)
BestList에서는 이미지가 비어있는 경우에 대한 더미 이미지 처리를 꼼꼼하게 해주셨는데, 여기서는 item.images[0]을 바로 참조하고 있습니다. 만약 백엔드에서 images가 빈 배열([])로 오거나 프로퍼티가 누락된 채 전달된다면 에러가 발생하거나 엑스박스가 뜰 수 있습니다. BestList와 동일하게 엣지 케이스 로직을 추가해 주세요.
| > | ||
| < | ||
| </button> | ||
| {[1, 2, 3, 4, 5].map((num) => ( |
There was a problem hiding this comment.
[P4] 페이징 처리 하드코딩
[1, 2, 3, 4, 5]를 배열로 고정(하드코딩)하셨습니다. 만약 데이터가 너무 적어서 전체 페이지가 2페이지밖에 없다면, 3~5번 버튼은 빈 화면을 호출하게 됩니다. 서버에서 응답해주는 총 데이터 갯수나 전체 페이지 수를 기반으로 동적으로 페이지 번호를 렌더링하는 것을 추천드립니다. 또한, 다음(Next) 버튼에도 비활성화(disabled) 조건이 필요합니다.
스프린트 미션4
📌 프로젝트 소개
리액트를 사용하여 페이지를 작성하라는 스프린트 요구사항을 반영하여 좋아요가 가장 많은 상품을 베스트 상품 섹션에, 전체 상품을 판매중인 상품 섹션에 나열한 페이지를 구성함
나열된 전체 상품은 정렬 기능을 통해 좋아요 수 혹은 등록 순으로 순서를 변경하여 볼 수 있음
🛠 사용 기술
🛠 사용 협업툴
🚀 주요 기능
1. App.jsx
2. Layout.jsx / App.css
3. BestList.jsx
4. ProductList.jsx
✔셀프 체크리스트
요구사항
[공통]
[중고마켓 페이지]
PC, Tablet, Mobile 디자인에 해당하는 중고마켓 페이지를 만들어 주세요.
중고마켓 페이지 url path는 별도로 설정하지 않고, '/'에 보이도록 합니다.
상단 네비게이션 바, 푸터는 랜딩 페이지와 동일한 스타일과 규칙으로 만들어주세요.
상품 데이터는 https://panda-market-api.vercel.app/docs/에 명세된 GET 메소드 "/products" 를 활용해주세요.
상품 목록 페이지네이션 기능을 구현합니다.
드롭 다운으로 "최신 순" 또는 "좋아요 순"을 선택해서 정렬을 구현하세요.
상품 목록 검색 기능을 구현합니다.
베스트 상품 데이터는 https://panda-market-api.vercel.app/docs/에 명세된 GET 메소드 "/products"의 정렬 기준 favorite을 사용해주세요.