상품관리 - Part 5 상품 카테고리 분류 및 소트 정렬 기능
ㆍProject Diary/React + Firebase (Snack ShoppingMall)
상품을 카테고리별로 분류하고, 상품 리스트를 다양한 기준으로 정렬하는 기능을 구현 방법 기록
이 과정에서는 부모 컴포넌트와 자식 컴포넌트 간의 데이터 전송이 중요한 포인트입니다.
1. 컴포넌트 구조
컴포넌트는 크게 ProductView, ProductCategory, ProductSection으로 나뉘며, ProductView가 부모 컴포넌트 역할을 합니다.
ProductView 컴포넌트(부모)
먼저, ProductView 컴포넌트에서는 title 상태를 관리하고, 이를 자식 컴포넌트인 ProductCategory와 ProductSection에 전달합니다. title 상태는 현재 선택된 카테고리를 나타냅니다.
import React, { useState } from "react";
import styled from "styled-components";
import ProductCategory from "@/components/product/ProductCategory";
import ProductSection from "@/components/product/ProductSection";
const ItemListViewBlock = styled.div``;
const ProductView = () => {
const [title, setTitle] = useState("all");
const changeTitle = (value) => {
setTitle(value);
};
return (
<ItemListViewBlock>
<ProductCategory changeTitle={changeTitle} title={title} />
<ProductSection title={title} />
</ItemListViewBlock>
);
};
export default ProductView;
- title 상태는 현재 선택된 카테고리를 나타냅니다.
- changeTitle 함수는 카테고리를 변경하는 역할을 합니다.
ProductCategory 컴포넌트
ProductCategory 컴포넌트는 카테고리 버튼을 렌더링하며, 버튼 클릭 시 부모 컴포넌트의 changeTitle 함수를 호출해 카테고리를 변경합니다.
import React from "react";
import styled from "styled-components";
const ProductCategoryBlock = styled.div`
//디자인 생략
`;
const ProductCategory = ({ changeTitle, title }) => {
const category = ["all", "클래식상품", "시즌한정상품"];
return (
<ProductCategoryBlock>
{category.map((item, index) => (
<button
key={index}
type="button"
onClick={() => changeTitle(item)}
className={title === item && "on"}
>
{item}
</button>
))}
</ProductCategoryBlock>
);
};
export default ProductCategory;
- changeTitle 함수는 부모 컴포넌트에서 전달받아 사용됩니다.
- 현재 선택된 카테고리는 title을 통해 표시됩니다.
ProductSection 컴포넌트
ProductSection 컴포넌트는 상품 리스트를 렌더링하고, 카테고리와 정렬 기능을 제공합니다.
import React, { useState, useRef, useEffect } from "react";
import styled from "styled-components";
import { fetchProducts } from "@/store/product";
import { useSelector, useDispatch } from "react-redux";
import { Link } from "react-router-dom";
const OnlineShopsectionBlock = styled.div`
display: flex;
flex-direction: column;
align-items: center;
`;
const UlBlock = styled.ul`
display: flex;
flex-wrap: wrap;
list-style: none;
margin-top: 50px;
`;
const ListBlock = styled.li`
//스타일 생략
`;
const ProductSection = ({ title }) => {
const dispatch = useDispatch();
// Redux store에서 전체 상품 데이터를 가져옵니다.
const allData = useSelector((state) => state.products.products);
// 현재 보여줄 상품 리스트를 관리하는 상태
const [products, setProducts] = useState(allData);
// 정렬 기준 목록
const sortType = [
{ type: "name", text: "상품명순" },
{ type: "price", text: "가격순" },
];
// 현재 선택된 정렬 기준
const [changeSort, setChangeSort] = useState("");
// 정렬 방향을 관리하는 플래그
const sortFlag = useRef(false);
// 컴포넌트가 처음 마운트될 때 전체 상품 데이터를 가져옵니다.
useEffect(() => {
dispatch(fetchProducts());
}, [dispatch]);
// 상품 데이터 또는 카테고리 제목이 변경될 때마다 필터링된 상품 리스트를 업데이트합니다.
useEffect(() => {
if (allData.length > 0) {
if (title === "all") {
setProducts(allData);
} else {
setProducts(allData.filter((item) => item.category === title));
}
}
}, [allData, title]);
// 주어진 keyname을 기준으로 상품 리스트를 정렬합니다.
const sortProduct = (keyname) => {
if (!sortFlag.current) {
setProducts((products) => {
let sortProducts = [...products];
return sortProducts.sort((a, b) => (a[keyname] < b[keyname] ? -1 : 1));
});
} else {
setProducts((products) => {
let sortProducts = [...products];
return sortProducts.sort((a, b) => (a[keyname] > b[keyname] ? -1 : 1));
});
}
sortFlag.current = !sortFlag.current; // 정렬 방향을 토글합니다.
};
return (
<OnlineShopsectionBlock className="row">
{/* 정렬 버튼 */}
<ButtonBlock>
{sortType.map((item, index) => (
<button
key={index}
onClick={() => {
setChangeSort(item.type);
sortProduct(item.type);
}}
className={changeSort === item.type && "on"}
>
{item.text}
</button>
))}
</ButtonBlock>
{/* 상품 리스트 */}
<UlBlock>
{products.map((item, index) => (
<ListBlock key={index}>
<div className="photo">
<Link to={`/product/${item.id}`} state={{ item: item }}>
<img src={item.photo} alt={item.name} />
</Link>
</div>
<div className="info">
<p>
<a href="#">{item.name}</a>
</p>
<p>{parseInt(item.price).toLocaleString()}¥</p>
<span>{item.inventory}개 남았습니다.</span>
</div>
</ListBlock>
))}
</UlBlock>
{/* 관리자 전용 상품 등록 버튼 */}
<ProductInsert>
<Link to="/productInsert">상품등록</Link>
</ProductInsert>
</OnlineShopsectionBlock>
);
};
export default ProductSection;
- ProductSection 컴포넌트는 title에 따라 필터링된 상품을 렌더링합니다.
- sortProduct 함수는 정렬 기준에 따라 상품을 정렬합니다.
2. 데이터 전송과 상태 관리
- 부모 컴포넌트(ProductView)는 자식 컴포넌트(ProductCategory, ProductSection)에 데이터를 전달하고, 자식 컴포넌트는 부모 컴포넌트의 상태 변경 함수를 호출하여 상호작용합니다.
정리
이번 파트에서는 상품을 카테고리별로 분류하고, 다양한 기준으로 정렬하는 기능을 구현했습니다.
중요한 포인트는 부모 컴포넌트와 자식 컴포넌트 간의 데이터 전송과 상호작용이며, 이를 통해 효율적으로 상태를 관리하고 화면에 반영할 수 있습니다.