Skip to content

[feat/#48] 작품 정보 페이지 구현#59

Merged
mimizae merged 7 commits intodevelopfrom
feat/product-detail/#48
Nov 26, 2025
Merged

[feat/#48] 작품 정보 페이지 구현#59
mimizae merged 7 commits intodevelopfrom
feat/product-detail/#48

Conversation

@mimizae
Copy link
Copy Markdown
Collaborator

@mimizae mimizae commented Nov 25, 2025

📌 Summary

작품 정보 페이지를 구현했습니다.

📄 Tasks

image

작품 정보 탭에 해당하는 작품 정보 페이지를 구현했습니다.

export const ProductDetail = () => {
  return (
    <div className={styles.container}>
      <PurchaseSafetyNotice />
      <DetailSection />
      <ProductInfoNotice />
    </div>
  );
};
  • 안전 거래 안내
  • 상세 정보 토글
  • 작품 정보 고시

이렇게 세 부분을 컴포넌트로 분리해 페이지를 구성했습니다.

PurchaseSafetyNotice

PurchaseSafetyNotice는 페이지 상단에 노출되는 안전 거래 안내 컴포넌트입니다.
텍스트 중 "고객센터"는 링크 스타일로 보이지만 실제로는 다른 페이지로 이동하지 않는 요소이기 때문에 다음과 같이 처리했습니다

  • 의미적으로는 링크이기 때문에 태그 유지
  • href="#"를 사용하되 실제 페이지 이동을 막기 위해 onClick={(e) => e.preventDefault()} 적용
  • 시각적으로 링크처럼 보이도록 customerCenter 클래스에서 underline 스타일 지정

DetailSection

const largeButtonProps = {
    type: "button" as const,
    "aria-label": isDetailOpen ? "상세 정보 접기" : "상세 정보 더보기",
    hasArrow: true,
    variant: isDetailOpen
      ? LARGE_BUTTON_VARIANTS.ACTIVE
      : LARGE_BUTTON_VARIANTS.DEFAULT,
    onClick: () => setIsDetailOpen(!isDetailOpen),
  };
export const imageWrapperClosed = style({
  maxHeight: 0,
  opacity: 0,
  overflow: "hidden",
  transition: "max-height 0.5s ease, opacity 0.5s ease",
});

export const imageWrapperOpen = style({
  padding: "0.8rem 0 1.6rem 0",
  maxHeight: "50rem",
  opacity: 1,
  transition: "max-height 0.5s ease, opacity 0.5s ease",
});

DetailSection는 상품 상세 이미지의 접기/펼치기 기능을 제공하는 UI 영역입니다.
버튼에 전달되는 props는 largeButtonProps 객체로 묶어 스프레드로 넘기도록 했습니다.
또한 상세 이미지 영역은 isDetailOpen 값에 따라 height와 opacity가 부드럽게 transition 되도록 애니메이션을 적용해 자연스러운 열림/닫힘 UI를 구현했습니다.

ProductInfoNotice

스크린샷 2025-11-25 오후 3 12 32

기본 정보로 구성된 3개의 아코디언과 마지막 작품 제보하기 항목은 레이아웃 상 간격이 달라 동일한 리스트로 처리하기 어려운 구조였습니다.
그래서 먼저 아코디언 항목에 공통으로 필요한 필드를 정리해 AccordionItem 타입을 만들었고, 이를 기준으로 메인 항목(mainInfoItems)과 제보 항목(reportItem)을 각각 별도의 데이터로 분리했습니다.

이렇게 분리하니 상단의 3개 항목은 map으로 깔끔하게 렌더링할 수 있고, 마지막 항목은 개별적으로 렌더링하면서 원하는 간격과 스타일을 자연스럽게 조절할 수 있게 되어 전체 구조가 훨씬 명확하고 유지보수하기 좋은 형태로 바뀌었습니다.

🔍 To Reviewer

  • 지난 svg/img 세팅 시 작품 상세 이미지를 상단에 여백이 있는 채로 저장해서 삭제하고 새로 export 했습니다.
  • 탭바 구현 브랜치에서 미처 삭제하지 못한 임시 컴포넌트들 일괄 삭제했습니다.

📸 Screenshot

2025-11-25.2.36.13.mov

@mimizae mimizae self-assigned this Nov 25, 2025
@mimizae mimizae requested a review from jstar000 as a code owner November 25, 2025 05:56
@mimizae mimizae added the feat 기능 개발 label Nov 25, 2025
@github-actions
Copy link
Copy Markdown

빌드 결과

빌드 성공 🥳

@mimizae mimizae marked this pull request as draft November 25, 2025 05:57
@mimizae mimizae marked this pull request as ready for review November 25, 2025 06:14
Copy link
Copy Markdown
Collaborator

@odukong odukong left a comment

Choose a reason for hiding this comment

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

이미 너무 잘 구현해주셔서 코드리뷰 할 게 딱히 없었던 거 같아요🙌🏻
그래도 추가적으로 개선하면 좋을 것 같은 부분에 대해 약간의 코멘트 달아두었으니
확인 부탁드려요!! 수고하셨습니다 ♥️

Comment on lines +21 to +26
<div
className={
isDetailOpen ? styles.imageWrapperOpen : styles.imageWrapperClosed
}>
<img src={ProductDetailImage} alt="작품 세부 이미지" />
</div>
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.

지금 코드 로직만으로 디테일 섹션에 대한 오픈, 클로즈 스타일 적용이 충분할 것 같긴 한데 딱 recipe를 활용하기 너무 좋아 보이는 코드라ㅎㅎ isDetailOpen 상태에 따라 서로 다른 스타일 클래스를 분기해서 넣어주고 계신데 recipe를 활용하면 return문에서 조건 분기 없이 좀 더 깔끔하게 작성할 수 있을 것 같아요!!

// css
export const imageWrapper = recipe({
  base: {
    transition: "max-height 0.5s ease, opacity 0.5s ease",
  },
  variants: {
    isDetailOpen: {
      true: {
        padding: "0.8rem 0 1.6rem 0",
        maxHeight: "50rem",
        opacity: 1,
      },
      false: {
        maxHeight: 0,
        opacity: 0,
        overflow: "hidden",
      },
    },
  },
  defaultVariants: {
    isDetailOpen: false,
  },
});

variants 값을 일치시켜두면 스타일을 넘겨주는 쪽에서는 속성 키를 따로 작성해주지 않아도 된다는 장점이 있어요!!

<div className={styles.imageWrapper({ isDetailOpen })}> 

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

바엑의 제왕...... ㅠㅠ 감사합니다... 하!!!!!! recipe를 씹어먹는 그날까지...

Comment on lines +4 to +25
interface AccordionItem {
id: string;
title: string;
selectedOption?: string;
iconType?: "arrow" | "chevron";
}

const mainInfoItems: AccordionItem[] = [
{ id: "basic", title: "작품 기본 정보" },
{
id: "delivery",
title: "제작 / 배송",
selectedOption: "평균 1일 / 최대 1일 이내",
},
{ id: "refund", title: "교환 / 환불 / 반품" },
];

const reportItem: AccordionItem = {
id: "report",
title: "작품 제보하기",
iconType: "arrow",
};
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.

디자인상 상단 3개와 하단 1개의 아이코디언 레이아웃이 다른데, 억지로 하나의 배열에 넣고 map 안에서 로 분기 처리하지 않고, 데이터 자체를 mainreport로 깔끔하게 나누니까 목적이 더 명확해 보이는 코드가 된 것 같아요 굳굳👍🏻👍🏻

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

부힛부힛...//

@github-actions
Copy link
Copy Markdown

빌드 결과

빌드 성공 🥳

@github-actions
Copy link
Copy Markdown

빌드 결과

빌드 성공 🥳

@mimizae mimizae merged commit 8ccfece into develop Nov 26, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능 개발 🌸 민재

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 작품 정보 페이지 구현

2 participants