이번 글에서는 페이지 라우팅 시 여러 가지 path를 사용하지 않고 하나의 URL의 쿼리 문자열 부분을 조작하여 읽어 라우팅을 하는 useSearchParams에 대해 작성해보려고 한다.

useSearchParams란?

주로 JavaScript 프레임워크와 라이브러리에서 URL 검색 파라미터를 관리하는 데 사용되는 개념이다.
특히 react와 같은 SPA(Single Page Application)에서 많이 사용된다.

보통의 경우 URL의 쿼리 문자열은 `?`로 시작하여 `&`로 구분되는 키-값 쌍으로 이루어져 있다.
예시로 알아보자

http://localhost:3000/survey/new/?step=1

위 주소는 현재 내가 하고 있는 프로젝트에서 설문 생성하기 페이지 중 1페이지를 가리킨다.

그렇다면 어떤 원리로 문자열을 읽고 해당 페이지로 이동하는지 알아보자.

프로젝트 내 코드

import { useSearchParams } from 'react-router-dom';  
import { useEffect } from 'react';  
import CreateSurveyPageStep1 from './create-survey-page1';  
import CreateSurveyPageStep2 from './create-survey-page2';  
import CreateSurveyPageStep3 from './create-survey-page3';  
import { SurveyPreviewPage } from './survey-preview-page';  

export default function CreateSurveyPage() {  
  const [searchParams, setSearchParams] = useSearchParams();  

  const step = searchParams.get('step');  

  useEffect(() => {  
    if (!step) {  
      setSearchParams({ step: '1' });  
    }  
  }, [step, setSearchParams]);  

  let content;  
  if (step === '1') {  
    content = <CreateSurveyPageStep1 />;  
  } else if (step === '2') {  
    content = <CreateSurveyPageStep2 />;  
  } else if (step === '3') {  
    content = <CreateSurveyPageStep3 />;  
  } else if (step === 'preview') {  
    content = <SurveyPreviewPage />;  
  } else {  
    content = <CreateSurveyPageStep1 />; // Default 값으로 첫 페이지  
  }  

  return <>{content}</>;  
}  
  1. const [searchParams, setSearchParams] = useSearchParams();

useSearchParams훅을 사용하여 searchParamssetSearchParams를 정의한다.

searchParams는 URL 쿼리 파라미터의 현재 상태를 나타내고, setSearchParams는 이 파라미터를 업데이트하는 함수이다.

  1. const step = searchParams.get('step');

여기서 get 개념이 중요하다.

get(name): 특정 이름의 첫 번째 값을 반환한다.

따라서 URL의 step 이라는 부분의 첫 번째 값을 반환하다고 보면 된다.

  1. useEffect(() => {  
    if (!step) {  
     setSearchParams({ step: '1' });  
    }  
    }, [step, setSearchParams]);  

useEffect 훅을 사용하여 step이 설정되어 있지 않으면 step의 값을 1로 설정한다.
그리고 컴포넌트가 처음 마운트 될 때 실행되는데 step 또는 setSearchParams가 변경될 때마다 이 효과가 재실행된다.

  1. if (step === '1') {  
    content = <CreateSurveyPageStep1 />;  
    } else if (step === '2') {  
    content = <CreateSurveyPageStep2 />;  
    } else if (step === '3') {  
    content = <CreateSurveyPageStep3 />;  
    } else if (step === 'preview') {  
    content = <SurveyPreviewPage />;  
    } else {  
    content = <CreateSurveyPageStep1 />; // Default 값으로 첫 페이지  
    }  

step 뒤에 어떤 숫자가 나오냐에 따라 렌더링되는 페이지가 달라진다. 물론 default 값은 1페이지다.

  1. return <>{content}</>;  
    마지막으로 렌더링하고자 하는 페이지를 return 해준다.

프로젝트 내 또다른 예시

import { NoResult } from '@/components/home/no-result';  
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';  
import { useCallback, useEffect, useState } from 'react';  
import { useSearchParams } from 'react-router-dom';  
import { HomePageWrapper } from './home-page.styles';  

const HomePage = () => {  
  const [searchParams, setSearchParams] = useSearchParams();  
  const [activeTab, setActiveTab] = useState<string>('progress');  

  useEffect(() => {  
    const type = searchParams.get('type');  
    if (type && (type === 'progress' || type === 'complete')) {  
      setActiveTab(type);  
    } else {  
      setSearchParams({ type: 'progress' });  
    }  
  }, [searchParams, setSearchParams]);  

  const handleTabChange = useCallback(  
    (value: string) => {  
      setActiveTab(value);  
      setSearchParams({ type: value });  
    },  
    [setSearchParams]  
  );  

  return (  
    <HomePageWrapper>  
      <Header>  
        <h1>내 이벤트</h1>  
      </Header>  
      <Tabs  
        style={{ height: 'calc(100% - 6.375rem)' }}  
        value={activeTab}  
        onValueChange={handleTabChange}  
      >  
        <TabsList>  
          <TabsTrigger value="progress">진행 중</TabsTrigger>  
          <TabsTrigger value="complete">진행 완료</TabsTrigger>  
        </TabsList>  
        <TabsContent value="progress">  
          <NoResult />  
        </TabsContent>  
        <TabsContent value="complete">  
          <NoResult />  
        </TabsContent>  
      </Tabs>  
      <FloatingButton />  
    </HomePageWrapper>  
  );  
};  

export default HomePage;  

위 예시 또한 useSearchParams를 사용한 예제이지만, 선택한 Tab에 따라 렌더링되는 컴포넌트(페이지)가 달라진다.
원리는 똑같지만 페이지 라우팅뿐만 아니라 선택한 Tab에 따라 보이는 컴포넌트(페이지)가 달라진다는 것을 알려주고자 예시를 첨부하게 되었다.
아래는 예시에 대한 영상이다.

<video autoplay muted controls>  
  <source src="[https://velog.velcdn.com/images/kop981020/post/893a8974-2dd9-4d4a-89da-d54f569002a9/image.mov](https://velog.velcdn.com/images/kop981020/post/893a8974-2dd9-4d4a-89da-d54f569002a9/image.mov)" type="video/mp4"> 
</video>  

장점

간편한 상태 관리

useSearchParams를 사용하면 URL의 쿼리 파라미터를 통해 상태를 관리할 수 있다.
이를 통해 사용자가 페이지를 새로고침하거나 직접 URL을 입력해도 상태를 유지할 수 있다. 예를 들어, 필터링, 정렬, 검색어와 같은 UI 상태를 URL에 저장해두면 사용자가 페이지를 다시 방문할 때 동일한 상태로 페이지를 볼 수 있다.

뒤로 가기/앞으로 가기 지원

SPA(Single Page Application)에서 브라우저의 뒤로 가기 및 앞으로 가기 버튼을 통해 페이지 상태를 쉽게 탐색할 수 있다. useSearchParams를 사용하면 상태가 URL에 저장되므로, 사용자가 브라우저의 탐색 기능을 사용할 때 URL에 저장된 상태에 따라 페이지가 적절하게 업데이트된다.

SEO 및 웹 분석

쿼리 파라미터를 통해 검색 엔진 최적화(SEO)를 개선하고 웹 분석 도구에서 유용한 데이터를 수집할 수 있다.
예를 들어, 검색어, 카테고리, 페이지 번호 등을 쿼리 파라미터로 설정하면 웹 분석 도구에서 사용자의 행동을 더 잘 이해할 수 있다.

서버 사이드 렌더링 및 코드 분할

쿼리 파라미터를 사용하면 특정 상태에 따라 필요한 데이터를 서버에서 미리 가져오거나 코드 분할을 통해 특정 컴포넌트만 로드할 수 있다. 이는 페이지 로딩 시간을 줄이고 사용자 경험을 향상시키는 데 도움이 된다.

배운 점

위와 같이 여러 가지 장점들을 살펴봤는데 가장 인상 깊었던 부분은 서버 사이드 렌더링과 검색 엔진 최적화를 개선시킬 수 있다는 것이다. 이전 글에서도 최적화에 관심이 있다고 작성한 부분이 있다. 특히 검색 엔진 최적화는 한 번도 생각해 본 적이 없었기에 더 좋은 경험인 것 같다. 재사용성을 고려한다고 하더라도 페이지 또는 컴포넌트 관리에 유용하다.

'React' 카테고리의 다른 글

Context API 쓰다가 프로젝트 터질 뻔 했습니다 (feat. Zustand)  (0) 2025.03.27
Lazy Loading  (1) 2024.10.05
Tennis_Coder