[#59] React 쇼핑몰 - Shop 페이지 상품 필터링/정렬 + 페이지네이션

2025. 4. 22. 15:42·LG 유플러스 유레카 SW/React

상품 필터링/정렬

  • 카테고리 필터: 전체, 신상품(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] || '등록순'

 

  • 디폴트

http://localhost:5173/shop

  • 신상품 카테고리

http://localhost:5173/shop/?_page=1&_per_page=12&category=new

  • 낮은 가격순 정렬

http://localhost:5173/shop/?_page=1&_per_page=12&category=new&_sort=price

 

 

페이지네이션

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
'LG 유플러스 유레카 SW/React' 카테고리의 다른 글
  • [#61] Redux란
  • [#60] React - Hook + 성능 최적화 정리
  • [#58] React 쇼핑몰 - 장바구니 수정/삭제 + 반응형 Shop 페이지
  • [#57] React 쇼핑몰 - 상품 상세 페이지(탭, 슬라이더) + 장바구니 조회/추가
nueos
nueos
  • nueos
    nueos 공부 기록
    nueos
  • 전체
    오늘
    어제
    • 분류 전체보기 (191)
      • 해커톤 (1)
      • 네이버 BoostCamp (6)
      • LG 유플러스 유레카 SW (3)
        • React (21)
        • TypeScript (2)
        • JavaScript (2)
        • HTML+CSS (5)
        • Spring (7)
        • Java (6)
        • SQL (2)
        • Algorithm (8)
        • CX (6)
        • Git (2)
        • 프로젝트 (2)
        • 스터디 (9)
        • 과제 (8)
        • 특강 (1)
      • React (3)
      • Next (0)
      • Javascript (2)
      • HTML (2)
      • CSS (9)
      • Algorithm (6)
      • Database (0)
      • OS (13)
      • C++ (24)
      • Python (1)
      • jQuery (1)
      • Django (1)
      • Git (1)
      • 개발 지식 (3)
      • 정보 보안 (22)
      • 포렌식 (1)
      • 암호 (2)
      • 기타 (4)
      • 패스트캠퍼스 FE 프로젝트십 (5)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    exhaustive search
    디지랩챌린지
    기술로바꾸는세상
    Stack
    완전 탐색
    제주지역혁신플랫폼지능형서비스사업단
    힙
    Queue
    스택
    큐
    디지털혁신
    제주해커톤
    heap
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
nueos
[#59] React 쇼핑몰 - Shop 페이지 상품 필터링/정렬 + 페이지네이션
상단으로

티스토리툴바