(React) 쿼리 스트링을 이용한 필터링 기능 구현

개발차 DevCHA·2023년 5월 22일
2
post-thumbnail

react-router-dom의 useSearchParams 훅을 이용하여 필터링을 구현합니다.



이미지를 보면 URL의 뒷부분에 형광펜으로 칠해진 ?language=javascript 부분이 있다. ?로 시작하는 이 부분을 쿼리 스트링이라고 하며, 키=값의 형태로 나타낸다. 만약 여러 쌍의 쿼리 스트링이 있을 경우 &로 구분한다. 쿼리 스트링은 서버에 데이터를 보내는 용도로도 사용할 수 있지만, 클라이언트에서 데이터 필터링을 할 때도 유용하다.

프로그래머스 코딩 테스트 페이지를 예로 들면, 쿼리 스트링이 없는 상태에서 언어의 기본값은 C 이다. 유저가 다른 언어를 선택하면 쿼리 스트링의 KEY 부분에 language가 추가되고 VALUE에는 유저가 선택한 언어(예: javascript)가 들어간다.

쿼리 스트링을 이용한 필터링이 다른 필터링 방식보다 유용한 이유는 다음과 같다:

  1. 새로고침을 하더라도 필터가 그대로 유지된다.
  2. URL을 공유했을 때 서로 같은 데이터를 볼 수 있다.

구현하기

특정 카테고리가 선택되면 전체 아이템 리스트에서 조건을 만족하는 아이템만 필터링해서 보여주고 싶다. 구현 순서는 다음과 같다:

  1. 카테고리 목록을 만든다.
  2. 각각의 카테고리 클릭시 쿼리 스트링이 추가된다.
  3. 쿼리 스트링을 통해 아이템을 필터링한다.

카테고리 목록 만들기

나는 카테고리 컴포넌트를 구현하기 위해 카테고리의 이름쿼리 스트링으로 추가할 값, 이미지의 경로가 필요했다. 그래서 이 3가지 정보를 상수 배열로 관리했다.

export const CATEGORY_LIST = [
  {
    parameter: 'all',
    name: '전체',
    imgSrc: '/all.png',
  },
  {
    parameter: 'product',
    name: '상품',
    imgSrc: '/product.png',
  },
  {
    parameter: 'category',
    name: '카테고리',
    imgSrc: '/category.png',
  },
  {
    parameter: 'exhibition',
    name: '기획전',
    imgSrc: '/exhibition.png',
  },
  {
    parameter: 'brand',
    name: '브랜드',
    imgSrc: '/brand.png',
  },
];

이렇게 배열을 만들었다면 카테고리 목록을 만들 때는 CATEGORY_LIST 를 가져와서 map 메서드를 이용하면 된다.

import Category from '../ui/Category.jsx';
import { CATEGORY_LIST } from '../../helpers/constants.js';

function CategoryList() {
  return (
    <ul>
      {CATEGORY_LIST.map((category) => (
        <Category key={category.name} {...category} />
      ))}
    </ul>
  );
}

export default CategoryList;


쿼리 스트링 추가하기

쿼리 스트링을 추가하는 데는 다양한 방법이 있지만, 이번 프로젝트에서는 react-router-domuseSearchParams 훅을 이용했다.

useSearchParams 훅은 useState와 유사하게 두 가지 값을 리턴하는데, params와 params의 setter 함수가 그것이다.

import { useSearchParams } from 'react-router-dom';

const [searchParams, setSearchParams] = useSearchParams();

searchParamsgetset 메서드를 통해 각각 쿼리 스트링을 가져오거나 추가할 수 있다.

searchParams.set('key', 'value');
searchParams.get('key') // value

사용 방법을 익혔으니 실제 프로젝트에 적용해보자!


<CategoryList /> 컴포넌트에서는 CATEGORY_LIST 의 요소 개수만큼 <Category /> 컴포넌트를 반복 출력하고 있다.

<Category /> 컴포넌트는 부모로부터 parameter, name, imgSrc 값을 받아온다. 이 중 카테고리를 클릭했을 때 parameter를 쿼리 스트링에 추가하면 된다.

import { useSearchParams } from 'react-router-dom';

function Category({ parameter, name, imgSrc }) {
  const [searchParams, setSearchParams] = useSearchParams();

  const setSortParams = () => {
    searchParams.set('sort', parameter);
    setSearchParams(searchParams);
  };

  return (
    <li onClick={setSortParams}>
      <img src={imgSrc} alt={name} />
      <span>{name}</span>
    </li>
  );
}

export default Category;

sort라는 키를 가진 쿼리 스트링의 값이 잘 추가되는 모습을 볼 수 있다.


아이템 필터링하기

필터링을 할 때는 useEffect를 사용하면 된다. 의존성 배열에 sort를 넣어 sort 값이 바뀔 때마다 아이템의 목록을 필터링하는 방식이다.

import { useSelector } from 'react-redux';
import { useSearchParams } from 'react-router-dom';
import { useState, useEffect } from 'react';
import Card from './Card';

function ProductList() {
  const [searchParams] = useSearchParams();
  const sort = searchParams.get('sort'); // sort 값을 가져온다.
  const isAll = !sort || sort === 'all';
  const { products } = useSelector((state) => state.products);
  const [filteredProducts, setFilteredProducts] = useState(products);

  useEffect(() => {
    if (!products.length) return; 
    if (isAll) {
    // sort의 값이 '전체'일 경우 전체 상품을 렌더링한다.
      setFilteredProducts(products); 
      return;
    }
    // sort의 값을 이용해 전체 상품에서 조건을 만족하는 새 상품으로 구성된 배열을 만든다.
    const newProducts = products.filter((product) => product.type.toLowerCase() === sort);
    setFilteredProducts(newProducts); 
  }, [sort, products]);

  return (
    <div>
    // 필터링된 배열을 렌더링한다.
      {filteredProducts.map((product) => (
        <Card key={product.id} product={product} />
      ))}
    </div>
  );
}

export default ProductList;

리덕스 등 다른 로직이 섞여서 복잡해 보일 수 있지만 원리 자체는 단순하다.
완성된 모습은 아래와 같다!
이제 새로고침을 하거나 주소를 공유해도 같은 화면을 볼 수 있다.


0개의 댓글