Skip to content

[김예인] 스프린트 미션 5#68

Open
yein0528 wants to merge 1 commit into
codeit-sprint-fullstack:react-김예인from
yein0528:react-김예인

Hidden character warning

The head ref may contain hidden characters: "react-\uae40\uc608\uc778"
Open

[김예인] 스프린트 미션 5#68
yein0528 wants to merge 1 commit into
codeit-sprint-fullstack:react-김예인from
yein0528:react-김예인

Conversation

@yein0528

@yein0528 yein0528 commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

기본 요구사항

공통

  • Github에 스프린트 미션 PR 생성
  • React, Express 사용

프론트엔드 구현 요구사항

랜딩 페이지

  • HTML과 CSS로 구현한 랜딩페이지를 React로 마이그레이션하세요.
  • 랜딩 페이지 url path는 "/"로 설정하세요.

중고마켓 페이지

  • 중고마켓 페이지 url path를 "/items"으로 설정하세요.
  • 페이지 주소가 "/items" 일 때 상단내비게이션바의 "중고마켓" 버튼의 색상은 "3692FF"입니다.
  • 중고마켓 페이지 판매 중인 상품은 본인이 만든 GET 메서드를 사용해 주세요.
  • 다만 좋아요 순 정렬 기능은 제외해 주세요.
  • 사진은 디폴트 이미지로 프론트엔드에서 처리해주세요.
  • 베스트 상품 목록 조회는 구현하지 않습니다.
  • '상품 등록하기' 버튼을 누르면 "/registration" 로 이동합니다. ( 빈 페이지 )

상품 등록 페이지

  • PC, Tablet, Mobile 디자인에 해당하는 상품 등록 페이지를 만들어 주세요.
  • 상품 등록 url path는 "/registration"입니다.
  • 상품 등록은 본인이 만든 POST 메서드를 사용해 주세요.
  • 등록 성공 시, 해당 상품 상세 페이지로 이동합니다. (빈페이지)

백엔드 구현 요구사항

중고마켓

  • Product 스키마를 작성해 주세요.
  • id, name, description, price, tags, createdAt, updatedAt필드를 가집니다.
  • 필요한 필드가 있다면 자유롭게 추가해 주세요.
  • 상품 등록 API를 만들어 주세요.
  • name, description, price, tags를 입력하여 상품을 등록합니다.
  • 상품 상세 조회 API를 만들어 주세요.
  • id, name, description, price, tags, createdAt를 조회합니다.
  • 상품 수정 API를 만들어 주세요.
  • PATCH 메서드를 사용해 주세요.
  • 상품 삭제 API를 만들어 주세요.
  • 상품 목록 조회 API를 만들어 주세요.
  • id, name, price, createdAt를 조회합니다.
  • offset 방식의 페이지네이션 기능을 포함해 주세요.
  • 최신순(recent)으로 정렬할 수 있습니다.
  • name, description에 포함된 단어로 검색할 수 있습니다.
  • 각 API에 적절한 에러 처리를 해 주세요.
  • 각 API 응답에 적절한 상태 코드를 리턴하도록 해 주세요.
  • . env 파일에 환경 변수를 설정해 주세요.
  • CORS를 설정해 주세요.
  • render.com로 배포해 주세요.
  • MongoDB를 활용해 주세요.

심화 요구사항

  • 모든 입력 input box에 빈 값이 있을 경우, 등록 버튼이 비활성화됩니다.
  • 태그를 입력한 후 엔터키를 누르면, 그 태그가 칩 형태로 쌓입니다.
  • 상품명, 상품 소개, 판매 가격, 태그에 대한 유효성 검사 Custom Hook을 만들어주세요. 유효성 검사를 통과하지 않을 경우, 각 input에 빨간색 테두리와, 각각의 Input 아래에 빨간색 에러 메시지를 보여주세요.

배포 링크 : https://one4-sprint-mission-fe-20v4.onrender.com

@yein0528
yein0528 requested a review from devbini June 30, 2026 11:58

@devbini devbini left a comment

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.

🎉 고생 많으셨습니다!

아마 첫 Express/MongoDB API 구현이었을텐데, 전반적으로 큰 문제는 없던 것 같아요.
프론트에서는 커스텀 훅을 적절히 사용하고 있기도 하고, 등록 폼의 touched 상태와 에러 메시지 같은 놓치기 쉬운 세부적인 내용들을 하나씩 처리한 점도 눈에 잘 보였네요.

서버에서도 모델을 분리와 응답을 지정된 형태로 맞춘 덕분에 프론트 페이지네이션과 연결하기 쉬운 구조가 되었습니다. 이런 가시성 좋고 심플한 구조로 캡슐화하는 것도 좋은 능력 중 하나이니, 지금처럼만 쭉 이어가도 좋은 개발 코드가 나올 것 같아요.

그래서.. 전체적으로 화면과 API를 이어보는건 잘 되어있으니... 이제는 프론트와 서버간 통신에 사용되는 구조와 CSS 영향 범위처럼 사소한 제약과 계약? 사항들을 정리하면 다음 작업에서 디버깅 하는 시간도, 유지보수하며 코드를 이해하고 수정하는 시간도 많이 줄어들 것 같습니다.

sprint 6을 먼저 리뷰 해 버려서, sprint 6에서 나온 리뷰 사항들은 여기에 중복으로 적지는 않았어요. 참고 해 주세요! :D

너무나 고생 많이 하셨습니다!
파이팅이에요 🤗

Comment thread server/app.js
.sort(sortOption)
.skip(offset)
.limit(limit)
.select('_id name price favoriteCount createdAt')

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.

🚩 Check Point

카드가 사용할 이미지 필드가 response에 없는 것 같아요.

FE의 ProductCardproduct.images?.[0]를 먼저 확인하고 없으면 기본 이미지를 보여주고 있어서 아마 문제는 없는데 (실 동작 자체에는...)
서버 목록 응답에는 이미지 관련 필드가 다 빠져 있는 것 같아요!.

요구사항상 기본 이미지를 보여주는 처리는 잘 되어 있지만... 나중에 이미지가 있는 상품을 저장해도 목록에서는 계속 기본 이미지만 보일 가능성이 있네요ㅎㅎ

Comment thread server/app.js
Comment on lines +26 to +29
const offset = Number(req.query.offset) || 0
const limit = Number(req.query.limit) || 10
const keyword = req.query.keyword || ''
const orderBy = req.query.orderBy || 'recent'

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.

☕ Thinking

sprint 6 리뷰에도 남겼듯,

정상적인 상황에서는 문제가 없지만, 실제 환경에서는 어떤 문제가 나올지 몰라요.
예를 들어- limit 데이터가 음수로 들어간다거나, 비정상적으로 엄청 큰 데이터가 들어간다던가...

Math 라이브러리를 활용하면 아마 상/하 제한을 걸 수 있을텐데, 서버 리소스 보호를 생각하는 방법이 하나 더 있지 않을까 싶어 남겨둡니다ㅎㅎ

} finally {
setIsLoading(false);
}
}, [orderBy, keyword, page, pageSize]); //의존성 배열에 넣기

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.

☕ Thinking

의존성 규칙에 따라.... 상품 목록 요청이 여러 번 수행됐을 때 최신 데이터가 덮어씌워질 위험이 있어보이긴 하네요.

예를 들면..

  • 사용자가 검색어를 빠르게 입력하거나
  • 화면 크기 변경으로 pageSize가 바뀌거나

이런 경우 요청이 여러 개 날아갈 수 있어 보여요. 이때... 늦게 끝난 이전 요청이 setProducts, setTotalCount를 실행하면 화면에는 과거 데이터가 보일 수 있어요.

요청마다 id를 두고 가장 마지막 요청만 상태를 반영하도록 처리하는 것도 방법이고, 인터벌을 주는 등 다양한 방법이 있으니.. 안정성 향상을 위해 한번 생각 해 보죠ㅎㅎ

@@ -0,0 +1,40 @@
const BASE_URL = 'http://localhost:3000'

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.

☕ Thinking

그 아래 보니까 axios.js와 이 곳 두 곳에 BASE_URL이 동일하게 선언이 되어있는 것 같아요.

이렇게 되면.. API 주소를 바꿀 때 두 파일을 모두 수정해야 합니다. 나중에 배포 주소나 프록시 설정이 바뀌면 한쪽만 수정되어 버그가 생길 수 있습니다. (휴먼 에러는 생각보다 자주 일어나요!)

공통 API_BASE_URL 상수를 만들어 함께 쓰면 유지보수가 더 쉬워지지 않을까요! 🤗

Comment on lines +1 to +13
* {
box-sizing: border-box;
}

body {
margin: 0;
font-family: Pretendard, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: #374151;
}

a {
text-decoration: none;
}

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.

☕ Thinking

React에서 css는 해당 컴포넌트에만 스코프가 생기는 게 아니라 전역으로 적용됩니다.

무슨 말이냐면, 이 CSS를 로드할 때 랜딩 페이지 스타일 뿐 만 아니라 등록 페이지의 링크나 body 기본 스타일에도 영향이 갈 수 있어요.

그래서.. 이런 a나 body, * 등의 전역 리셋 선택자는 index.css 같은 공통 파일에 모아서 관리하는 게 일반적이랍니다.
랜딩 전용 스타일은 .landingPage a처럼 페이지 기반 루트 클래스를 만들어 관리해서 스타일 충돌을 줄일 수 있습니다.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants