Skip to content

Conversation

@heewls
Copy link
Collaborator

@heewls heewls commented Jan 16, 2025

요구사항

기본

  • 중고마켓 페이지 주소는 “/items” 입니다.

  • 페이지 주소가 “/items” 일때 상단네비게이션바의 '중고마켓' 버튼의 색상은 “3692FF”입니다.

  • 상단 네비게이션 바는 이전 미션에서 구현한 랜딩 페이지와 동일한 스타일로 만들어 주세요.

  • 전체 상품에서 드롭 다운으로 “최신 순” 또는 “좋아요 순”을 선택해서 정렬을 할 수 있습니다.

  • '상품 등록하기' 버튼을 누르면 “/additem” 로 이동합니다. ( 빈 페이지 )

  • 카드 데이터는 제공된 백엔드 API 페이지의 GET 메소드인 “/products”를 사용해주세요

  • 미디어 쿼리를 사용하여 반응형 view 마다 물품 개수를 다르게 보여줍니다 (서버로 요청하는 값은 동일)

  • 베스트 상품

  • Desktop : 4개 보이기

  • Tablet : 2개 보이기

  • Mobile : 1개 보이기

  • 전체 상품

  • Desktop : 12개 보이기

  • Tablet : 6개 보이기

  • Mobile : 4개 보이기

심화

  • 페이지 네이션 기능을 구현합니다.
  • 반응형으로 보여지는 물품들의 개수를 다르게 설정할때 서버에 보내는 pageSize값을 적절하게 설정합니다.

주요 변경사항

스크린샷

멘토에게

@heewls heewls changed the title [김희진] React sprint1 [김희진] sprint5 Jan 16, 2025
@heewls heewls requested a review from kiJu2 January 16, 2025 12:20
@heewls heewls added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label Jan 16, 2025
- search encodeURIComponent delete
- get best items pageSize edit & code cleanup
- if no items when searching(NoneItem)
@kiJu2
Copy link
Collaborator

kiJu2 commented Jan 18, 2025

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

/node_modules
/.pnp
.pnp.js
.env
Copy link
Collaborator

Choose a reason for hiding this comment

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

굿굿 ! .env파일을 작성하고 형상관리에서 제외하셨군요 ! 👍

Comment on lines +3 to +12
const BASE_URL = process.env.REACT_APP_BASE_URL;

export async function getProducts(params) {
const { page, pageSize, orderBy, keyword } = params;
const response = await axios.get(`${BASE_URL}/products`, {
params: { page, pageSize, orderBy, keyword },
});

return response.data;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

크으 ~ 정말 깔끔한 API관리 ! 😊

BASE_URL도 설정하셨네요 👍👍

@@ -0,0 +1,12 @@
import axios from "axios";
Copy link
Collaborator

Choose a reason for hiding this comment

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

instacne를 설정하셔서 사용하시는걸 추천드려요 !

어떻게 세팅하면 될까? 🤔

instance를 만들어서 export를 하고 사용해보는 것 정도로 시도해보면 좋을 것 같아요. axios-instance 파일을 만들어서 instance를 생성하고 export한 후 사용해보는건 어떨까요?
다음과 같이 만들어볼 수 있어요:

const baseURL = process.env.NEXT_PUBLIC_LINKBRARY_BaseURL;

const instance = axios.create({
  baseURL: baseURL,
  headers: {
    'Content-Type': 'application/json',
  },
});

export default instance

axios instance

Comment on lines +10 to +39
export default function AllItems({ items, sortOption, onChange, setKeyword }) {
return (
<S.AllContainer>
<S.AllHeader>
<S.Div>
<S.Title>전체 상품</S.Title>
<Link to="/addItem">
<S.AddBtnForMedia>상품 등록하기</S.AddBtnForMedia>
</Link>
</S.Div>
<S.Filter>
<Search onSearch={setKeyword} />
<Link to="/addItem">
<S.AddBtn>상품 등록하기</S.AddBtn>
</Link>
<Dropdown sortOption={sortOption} list={list} onChange={onChange} />
</S.Filter>
</S.AllHeader>
{items.length !== 0 ? (
<S.ItemCardContainer>
{items.map((items, idx) => (
<ItemCard key={idx} list="all" {...items} />
))}
</S.ItemCardContainer>
) : (
<NoneItem />
)}
</S.AllContainer>
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

크으 ~ 깔끔하네요.

컴포넌트를 선언하고 props를 적절히 사용하고 있군요.
컴포넌트 내부에 상태가 없으며 prosp로만 설계 되었네요? 😉

Comment on lines +6 to +13
useEffect(() => {
updateBestItems();
window.addEventListener("resize", updateBestItems);

return () => {
window.removeEventListener("resize", updateBestItems);
};
}, [updateBestItems]);
Copy link
Collaborator

Choose a reason for hiding this comment

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

굿굿 이벤트 리스너 생성과 제거까지 ! 😊😊

그리고 의존성까지 잘 처리하셨네요 ! 훌륭합니다 ! 👍👍👍

import { getProducts } from "../../../api/products";
import Paging from "../../Paging/Paging";

export default function ItemPage() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

(의견/제안) 해당 페이지에 책임이 많은 것 같아요.

items, bestItems 그리고 페이지네이션에 관련된 상태들까지 페이지에서 관리하고 있어요.
그렇다면 AllItems의 상태가 변경되면 되는 상황에서도 BestItems도 리렌더링 될 것이 우려가 됩니다 !

관련 없는 서로 다른 상태들로 인해 리렌더링이 될 수도 있겠군요.
관련된 상태들을 각각의 컴포넌트로 인계하는건 어떨지 제안드리고 싶어요.
다음은 예

return (
<S.Container>
<BestItems bestItems={responsiveItems} updateBestItems={updateBestItems} />
<AllItems items={items} sortOption={sortOption} onChange={handleChangeClick} setKeyword={setKeyword} />
Copy link
Collaborator

Choose a reason for hiding this comment

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

(이어서) 예를 들어서 다음과 같이 작성해볼 수 있어요:

GPT로 작성된 임의의 코드입니다:

import React, { useEffect, useState } from "react";

function AllItems() {
  const [items, setItems] = useState([]);
  const [currentPage, setCurrentPage] = useState(1);
  const [pageSize, setPageSize] = useState(10);
  const [keyword, setKeyword] = useState("");
  const [totalItems, setTotalItems] = useState(0);
  const [sortOption, setSortOption] = useState("최신순");

  useEffect(() => {
    if (window.innerWidth <= 767) setPageSize(4);
    else if (window.innerWidth >= 768 && window.innerWidth <= 1199) setPageSize(6);
    else setPageSize(10);
  }, []);

  useEffect(() => {
    const orderByValue = sortOption === "최신순" ? "recent" : "favorite";
    getProducts({ page: currentPage, pageSize, orderBy: orderByValue, keyword }).then((result) => {
      if (result) {
        setItems(result.list);
        setTotalItems(result.totalCount);
      }
    });
  }, [currentPage, pageSize, sortOption, keyword]);

  const handleSortChange = (option) => {
    setSortOption(option);
  };

  return (
    <div>
      <div>
        <button onClick={() => handleSortChange("최신순")}>최신순</button>
        <button onClick={() => handleSortChange("좋아요순")}>좋아요순</button>
      </div>
      {items.map((item) => (
        <div key={item.id}>{item.name}</div>
      ))}
      <Paging currentPage={currentPage} pageSize={pageSize} totalItemsCount={totalItems} setPage={setCurrentPage} />
    </div>
  );
}

export default AllItems;


return (
<S.Container>
<BestItems bestItems={responsiveItems} updateBestItems={updateBestItems} />
Copy link
Collaborator

Choose a reason for hiding this comment

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

(이어서)

GPT로 작성된 임의의 코드입니다:

import React, { useEffect, useState } from "react";

function BestItems() {
  const [bestItems, setBestItems] = useState([]);
  const [showItems, setShowItems] = useState(4);

  const updateBestItems = () => {
    if (window.innerWidth <= 767) setShowItems(1);
    else if (window.innerWidth >= 768 && window.innerWidth <= 1199) setShowItems(2);
    else setShowItems(4);
  };

  useEffect(() => {
    updateBestItems();
    window.addEventListener("resize", updateBestItems);
    return () => window.removeEventListener("resize", updateBestItems);
  }, []);

  useEffect(() => {
    getProducts({ page: 1, pageSize: showItems, orderBy: "favorite", keyword: "" }).then((result) => {
      if (result) setBestItems(result.list.slice(0, showItems));
    });
  }, [showItems]);

  return (
    <div>
      {bestItems.map((item) => (
        <div key={item.id}>{item.name}</div>
      ))}
    </div>
  );
}

export default BestItems;

import { getProducts } from "../../../api/products";
import Paging from "../../Paging/Paging";

export default function ItemPage() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

(이어서) 최종적으로 ItemPage는 다음과 같이 될 수 있군요 !:

import React from "react";
import BestItems from "./BestItems";
import AllItems from "./AllItems";

export default function ItemPage() {
  return (
    <div>
      <BestItems />
      <AllItems />
    </div>
  );
}

Comment on lines +6 to +12
export default function Search({ onSearch }) {
const debouncedOnSearch = useMemo(() => debounce(onSearch, 500), [onSearch]);

const handleSearchChange = (e) => {
const search = e.target.value;
debouncedOnSearch(search);
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

크으 ~ 디바운스까지 🥺🥺🥺🥺

@kiJu2
Copy link
Collaborator

kiJu2 commented Jan 18, 2025

수고하셨습니다 희진님 !!
빠른 시간에 리액트에 이미 적응이 되신 것 같아요 ㄷㄷㄷ 이 전에 리액트를 하셨었나욤? 🫢
디바운싱 적용하신 점이 특히 인상깊어요. resize에도 디바운스를 적용해볼 수도 있으니 다음에 한 번 도전해보셔도 될 것 같습니다 😊

@kiJu2 kiJu2 merged commit d3e9461 into codeit-bootcamp-frontend:React-김희진 Jan 18, 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