고양이와 코딩

[스프린트 3] 도서 구매 사이트 - 카테고리별 도서 렌더링을 구현해보자 본문

데브코스 TIL

[스프린트 3] 도서 구매 사이트 - 카테고리별 도서 렌더링을 구현해보자

ovovvvvv 2024. 3. 2. 17:53
728x90

이렇게 구현되어있는 도서 화면 !
전체/동화/소설/사회/신간 카테고리가 아무 의미가 없다. 난 분명 강의중에 놓친게 없는거같은데... 왜 필터링이 안되지?! 생각했지만

아마 실제로 구현한 적이 없는듯 합니다 ... 

 

현재는 BookFilter.tsx 파일에서

    const handleCategory = (id: number | null) => {
    const newSearchParams = new URLSearchParams(searchParams);

    if (id === null) {
      newSearchParams.delete(QUERYSTRING.CATEGORY_ID);
    } else {
      newSearchParams.set(QUERYSTRING.CATEGORY_ID, id.toString());
    }
    setSearchParams(newSearchParams);
  };

  const handleNews = () => {
    const newSearchParams = new URLSearchParams(searchParams);

    if (newSearchParams.get(QUERYSTRING.NEWS)) {
      newSearchParams.delete(QUERYSTRING.NEWS);
    } else {
      newSearchParams.set(QUERYSTRING.NEWS, "true");
    }
    setSearchParams(newSearchParams);
  };
  
  
  return (
    <BooksFilterStyle>
      <div className="category">
        {category.map((item) => (
          <Button
            key={item.categoryId}
            size="medium"
            scheme={item.isActive ? "primary" : "normal"}
            onClick={() => handleCategory(item.categoryId)}
          >
            {item.categoryName}
          </Button>
        ))}
      </div>
      <div className="new">
        <Button
          size="medium"
          scheme={searchParams.get("news") ? "primary" : "normal"}
          onClick={handleNews}
        >
          신간
        </Button>
      </div>
    </BooksFilterStyle>
  );
}

이렇게 버튼을 카테고리별로 필터링 하고 있지만, 이것이 카테고리별로 책을 렌더링 하는 기능을 제공하진 않습니다.

 

  1. 버튼을 누르면 화면이 필터링 되어야 합니다.
  2. `handleCategory` 함수를 수정하여 버튼을 클릭할 때 카테고리에 해당하는 도서들을 필터링합니다.
  3. 그러나, 도서 데이터는 BookList 컴포넌트에 위치합니다.
  4. 따라서 필터링된 도서 목록을 `BookList` 컴포넌트에 전달해야 합니다.

→ → 

  1. `BooksFilter` 컴포넌트에서 버튼을 클릭할 때, `handleCategory` 함수를 호출하여 카테고리에 해당하는 도서들을 필터링합니다
  2. 필터링된 도서 목록을 상태로 관리합니다.
  3. 필터링된 도서 목록을 `BookList` 컴포넌트에 전달합니다.
  4. `BookList` 컴포넌트에서는 전달받은 도서 목록을 렌더링합니다.
import styled from "styled-components";
import BookItem from "./BookItem";
import { Book } from "../../models/book.model";
import { useLocation } from "react-router-dom";
import { useEffect, useState } from "react";
import { QUERYSTRING } from "../../constants/querystring";
import { ViewMode } from "./BooksViewSwitcher";

interface Props {
  books: Book[];
}

const BooksList = ({ books }: Props) => {
  const location = useLocation();
  const [view, setView] = useState<ViewMode>("grid");
  const [filteredBooks, setFilteredBooks] = useState<Book[]>(books);

  useEffect(() => {
    const params = new URLSearchParams(location.search);
    if (params.get(QUERYSTRING.VIEW)) {
      setView(params.get(QUERYSTRING.VIEW) as ViewMode);
    }
  }, [location.search]);

  useEffect(() => {
    const categoryId = new URLSearchParams(location.search).get(
      QUERYSTRING.CATEGORY_ID
    );
    if (categoryId !== null) {
      const filtered = books.filter(
        (book) => book.categoryId === parseInt(categoryId)
      );
      setFilteredBooks(filtered);
    } else {
      setFilteredBooks(books);
    }
  }, [location.search, books]);

  return (
    <BooksListStyle view={view}>
      {filteredBooks?.map((item) => (
        <BookItem key={item.id} book={item} view={view} />
      ))}
    </BooksListStyle>
  );
};

interface BooksListStyleProps {
  view: ViewMode;
}

const BooksListStyle = styled.div<BooksListStyleProps>`
  display: grid;
  grid-template-columns: ${({ view }) =>
    view === "grid" ? "repeat(4, 1fr);" : "repeat(1, 1fr);"};
  gap: 24px;
`;

export default BooksList;

filteredBooks, setFilteredBooks useState를 하나 추가하고,

BooksList 컴포넌트에서 useLocation 훅을 사용하여 URL을 통해 현재 카테고리 아이디를 추출 한 후,

useEffect 내에서 categoryId 값으로 상태를 업데이트합니다.

그 후 return 할때 books 대신 filteredBooks를 넣어주면!! 아이디를 기반으로 필터링된 책들만을 렌더링 하게 됩니다.

이렇게 필터링에 성공 ! ՞ o̴̶̷̤ ̫ o̴̶̷̤ ՞🎶

하지만 어쩐지 한 페이지에 8개씩 보여주기로 한 grid가 도서가 2개밖에 없어도 페이지를 나누어 작동하는 등,, 요상하게 작동하는데

이는 pagination 파일의 이 코드 때문이 아닐까,,

  const pages: number = Math.ceil(totalCount / LIMIT);

 

 

🍀Recap.
처음에는 버튼을 눌렀을때 책이 필터링 되어서 화면에 보여야한다!! 라는 생각에 꽂혔기 때문에 무조건 
BooksFilter과 BooksList파일 두 곳의 수정이 필요하다고 생각했다.

하지만 버튼 구성에는 URLSearchParams를 이용해 버튼을 누를 때마다 url에 categoryId 값이 변경되므로
BooksList에서는 다른걸 고려할 필요 없이 URLSearchParams를 사용해 현재 categoryId의 변경만을 캐치해
상태를 업데이트 하며 화면을 렌더링 해주면 된다!


👾보완점
프로젝트가 커질수록 파일 구조 파악은 필수. 파일 구조를 제대로 파악하고 있지 않아서 해결책을 강구하는데 오랜 시간이 걸린다.
그렇다면 URL을 이용하지 않고 어떻게 구현할것인가? 했을때 로직이 바로 떠오르지 않는다
==> 기술부족!!!

props 공부를 다시 해야겠다...