Don't be afraid of challenges

쿼리 파라미터를 이용한 검색로직 구현 본문

nextjs

쿼리 파라미터를 이용한 검색로직 구현

초아롱 2025. 1. 12. 17:51

2025-01-09 저녁, 인턴업무를 수행하던 와중 검색, 필터링 구현을 시작하게 되었다.

지금까지는 그냥 데이터를 다 받은 다음 프론트 단에서 데이터를 slice하고 그랬었는데 몇 달전 내배캠에서 최종프로젝트를 같이하던 팀원분이 검색로직을 맡았었는데 searchParam을 이용하여 로직구현을 하던것을 보았다.

그땐 시간도 없고 내 업무 처리하느라 바빠서 저게 뭐지 ..? 하고 넘겼었는데 이제 본인이 그 업무를 맡게 된 것이다.

그래서 관련 블로그를 찾아보니 url에 쿼리스트링을 조작하여 상태정보를 저장할 수 있는 방법을 보게 되었다.

 

지금까지 쿼리를 이용하여 검색로직을 구현해야하는 것에 대해 굳이..? 라는 생각이 들었다.

사용자가 검색창에 검색을 하고 필터링을 직접 조작하면 되는 것이 아닌가라고 생각했었는데

 

어느날 게임을 하다가 필터링이 계속 검색할때마다 초기화되는 현상을 맞닥들였는데 이때 마다 계속 필터링 버튼을 클릭하고 검색하고 반복하니 사용자 경험이 떨어진다는 생각이 들었다.

 

이와 마찬가지로 웹사이트에서도 똑같이 사용자가 입력한 값을 어디다가 저장하여 계속 그 데이터를 이용한다면 UX에 매우 도움이 될 것이라 생각하였다. 

 

그래서 관련 블로그를 참고하여 parameter를 이용한 검색로직을 구현해보았다.

 

 

1. 먼저 url에 받아서 쓰기 까지 가능한 URLSearchParams을 조작하는 파일이 필요하다.

참고한 블로그에선

searchParams: Object.fromEntries(searchParams)

 

이렇게 썼는데 이러면 객체형태로 됬었나 ..? 아무튼 데이터 구성이 내 로직과 맞지 않아 에러가 발생했었기 때문에 나는 그냥 searchParams 통째로 받아왔다. 

'use client';

import { usePathname, useRouter, useSearchParams } from 'next/navigation';

type NewParamsType = { [key: string]: string };

const useCustomSearchParams = () => {
  const router = useRouter();
  const pathname = usePathname();
  const _searchParams = useSearchParams();
  const searchParams = new URLSearchParams(_searchParams.toString());

  const setNewParams = (newParams: NewParamsType) => {
    for (const [key, value] of Object.entries(newParams)) {
      if (value) searchParams.set(key, value);
      else searchParams.delete(key);
    }
    return searchParams.toString();
  };

  const setSearchParams = (newParams: NewParamsType) => {
    return router.push(`${pathname}?${setNewParams(newParams)}`);
  };

  return { searchParams, setSearchParams };
};

export default useCustomSearchParams;

참고 : https://gyyeom.tistory.com/148

 

2. Search 구현

회사 구현 시스템이라서 다는 못보여주고 본인이 구현한 검색로직만 가져와봤다.

이런식으로 짜면 url에 검색입력값, 필터링값이 들어와서 자동적으로 필터링된 검색값에 해당하는 데이터를 보여줄 것이다.

export default function SamplePage() {
  const { searchParams, setSearchParams } = useCustomSearchParams();
  const [data, setData] = useState<Data[]>([]);

  const searchQuery = searchParams.get('search') || '';
  const order = searchParams.get('order') === 'true';

  useEffect(() => {
    // 문서 불러오기 API 호출
    const getDataAPI = async () => {
      try {
        const response = await axios.get('데이터 불러오는 url');

        setData(response.data);
      } catch (error) {
        console.error('문서 가져오기 실패:', error);
      }
    };
    getDataAPI();
  }, [searchQuery, order]);
  
  const applyFilters = (data: any[]) => {
    let filteredData= data.filter((item) => item !== null);

    // 검색 필터링
    if (searchQuery) {
      filteredData = filteredData.filter((item) =>
        item.name.toLowerCase().includes(searchQuery.toLowerCase()),
      );
    }

    // 정렬 필터링
    filteredData.sort((a, b) => {
     //필터링 로직
    });

    return filteredData;
  };

  const filteredData = applyFilters(documents);

  // 검색 처리
  const handleSearch = (e: FormEvent) => {
    e.preventDefault();
    const formData = new FormData(e.target as HTMLFormElement);
    const name = formData.get('name') as string;
    setSearchParams({ search: name });
  };

  // 정렬 처리
  const handleOrder = () => {
    setSearchParams({ order: (!order).toString() });
  };

  return (
    <div>
      <검색 바가 있는 컴포넌트
      />

      <div>
        {filteredData.map((item) => (
          <Card
            key={item.id}
            item={item}
            onClick={() => handleDocManager(item.id)}
          />
        ))}
      </div>

    </div>
  );
}

 

 

사실 위에 조건들 말고도 더 조건이 있었는데 현재 백엔드와의 연결에 문제점을 겪고 있어서 이후는 리펙토링을 하면서 다시 짜야할 것같다... 

 

검색로직을 구현하면서 겪은 문제점은 역시 useEffect으로 검색값, 필터링조건 등등이 변경될 때마다 useEffect가 실행되게 해야하는데 무한루프에 빠지게 되어버린 것이다. 그래서 메모이제이션을 사용하는 등의 방법으로 바꿔봤지만 여전히 안됐었는데 지금은 괜찮은데 나중에 다른 값들이 나오면 문제점이 생긴다. 이부분도 천천히 알아본 후 관련 블로그를 개시할 예정이다.

 

검색로직을 구현하면서 좋아진 점은 불필요한 상태관리 변수가 확 줄어든다는 점이다. 상태정보를 검색 parameter에 담아 두니까 상태변수를 선언할 필요가 없어 코드양이 확 줄어들어 가독성 향상에도 도움을 주었다. 이부분의 장점이 너무커서 앞으로도 이 방법을 애용하고 더 깊게 공부해볼 생각이다.