Skip to content

Conversation

@SanginJeong
Copy link
Collaborator

@SanginJeong SanginJeong commented Jul 29, 2025

요구사항

기본

중고마켓

배포링크

  • 중고마켓 페이지 주소는 “/items” 입니다.
  • 페이지 주소가 “/items” 일때 상단네비게이션바의 '중고마켓' 버튼의 색상은 “3692FF”입니다.
  • 상단 네비게이션 바는 이전 미션에서 구현한 랜딩 페이지와 동일한 스타일로 만들어 주세요.
  • 상품 데이터 정보는 https://panda-market-api.vercel.app/docs/#/ 에 명세된 GET 메소드 “/products” 를 사용해주세요.
  • '상품 등록하기' 버튼을 누르면 “/additem” 로 이동합니다. ( 빈 페이지 )
  • 전체 상품에서 드롭 다운으로 “최신 순” 또는 “좋아요 순”을 선택해서 정렬을 할 수 있습니다.

중고마켓 반응형

베스트 상품

  • Desktop : 4개 보이기
  • Tablet : 2개 보이기
  • Mobile : 1개 보이기

전체 상품

  • Desktop : 12개 보이기
  • Tablet : 6개 보이기
  • Mobile : 4개 보이기

심화

  • 페이지 네이션 기능을 구현합니다.

스크린샷

react-sprint-pandamarket-sanginjeong netlify app_items (2) react-sprint-pandamarket-sanginjeong netlify app_items (3) react-sprint-pandamarket-sanginjeong netlify app_items (6)

멘토에게

  • 세팅에 문제가 있어서 rebase를 했더니 이전 커밋들이 남아있네욥...
  • 셀프 코드 리뷰를 통해 질문 이어가겠습니다.

@SanginJeong SanginJeong requested a review from kiJu2 July 29, 2025 10:07
@SanginJeong SanginJeong added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label Jul 29, 2025
@kiJu2
Copy link
Collaborator

kiJu2 commented Jul 31, 2025

스프리트 미션 하시느라 수고 많으셨어요.
상인님 학습에 도움 되실 수 있게 꼼꼼히 리뷰 하도록 해보겠습니다. 😊

@kiJu2
Copy link
Collaborator

kiJu2 commented Jul 31, 2025

세팅에 문제가 있어서 rebase를 했더니 이전 커밋들이 남아있네욥...

하핳 괜찮습니다 !
언제나 깔끔한 커밋 👍👍

Comment on lines +9 to +29
html,
body {
padding: 0;
margin: 0;
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);

ul,
li {
list-style: none;
margin: 0;
padding: 0;
}

@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
a {
text-decoration: none;
color: black;
}

@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
p {
margin: 0;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

(제안/선택) 기본 스타일을 초기화하고 싶다면 reset.css도 좋은 방법이 될 수 있어요 ! 😊

브라우저마다 기본 스타일이 미묘하게 다른거 알고 계셨나요?

https://stackoverflow.com/questions/11578819/css-reset-what-exactly-does-it-do

이처럼 브라우저마다 스타일이 다른 경우 일관된 스타일을 제공할 수 있도록 reset.css를 작성하기도 한답니다 !
그대로 사용하셔도 좋고, 참고해도 좋은 Eric meyer의 reset.css는 다음과 같습니다:

/* http://meyerweb.com/eric/tools/css/reset/ 
   v2.0 | 20110126
   License: none (public domain)
*/

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section {
	display: block;
}
body {
	line-height: 1;
}
ol, ul {
	list-style: none;
}
blockquote, q {
	quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
	content: '';
	content: none;
}
table {
	border-collapse: collapse;
	border-spacing: 0;
}

원문 보기

Comment on lines +11 to +21
<Routes>
{/* 처음 시작 시 /items 로 이동 */}
<Route path="/" element={<Navigate to="/items" />} />

<Route element={<Layout />}>
<Route path="/items" element={<ItemsPage />} />
<Route path="/addItem" element={<AddItemPage />} />
{/* freeBoard : link active 테스트를 위해 미리 만듦 */}
<Route path="/freeBoard" element={<FreeBoard />} />{" "}
</Route>
</Routes>
Copy link
Collaborator

Choose a reason for hiding this comment

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

굿굿 ! react-router를 적절히 사용하셨군요 !

훌륭합니다. 레이아웃을 래핑하신거 보니 라이브러리 사용 방법도 꼼꼼히 확인하고 적용하신 것 같네요 👍

Comment on lines +3 to +17
const DropDown = ({ children }) => {
return <div className="dropdown">{children}</div>;
};

DropDown.header = ({ children, onClick }) => {
return (
<button onClick={onClick} className="button dropdown-btn">
{children}
</button>
);
};

DropDown.menus = ({ children, isOpen }) => {
return isOpen && <ul className="dropdown-menus">{children}</ul>;
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

크으 ~ 역시 👍👍👍

확장성과 유연성을 확보하셨군요 👍👍 각 컴포넌트가 기능 별로 확실히 구분되어 좋습니다 !
학습을 위해 컴포넌트를 구조적으로 설계하시려는 시도가 특히 좋네요 ! 멋집니다 !

Copy link
Collaborator

Choose a reason for hiding this comment

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

(생각해보기/인사이트) 컴포넌트를 최소한의 단위로 잘게 쪼개는게 긍정적인 면만 있을까?

다음 코드를 볼게요 !

<DropDown>
  <DropDown.header onClick={handleClick}>메뉴</DropDown.header>
  <DropDown.menus isOpen={isOpen}> ... </DropDown.menus>
</DropDown>

현재는 위와 같이 사용해야 할거예요.

다음 코드처럼 단순하게 만들어 볼 수도 있을거예요:

const DropDown = ({ label, isOpen, onToggle, children }) => {
  return (
    <div className="dropdown">
      <button onClick={onToggle} className="button dropdown-btn">
        {label}
      </button>
      {isOpen && <ul className="dropdown-menus">{children}</ul>}
    </div>
  );
};

만약 위처럼 만든다면 다음과 같이 사용해볼 수 있어요:

<DropDown
  label="메뉴 열기"
  isOpen={isOpen}
  onToggle={() => setIsOpen((prev) => !prev)}
>
  <li>항목 1</li>
  <li>항목 2</li>
</DropDown>

상인님이 보기에는 어떤게 더 심플하게 사용할 수 있다고 느껴지시나요? 😊

참고로 "이게 더 옳다"를 말하는게 아닙니다 ! 상인님께서 작성하신 코드는 확장성과 유연성 측면에서는 우위를 가진다고 사료되며, 다른 시각도 생각해보실 수 있을 것 같아서 코멘트 드립니다 😉😉

Copy link
Collaborator

Choose a reason for hiding this comment

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

(심화) 또한, 컴포넌트 컴파운드 패턴을 적용하실 수도 있을거예요.

현재 목적 별로 잘 구성해놓으셨기에 Component Compound 패턴으로 리팩토링하시기 쉬어보여서 제안드려봅니다 !

Suggested change
const DropDown = ({ children }) => {
return <div className="dropdown">{children}</div>;
};
DropDown.header = ({ children, onClick }) => {
return (
<button onClick={onClick} className="button dropdown-btn">
{children}
</button>
);
};
DropDown.menus = ({ children, isOpen }) => {
return isOpen && <ul className="dropdown-menus">{children}</ul>;
};
const DropDownContext = createContext();
export const DropDown = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
return (
<DropDownContext.Provider value={{ isOpen, setIsOpen }}>
<div className="dropdown">{children}</div>
</DropDownContext.Provider>
);
};
DropDown.Header = ({ children }) => {
const { setIsOpen } = useContext(DropDownContext);
return (
<button onClick={() => setIsOpen((prev) => !prev)} className="button dropdown-btn">
{children}
</button>
);
};
DropDown.Menu = ({ children }) => {
const { isOpen } = useContext(DropDownContext);
return isOpen && <ul className="dropdown-menus">{children}</ul>;
};

이미 리액트를 경험해보셨기에 조금 심화 피드백을 드려봅니다 !
위와 같이 구성한다면 상태를 자식 컴포넌트 간에 공유할 수 있어서 있어서 "Dropdown" 내에서 생태계가 이뤄질 수 있습니다. 😊😊

컴포넌트 컴파운드 패턴

Comment on lines +6 to +9
const navLinks = [
{ _id: 1, title: "자유게시판", url: "/freeBoard" },
{ _id: 2, title: "중고마켓", url: "/items" },
];
Copy link
Collaborator

Choose a reason for hiding this comment

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

크으 ~ 컴포넌트를 매핑하기 위한 상수군요 👍👍

const isMobile = useMediaQuery({ maxWidth: 767 });

if (isMobile) {
return [1, 4]; // [베스트상품 pageSize, 전체상품 pageSize]
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
return [1, 4]; // [베스트상품 pageSize, 전체상품 pageSize]
return {
bestProductPageSize: 1,
productPageSize: 4,
};

try {
const { data } = await instance.get("/products", { params });
console.log(data);
return [data.list, data.totalCount];
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
return [data.list, data.totalCount];
return data;

배열로 전달되면 첫번째, 두번째 요소를 파악하기 위해 해당 코드까지 확인해서 유추해야할거예요 !

@@ -0,0 +1,11 @@
import { instance } from "../utils/api";

export const useGetProduct = async (params) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 함수는 어떠한 상태도 가지지 않고 있네요 !

즉. 리액트 라이브러리를 사용하지 않는 모듈로 보여요.
use~로 작성하시면 자칫 훅으로 오해할 수 있으므로 다음과 같이 작성해볼 수 있을거예요:

Suggested change
export const useGetProduct = async (params) => {
export const getProduct = async (params) => {

@@ -0,0 +1,11 @@
import { instance } from "../utils/api";

export const useGetProduct = async (params) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

또한, params를 읽었을 때 "어떤 값"이 필요한지 알기 어려울 수 있어요.

Suggested change
export const useGetProduct = async (params) => {
export const useGetProduct = async ({ page, pageSize, orderBy }) => {

위와 같이 작성해준다면 "어떤 파라메터"가 필요한지 명확해지겠죠? 😉

@kiJu2
Copy link
Collaborator

kiJu2 commented Jul 31, 2025

훌륭합니다 상인님 !
빠르게 미션을 수행하셨네요 👍👍
컴포넌트를 분리하시려는 시도가 참 인상깊습니다. 이러한 실험과 열정은 상인님께서 분명 근 미래에 멋진 개발자가 될 상인님께 좋은 양분이 될거라고 확신합니다 !

이번 미션 정말 수고 많으셨습니다 😉😉

@kiJu2 kiJu2 merged commit 211b281 into codeit-bootcamp-frontend:React-정상인 Jul 31, 2025
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.

2 participants