Skip to content

Conversation

@joara-publisher
Copy link
Collaborator

@joara-publisher joara-publisher commented Jan 2, 2026

요구사항

  • 상품 상세 페이지 UI 및 기능 구현 (/items/{productId})
  • API response 데이터를 활용한 화면 렌더링
  • 문의하기 영역 UI 및 입력 상태에 따른 인터랙션 구현
  • 버튼 Hover 효과 적용 (심화)

주요 변경사항

  • 상품 상세 페이지(/items/{productId}) 기본 & 심화 요구사항 전체 구현
  • API response 기반 상품 상세 정보 렌더링
  • “목록으로 돌아가기” 버튼 클릭 시 /items 페이지로 이동 처리
  • 문의하기 영역 UI 구현 및 API 기반 문의 목록 렌더링
  • 문의 내용 입력 시 등록 버튼 색상 변경
  • 화면 구조상 명확한 CTA 버튼이 많지 않아 댓글 영역의 수정 / 삭제 토글 버튼에 Hover 효과 적용

이전 스프린트 미션 PR 피드백 반영 사항

  • 변수명 케이스 오류 수정 (camelCase 통일)
  • 미디어쿼리 수치 오류 수정
  • 네이밍 및 스타일 관련 사소한 오류 전반 수정

주강사님에게

  • 이전 PR에서 전달주신 피드백을 모두 반영했습니다.
  • 이번 미션의 기본 및 심화 요구사항을 모두 구현했습니다.
  • Hover 효과는 UI 구조상 댓글 영역의 수정/삭제 토글 버튼에 적용했습니다.
  • 확인 부탁드립니다. 감사합니다.

배포 링크

yongsub-lee and others added 25 commits October 15, 2025 12:48
…ildfix: correct filename casing in ProductListItem module for Netlify build
@joara-publisher joara-publisher added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label Jan 2, 2026
Copy link
Collaborator

@humonnom humonnom left a comment

Choose a reason for hiding this comment

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

우선 구현은 높은 구현도로 잘 하셨습니다 👍
코드도 깔끔하고 컴포넌트 분리가 적절하게 잘 되어있습니다.

그런데 스타일이 모두 react-project/src/components/ProductDetail.jsx 파일에서 분리되어 있는데, 이 부분에 대해서 한가지 말씀드리고 싶은 것이 있네요.

지금처럼 JSX 로직과 스타일 로직이 분리되면 각자 집중해서 리뷰하거나 테스트하기 쉬울 수 있습니다.
그런데 파일을 계속 왔다갔다 해야해서 작성하거나 수정할때는 번거로울 수도 있어요.(탐색 비용 상승)
따라서 지금의 상태를 유지할지, 내부에서 선언을 할지 고민을 한번 해보시면 좋을 것 같네요.

저는 특정 컴포넌트에서만 쓰이는 스타일은 그 컴포넌트 파일에 함께 두는 게 읽기/리팩토링에 유리해서 하나의 파일로 관리하는 걸 선호하긴 합니다. 컴포넌트의 구현을 한 눈에 볼 수 있다는 장점이 있죠.

나머지 코드 레벨에서 사소한 리뷰를 몇가지 남겨두었으니 확인바랍니다 :)

  • 전달해주신 배포 링크에 접속이 되지 않아서 이번에는 제가 아라님 레포를 클론해서 결과물을 확인했는데요, 혹시 프리뷰가 내부용으로 사용되도록 설정되어있지는 않은지 확인한번 해주시면 좋을 것 같습니다!


return (
<div className="item itemDetails">
<Container>
Copy link
Collaborator

Choose a reason for hiding this comment

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

컴포넌트 분리를 깔끔하게 잘 하셨네요 👍

</DetailNicknameWrap>
</DetailOwnerWrapper>
<DetailFavoritButton>
<DetailFavoritImg src={likeIcon} alt="하트 아이콘" />
Copy link
Collaborator

Choose a reason for hiding this comment

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

(권장) 심화 요구사항에서 모든 버튼에 hover 넣어보라는 내용이 있는데, 다른 부분은 다 잘 하셨는데 하트 아이콘에도 호버 효과 들어가면 좋을 것 같네요

2026-01-05.1.29.59.mov

function ProductDetail ({ item }) {
return (
<DetailWrap>
<DetailImg src={item.images[0]} alt="제품 이미지" />
Copy link
Collaborator

Choose a reason for hiding this comment

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

(권장) 주 이미지 대체 텍스트를 상품명 기반으로 개선하면 좋을 것 같습니다.

Suggested change
<DetailImg src={item.images[0]} alt="제품 이미지" />
<DetailImg src={item.images[0]} alt=`${item.name} 이미지` />

<DetailInfoTitle>상품 태그</DetailInfoTitle>
<LocalTagList>
{item.tags.map((tag, index) => (
<Tag key={index}>#{tag}</Tag>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
<Tag key={index}>#{tag}</Tag>
<Tag key={`tag-${tag}-${index}‎`}>#{tag}</Tag>

(권장) index는 배열 순서가 바뀌거나 중간 삽입/삭제 시 값이 바뀌어 동일 엘리먼트를 다른 것으로 오인할 수 있습니다. 따라서 항목 자체의 고유 식별자를 우선적으로 사용하는 습관을 들이는 것이 좋습니다.

tag 값의 경우 중복될 수 있기에(같은 태그 존재시), 고유한 id가 있다면 id를 사용하는 것이 가장 좋습니다. 현재 서버에서 보내주는 tag 데이터는 배열이고, 각 tag에 id가 없기 때문에 key={tag-${tag}-${index}‎} 이런식으로 쓰시면 됩니다.

<InquiryFormTitle>문의하기</InquiryFormTitle>
<InquiryForm>
<InquiryFormTextarea onChange={handleChange} placeholder='개인정보를 공유 및 요청하거나, 명예 훼손, 무단 광고, 불법 정보 유포시 모니터링 후 삭제될 수 있으며, 이에 대한 민형사상 책임은 게시자에게 있습니다.' />
<InquiryFormButton className="button defaultButton" disabled={content !== '' ? false : true}>등록</InquiryFormButton>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
<InquiryFormButton className="button defaultButton" disabled={content !== '' ? false : true}>등록</InquiryFormButton>
<InquiryFormButton className="button defaultButton" disabled={!content}>등록</InquiryFormButton>

(필수) 단축표현 추천, 삼항 연산자 없이 더 간단하게 쓸 수 있습니다.
컨텐츠가 있으면 false이기 때문에 !content로 쓰셔도 됩니다.

(참고) 공백만 입력된 경우도 막고 싶다면 trim까지 적용하시면 됩니다.
!content.trim()

function getRelativeTime (updatedAt) {
const now = new Date();
const updatedDate = new Date(updatedAt);
const seconds = Math.floor((now.getTime() - updatedDate.getTime()) / 1000); // 차이를 초 단위로 계산
Copy link
Collaborator

Choose a reason for hiding this comment

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

주석 적절히 잘 활용하셨습니다. 👍

@humonnom humonnom closed this Jan 5, 2026
@joara-publisher
Copy link
Collaborator Author

joara-publisher commented Jan 5, 2026

안녕하세요 강사님, 상세한 리뷰 감사합니다!
공유드린 링크가 접속이 안되셨을텐데 이렇게 꼼꼼히 리뷰해주셔서 감사합니다 :)

현재는 스타일 코드들을 별도의 styles 파일로 분리해두었는데,
말씀해주신 것처럼 특정 컴포넌트에서만 사용되는 스타일의 경우에는 컴포넌트 파일 내에 함께 두는 방식이 가독성과 리팩토링 측면에서 더 유리할 수 있겠다는 점을 이번 리뷰를 통해 다시 한 번 고민하게 되었습니다.

다만 이번 미션에서는,
스타일 파일 구조를 통일해서 관리하고 싶었던 점
공통 스타일과 컴포넌트 스타일을 명확히 구분하고 싶었던 점

이 두 가지 이유로 현재와 같은 구조를 선택하게 되었습니다.
다음 미션이나 리팩토링 단계에서는 컴포넌트 단위 스타일 인라인 관리 방식도 적극적으로 적용해보려 합니다.

또한 배포 링크 관련해서는,
Vercel을 이번에 처음 사용하면서 프리뷰 배포가 내부 접근용으로 설정되어 있다는 점을 인지하지 못했던 제 실수였습니다.
현재는 설정을 변경하여 누구나 접근 가능한 Production 배포 상태로 수정해두었고,
이후 미션에서는 동일한 문제가 발생하지 않도록 배포 설정을 더 꼼꼼히 확인하겠습니다.

배포 접근 문제로 번거로움을 드려 죄송합니다.
남겨주신 나머지 리뷰도 확인하면서 다음 미션전까지 반영해보겠습니다.
감사합니다!

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.

3 participants