더보기 (상품 목록 혹은 상품 상세 정보)

Minhyuk Song·2024년 12월 10일
0

쇼핑몰 기능 탐방

목록 보기
4/6

요구사항

  • 상품 목록의 더보기 버튼을 눌렀을 때 상품 목록의 개수가 추가된다.
    • 상품 개수가 많은 경우를 대비해서 limit와 offset을 이용하자.
    • 기존 배열에서 데이터를 추가한 배열을 새롭게 만들자 (스프레드 연산자)
  • 상품 상세정보 더보기 버튼을 눌렀을 때 상품 상세정보가 모두 보인다.
    • 상품 상세정보 더보기 기능은 많은 데이터를 가지지 않는 편이라고 생각하기 때문에 미리 데이터를 받아오고 상세 정보를 나타내는 박스의 높이를 조절하자.

구현결과

1️⃣ 상품 목록의 더보기 기능

import { ProductType } from '@/shared/types/data.type';
import { useState, useEffect } from 'react';

const useMoreView = (initialPage = 1, itemsPerPage = 10) => {
  // ✅ 상태) 페이지 수, 아이템, 더보기가 가능한지 여부
  const [page, setPage] = useState<number>(initialPage);
  const [items, setItems] = useState<ProductType[]>([]);
  const [hasMore, setHasMore] = useState<boolean>(true);

  // ✅ 아이템을 불러오기 위해 API 요청을 보내는 함수
  const getItems = async (page: number) => {
    const response = await fetch(
      `/api/products?page=${page}&limit=${itemsPerPage}`
    );
    const data = await response.json();
    return data;
  };

  // ✅ 불러온 아이템을 추가하고, 페이지 상태와 더보기 여부 상태를 변경하는 함수
  const loadMore = async () => {
    const newItems = await getItems(page);
    setItems((prevItems: ProductType[]) => [...prevItems, ...newItems]);
    setPage(prevPage => prevPage + 1);
    if (newItems.length < itemsPerPage) {
      setHasMore(false);
    }
  };

  // ✅ 렌더링 되고 난 이후 마운트될 때 초기 데이터 불러오기
  useEffect(() => {
    loadMore();
  }, []);

  return { items, loadMore, hasMore };
};

export default useMoreView;

상품 목록 페이지에서 useMoreView 사용하기

import ProductItem from '@/components/product/ProductItem';
import { Button } from '@/components/ui/button';
import useMoreView from '@/features/products/useMoreView';
import { ProductType } from '@/shared/types/data.type';

const ProductList = () => {
  const { items, loadMore, hasMore } = useMoreView();

  return (![](https://velog.velcdn.com/images/be_matthewsong/post/f5c2159f-a835-4261-9f7f-7a04c4902a69/image.gif)

    <div>
      <ul className="grid grid-cols-2 gap-x-2 gap-y-4">
        {items.map((product: ProductType) => (
          <ProductItem
            key={product.id}
            product={product}
          />
        ))}
      </ul>
      {/*✅ 더보기 여부(hasMore)에 따른 더보기 버튼 렌더링 */}
      {hasMore && (
        <div className="mt-4 flex justify-center">
          <Button onClick={loadMore}>더보기</Button>
        </div>
      )}
    </div>
  );
};

export default ProductList;

2️⃣ 상품 상세정보의 더보기 기능

import { fetcher, QueryKeys } from '@/queryClient';
import { useQuery } from '@tanstack/react-query';
import { useParams } from 'react-router-dom';
import { useState } from 'react';
import { Button } from '@/components/ui/button';

const ProductDetail = () => {
  const { id } = useParams();
  const [showDetails, setShowDetails] = useState(false);

  const { data } = useQuery({
    queryKey: [...QueryKeys.PRODUCTS, id],
    queryFn: () => fetcher({ method: 'GET', path: `/products/${id}` })
  });

  if (!data) return null;

  const { category, description, image, price, rating, title } = data;

  return (
    <div className="p-4">
      <img
        src={image}
        alt={title}
        className="h-[500px] w-full rounded-md object-cover"
      />
      <h2 className="mt-2 text-xl font-bold">{title}</h2>
      <p className="text-lg font-semibold">${price}</p>
      <p className="text-gray-700">{category}</p>
      <p className="text-sm text-gray-600">
        Rating: {rating.rate} ({rating.count})
      </p>
      <div
        className={`relative bg-slate-300 ${showDetails ? 'h-[600px]' : 'max-h-20 overflow-hidden'}`}>
        <h3 className="mt-4 text-xl font-bold">상세정보</h3>
        <p className="text-gray-700">{description}</p>
        {!showDetails && (
          <div className="absolute bottom-0 left-0 h-8 w-full bg-gradient-to-t from-white to-transparent"></div>
        )}
      </div>
      <Button
        onClick={() => setShowDetails(!showDetails)}
        className="mt-4 w-full rounded border border-solid border-black bg-white p-2 text-black hover:bg-black/5">
        {showDetails ? '상세정보 접기' : '상세정보 보기'}
      </Button>
    </div>
  );
};

export default ProductDetail;

배운 점

  • 동작에 따른 상태를 정의해보고 구현을 해보니 손쉽게 만들 수 있었던 것 같았다.
  • 커스텀 훅으로 코드를 관리하니 확실히 추상화가 잘되어있어서 책임을 잘 분리해보여 가독성과 유지보수성을 챙긴 것 같다.
  • 더보기 동작에 대해서 처음 생각해보는 시간을 가졌는데 어렵게만 느꼈던 동작이 구현해보니 별 거 아니었다는 생각이 들었다.
profile
어제보다 더 나은 오늘을 만들 수 있게

0개의 댓글

관련 채용 정보