상품 필터링/정렬
- 카테고리 필터: 전체, 신상품(new), 인기상품(top) 선택 가능
- 정렬 조건: 등록순(id), 낮은 가격순(price), 높은 가격순(-price), 낮은 할인율(discount), 높은 할인율(-discount)
- URL 쿼리스트링을 이용한 상태 관리
- ex) /shop?_page=1&_per_page=12&category=top&_sort=price
- React Router의 loader 기능 활용
Router Loader에서 쿼리 파싱 및 쿼리 스트링 조합
- 쿼리 파라미터를 받아 json-server에 전달할 쿼리스트링 구성
- 필요한 데이터를 미리 받아두기 위해 loader 사용
export const shopLoader = async ({ request }) => {
const url = new URL(request.url)
const page = url.searchParams.get('_page') || 1
const per_page = url.searchParams.get('_per_page') || 12
const category = url.searchParams.get('category') || ''
const sort = url.searchParams.get('_sort') || ''
let queryString = `_page=${page}&_per_page=${per_page}`
if (category) queryString += `&category=${category}`
if (sort) queryString += `&_sort=${sort}`
const products = await getProductsData(queryString)
return { products, per_page }
}
useSearchParams로 현재 URL 쿼리 스트링 가져오기
const [searchParams] = useSearchParams()
const currentCategory = searchParams.get('category') // 예: 'top'
ShopPage에서 동작 구현
- 클릭 시 URLSearchParams로 파라미터 조작
- 카테고리 클릭 시 category 파라미터 설정/삭제
const handleCategoryFilter = category => {
const params = new URLSearchParams(searchParams)
params.set('_page', 1)
params.set('_per_page', per_page)
category ? params.set('category', category) : params.delete('category')
navigate(`/shop/?${params}`)
}
- 정렬 조건 클릭 시 _sort 파라미터를 동적으로 설정
const handleSort = sortOption => {
const params = new URLSearchParams(searchParams)
params.set('_page', 1)
params.set('_per_page', per_page)
params.set('_sort', sortOption)
setIsDown(false)
navigate(`/shop/?${params}`)
}
정렬 옵션 텍스트 처리
const sortTextMap = {
id: '등록순',
price: '낮은 가격순',
'-price': '높은 가격순',
discount: '낮은 할인순',
'-discount': '높은 할인순',
}
const getSortText = () => sortTextMap[sortCase] || '등록순'
- 디폴트
- 신상품 카테고리
- 낮은 가격순 정렬
페이지네이션
useSearchParams()로 현재 페이지 가져오기
- _page 쿼리 스트링에서 현재 페이지 번호를 읽음
- 쿼리가 없을 경우 기본값으로 1을 사용
const [searchParams] = useSearchParams()
const currentPage = Number(searchParams.get('_page') || 1)
URL 기반 페이지 이동
- 사용자가 페이지 버튼을 누를 때마다 _page 값을 변경
const handlePageChange = page => {
const params = new URLSearchParams(searchParams)
params.set('_page', page)
navigate(`/shop/?${params}`)
}
페이지 번호 목록 동적 계산
- 전체 페이지 수가 maxPageNumber 이하면 전체 페이지 수만큼 표시
- 많을 경우, 현재 페이지를 기준으로 maxPageNumber 개수만큼 슬라이싱
- startPage를 현재 페이지 기준으로 계산하고, endPage가 전체 페이지 수를 초과하지 않도록 제한
- 다시 startPage를 보정하여 항상 maxPageNumber 개수의 페이지가 보이도록 만듦
// 페이지 번호 계산 함수
const getPageNumbers = () => {
// 한번에 보여줄 최대 페이지 번호 수
const maxPageNumber = 10
// 전체 페이지기가 최대 페이지보다 작으면 전체 페이지 번호 표시
if (pages <= maxPageNumber) {
return Array.from({ length: pages }, (_, i) => i + 1)
}
// 현재 페이지를 기준으로 startPage 계산 (최소 1 이상)
let startPage = Math.max(1, currentPage - Math.floor(maxPageNumber / 2))
// 시작 페이지로부터 maxPageNumber만큼 범위를 잡되, 최대 페이지 수를 넘지 않게 제한
let endPage = Math.min(pages, startPage + maxPageNumber - 1)
// endPage 기준으로 다시 startPage 보정 (1 미만으로 내려가지 않게)
startPage = Math.max(1, endPage - maxPageNumber + 1)
return Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i)
}
페이지 이동 버튼 (처음, 이전, 다음, 끝)
<div className={css.paginationArea}>
<button onClick={() => handlePageChange(first)} disabled={currentPage === first}>
처음으로
</button>
<button
onClick={() => handlePageChange(prev)}
disabled={prev === null || currentPage === first}
>
<i className="bi bi-chevron-left"></i>
</button>
{pageNumbers.map(num => (
<button
key={num}
onClick={() => handlePageChange(num)}
className={num === currentPage ? css.active : ''}
>
{num}
</button>
))}
<button
onClick={() => handlePageChange(next)}
disabled={next === null || currentPage === last}
>
<i className="bi bi-chevron-right"></i>
</button>
<button onClick={() => handlePageChange(last)} disabled={currentPage === last}>
끝으로
</button>
</div>
728x90
반응형
'LG 유플러스 유레카 SW > React' 카테고리의 다른 글
[#61] Redux란 (1) | 2025.04.24 |
---|---|
[#60] React - Hook + 성능 최적화 정리 (0) | 2025.04.23 |
[#58] React 쇼핑몰 - 장바구니 수정/삭제 + 반응형 Shop 페이지 (0) | 2025.04.21 |
[#57] React 쇼핑몰 - 상품 상세 페이지(탭, 슬라이더) + 장바구니 조회/추가 (0) | 2025.04.18 |
[#56] React 쇼핑몰 - 상품 상세 페이지 (1) | 2025.04.17 |