API/Functions/useSearchParams

김동현·2026년 3월 8일

next.js 공식문서 번역

목록 보기
72/79

useSearchParams는 현재 URL의 쿼리 스트링(query string)을 읽을 수 있게 해주는 클라이언트 컴포넌트(Client Component) 훅이에요.

👨‍🏫 강사의 부연 설명: > 쿼리 스트링이 뭐냐고요? 웹사이트 주소를 보면 https://example.com/products?search=shoes&color=black 처럼 ? 뒤에 붙는 값들을 보신 적 있죠? 여기서 search=shoes&color=black 부분이 바로 쿼리 스트링입니다. 사용자가 검색한 키워드나 필터링 옵션 같은 데이터를 URL을 통해 전달할 때 아주 많이 쓰인답니다.

useSearchParams는 브라우저에 내장된 URLSearchParams 인터페이스의 읽기 전용(read-only) 버전을 반환해요.

'use client'

import { useSearchParams } from 'next/navigation'

export default function SearchBar() {
  const searchParams = useSearchParams()

  const search = searchParams.get('search')

  // URL -> `/dashboard?search=my-project`
  // `search` -> 'my-project'
  return <>Search: {search}</>
}
'use client'

import { useSearchParams } from 'next/navigation'

export default function SearchBar() {
  const searchParams = useSearchParams()

  const search = searchParams.get('search')

  // URL -> `/dashboard?search=my-project`
  // `search` -> 'my-project'
  return <>Search: {search}</>
}

Parameters (파라미터)

const searchParams = useSearchParams()

useSearchParams는 호출할 때 아무런 파라미터도 받지 않아요. 빈 괄호로 편하게 호출하시면 됩니다.

Returns (반환값)

useSearchParamsURLSearchParams 인터페이스의 읽기 전용 버전을 반환하는데요, 이 안에는 URL의 쿼리 스트링을 읽어올 수 있는 유용한 유틸리티 메서드들이 포함되어 있어요.

  • URLSearchParams.get(): 검색 파라미터와 연결된 첫 번째 값을 반환합니다. 예를 들어볼게요:

    URLsearchParams.get("a")
    /dashboard?a=1'1'
    /dashboard?a=''
    /dashboard?b=3null
    /dashboard?a=1&a=2'1' - 모든 값을 가져오고 싶다면 getAll()을 사용하세요
  • URLSearchParams.has(): 주어진 파라미터가 URL에 존재하는지를 불리언(boolean, true/false) 값으로 반환해 줘요. 예시를 볼까요?

    URLsearchParams.has("a")
    /dashboard?a=1true
    /dashboard?b=3false
  • 이 외에도 getAll(), keys(), values(), entries(), forEach(), toString()과 같은 URLSearchParams의 다른 읽기 전용 메서드들에 대해 더 알아보시는 것도 좋습니다.

알아두면 좋은 점 (Good to know):

  • useSearchParams클라이언트 컴포넌트(Client Component) 전용 훅이에요. 부분 렌더링(partial rendering) 과정에서 예전(stale) 값이 남는 것을 방지하기 위해 서버 컴포넌트(Server Components)에서는 지원되지 않습니다.
  • 만약 서버 컴포넌트에서 검색 파라미터(search params)를 바탕으로 데이터를 가져오고 싶다면, 해당 Page 컴포넌트의 searchParams prop을 읽어오는 것이 훨씬 좋은 선택입니다. 그렇게 읽어온 값을 해당 페이지 내의 다른 컴포넌트들(서버든 클라이언트든 상관없이)에게 prop으로 넘겨주면 되니까요.
  • 어플리케이션에 예전 방식인 /pages 디렉토리가 포함되어 있다면, useSearchParamsReadonlyURLSearchParams | null을 반환할 수 있어요. 여기서 null 값은 마이그레이션(이전) 기간 동안의 호환성을 위한 것인데요, getServerSideProps를 사용하지 않는 페이지를 사전 렌더링(pre-rendering)할 때는 검색 파라미터를 알 수 없기 때문이랍니다.

Behavior (동작 방식)

이 부분이 정말 중요합니다! Next.js가 페이지를 정적으로 렌더링할지, 동적으로 렌더링할지에 따라 이 훅의 동작이 달라지거든요. 집중해서 봐주세요!

Static Rendering (정적 렌더링)

만약 라우트가 정적으로 렌더링(statically rendered) 된다면, useSearchParams를 호출할 때 해당 컴포넌트부터 가장 가까운 Suspense 경계(boundary)까지의 클라이언트 컴포넌트 트리가 클라이언트 측에서 렌더링(client-side rendered) 되도록 유도합니다.

이렇게 하면 라우트의 일부는 정적으로 렌더링하면서도, useSearchParams를 사용하는 동적인 부분만 클라이언트 렌더링으로 분리할 수 있게 되는 거죠.

그래서 useSearchParams를 사용하는 클라이언트 컴포넌트는 반드시 <Suspense/> 경계로 감싸주는 것을 권장해요. 이렇게 하면 그 상위에 있는 모든 클라이언트 컴포넌트들이 정상적으로 정적 렌더링되어 초기 HTML의 일부로 전송될 수 있답니다. 예제 보기.

예시 코드를 살펴볼게요:

'use client'

import { useSearchParams } from 'next/navigation'

export default function SearchBar() {
  const searchParams = useSearchParams()

  const search = searchParams.get('search')

  // 이 부분은 정적 렌더링을 사용할 때 서버 측 콘솔에 찍히지 않아요.
  console.log(search)

  return <>Search: {search}</>
}
'use client'

import { useSearchParams } from 'next/navigation'

export default function SearchBar() {
  const searchParams = useSearchParams()

  const search = searchParams.get('search')

  // 이 부분은 정적 렌더링을 사용할 때 서버 측 콘솔에 찍히지 않아요.
  console.log(search)

  return <>Search: {search}</>
}
import { Suspense } from 'react'
import SearchBar from './search-bar'

// Suspense의 fallback으로 전달된 이 컴포넌트는
// 초기 HTML에서 search bar를 대신해서 렌더링됩니다.
// React의 하이드레이션(hydration) 과정 중 값을 사용할 수 있게 되면,
// 이 fallback은 실제 `<SearchBar>` 컴포넌트로 교체됩니다.
function SearchBarFallback() {
  return <>placeholder</>
}

export default function Page() {
  return (
    <>
      <nav>
        <Suspense fallback={<SearchBarFallback />}>
          <SearchBar />
        </Suspense>
      </nav>
      <h1>Dashboard</h1>
    </>
  )
}
import { Suspense } from 'react'
import SearchBar from './search-bar'

// Suspense의 fallback으로 전달된 이 컴포넌트는
// 초기 HTML에서 search bar를 대신해서 렌더링됩니다.
// React의 하이드레이션(hydration) 과정 중 값을 사용할 수 있게 되면,
// 이 fallback은 실제 `<SearchBar>` 컴포넌트로 교체됩니다.
function SearchBarFallback() {
  return <>placeholder</>
}

export default function Page() {
  return (
    <>
      <nav>
        <Suspense fallback={<SearchBarFallback />}>
          <SearchBar />
        </Suspense>
      </nav>
      <h1>Dashboard</h1>
    </>
  )
}

💡 강사의 특급 꿀팁!
실무에서 가장 많이 에러를 내는 부분 중 하나예요! useSearchParams를 썼는데 상위에 <Suspense>가 없다? 그러면 빌드할 때 에러가 나면서 전체 페이지가 최적화되지 못하는 참사가 발생합니다. "나는 쿼리 파라미터만 하나 읽었을 뿐인데 웹사이트가 왜 이렇게 느려졌지?" 하는 상황을 막으려면 꼭 Suspense로 감싸는 습관을 들이세요!

알아두면 좋은 점 (Good to know):

  • 개발 모드(development)에서는 라우트들이 요청이 올 때마다 렌더링되기 때문에(on-demand), useSearchParams가 Suspense를 발생시키지 않아서 Suspense 없이도 그냥 잘 동작하는 것처럼 보일 수 있어요. (이래서 배포할 때 깜짝 놀라는 분들이 많습니다!)
  • 프로덕션 빌드 과정에서는, useSearchParams를 호출하는 클라이언트 컴포넌트가 포함된 정적 페이지(static page)의 경우 반드시 Suspense 경계로 감싸져 있어야 합니다. 그렇지 않으면 Missing Suspense boundary with useSearchParams 에러와 함께 빌드가 실패합니다.
  • 만약 이 라우트를 완전히 동적으로 렌더링하고 싶으시다면, 서버 컴포넌트에서 들어오는 요청을 기다리게 하는 connection 함수를 먼저 사용하는 것을 권장해요. 이렇게 하면 그 아래에 있는 모든 것들이 사전 렌더링(prerendering)에서 제외됩니다. 라우트를 동적으로 만드는 원리에 대해서는 동적 렌더링 가이드(Dynamic Rendering guide)를 참고해보세요.
  • 이미 서버 컴포넌트 Page 내부에 있다면, 훅을 쓰기보단 searchParams prop을 사용해서 그 값을 클라이언트 컴포넌트에 넘겨주는 방식을 고려해보세요. 이게 훨씬 깔끔할 수 있습니다.
  • Page의 searchParams prop을 클라이언트 컴포넌트로 직접 넘긴 다음 React의 use() 훅으로 풀어낼(unwrap) 수도 있어요. 물론 이 방법도 Suspense를 발생시키기 때문에 클라이언트 컴포넌트를 Suspense 경계로 감싸주어야 합니다.

Dynamic Rendering (동적 렌더링)

만약 라우트가 동적으로 렌더링(dynamically rendered) 된다면, 클라이언트 컴포넌트의 초기 서버 렌더링 과정 중 서버 측에서 useSearchParams를 사용할 수 있게 됩니다.

예시를 볼게요:

'use client'

import { useSearchParams } from 'next/navigation'

export default function SearchBar() {
  const searchParams = useSearchParams()

  const search = searchParams.get('search')

  // 이 콘솔 로그는 초기 렌더링 시에는 서버에서 찍히고,
  // 이후 페이지 이동(navigation) 시에는 클라이언트에서 찍히게 됩니다.
  console.log(search)

  return <>Search: {search}</>
}
'use client'

import { useSearchParams } from 'next/navigation'

export default function SearchBar() {
  const searchParams = useSearchParams()

  const search = searchParams.get('search')

  // 이 콘솔 로그는 초기 렌더링 시에는 서버에서 찍히고,
  // 이후 페이지 이동(navigation) 시에는 클라이언트에서 찍히게 됩니다.
  console.log(search)

  return <>Search: {search}</>
}
import { connection } from 'next/server'
import SearchBar from './search-bar'

export default async function Page() {
  await connection()
  return (
    <>
      <nav>
        <SearchBar />
      </nav>
      <h1>Dashboard</h1>
    </>
  )
}
import { connection } from 'next/server'
import SearchBar from './search-bar'

export default async function Page() {
  await connection()
  return (
    <>
      <nav>
        <SearchBar />
      </nav>
      <h1>Dashboard</h1>
    </>
  )
}

알아두면 좋은 점 (Good to know):

  • 예전에는 페이지에 export const dynamic = 'force-dynamic'을 설정해서 강제로 동적 렌더링을 만들곤 했어요. 하지만 이제는 connection()을 사용하는 것을 더 권장합니다. 들어오는 요청(request)과 동적 렌더링을 의미론적으로(semantically) 더 잘 연결해주기 때문이죠.

Server Components (서버 컴포넌트)

이 부분도 초보자분들이 정말 많이 헷갈려하시는 부분이에요. 서버 컴포넌트에서는 쿼리 스트링을 어떻게 다뤄야 할까요?

Pages (페이지)

Pages (서버 컴포넌트)에서 검색 파라미터에 접근하려면, searchParams prop을 사용하면 됩니다. 서버 컴포넌트인 Page는 기본적으로 이 prop을 전달받거든요.

Layouts (레이아웃)

하지만 Pages와 달리, Layouts (서버 컴포넌트)는 searchParams prop을 받지 않습니다. 그 이유는 공통 레이아웃의 경우 라우팅 간에 리렌더링 되지 않기 때문에, 페이지 이동 간에 이전(stale) searchParams 값이 남아서 꼬일 수 있기 때문이에요. 자세한 설명 보기.

👨‍🏫 강사의 부연 설명:
"어? 저는 모든 페이지 상단에 있는 Header(레이아웃에 포함됨)에서 현재 검색어를 보여주고 싶은데요?" 라고 하신다면 방법이 있습니다! 레이아웃 자체에서 파라미터를 읽으려고 하지 마시고,
대신 검색어를 보여줄 자그마한 클라이언트 컴포넌트를 하나 만드세요. 그 안에서 useSearchParams 훅을 사용한 뒤, 그 컴포넌트를 Layout에 가져와서 렌더링하면 됩니다. 클라이언트 컴포넌트는 새로운 searchParams가 들어올 때마다 최신 상태로 알아서 잘 리렌더링 되거든요!

대신에 Page의 searchParams prop을 사용하거나, 클라이언트 측에서 항상 최신 searchParams로 리렌더링되는 클라이언트 컴포넌트 안에서 useSearchParams 훅을 사용하세요.


Examples (예제)

Updating searchParams (searchParams 업데이트하기)

읽기만 하는 게 아니라 URL의 쿼리 스트링을 바꾸고 싶다면 어떻게 해야 할까요? useRouterLink를 사용해서 새로운 searchParams를 설정할 수 있습니다. 페이지 이동(navigation)이 실행되고 나면, 현재의 page.js는 업데이트된 searchParams prop을 새로 전달받게 됩니다.

👨‍🏫 강사의 코드 리딩 팁!
아래 코드를 보면 createQueryString 이라는 함수를 직접 만들어서 쓰고 있죠? 단순히 문자열 더하기("?sort=" + value)를 하지 않고 URLSearchParams 객체를 활용하면, 기존의 다른 파라미터들(예: ?page=2)은 그대로 유지하면서 내가 원하는 파라미터만 쏙쏙 추가하거나 변경할 수 있어서 매우 안전하고 편리한 패턴입니다. 무조건 외워두세요!

'use client'

export default function ExampleClientComponent() {
  const router = useRouter()
  const pathname = usePathname()
  const searchParams = useSearchParams()

  // 현재의 searchParams와 새로 제공된 key/value 쌍을 병합해서
  // 새로운 searchParams 문자열을 만들어내는 함수입니다.
  const createQueryString = useCallback(
    (name: string, value: string) => {
      const params = new URLSearchParams(searchParams.toString())
      params.set(name, value)

      return params.toString()
    },
    [searchParams]
  )

  return (
    <>
      <p>Sort By</p>

      {/* useRouter를 사용하는 방법 */}
      <button
        onClick={() => {
          // <pathname>?sort=asc 형태로 이동
          router.push(pathname + '?' + createQueryString('sort', 'asc'))
        }}
      >
        ASC
      </button>

      {/* <Link> 컴포넌트를 사용하는 방법 */}
      <Link
        href={
          // <pathname>?sort=desc 형태로 이동
          pathname + '?' + createQueryString('sort', 'desc')
        }
      >
        DESC
      </Link>
    </>
  )
}
'use client'

export default function ExampleClientComponent() {
  const router = useRouter()
  const pathname = usePathname()
  const searchParams = useSearchParams()

  // 현재의 searchParams와 새로 제공된 key/value 쌍을 병합해서
  // 새로운 searchParams 문자열을 만들어내는 함수입니다.
  const createQueryString = useCallback(
    (name, value) => {
      const params = new URLSearchParams(searchParams)
      params.set(name, value)

      return params.toString()
    },
    [searchParams]
  )

  return (
    <>
      <p>Sort By</p>

      {/* useRouter를 사용하는 방법 */}
      <button
        onClick={() => {
          // <pathname>?sort=asc 형태로 이동
          router.push(pathname + '?' + createQueryString('sort', 'asc'))
        }}
      >
        ASC
      </button>

      {/* <Link> 컴포넌트를 사용하는 방법 */}
      <Link
        href={
          // <pathname>?sort=desc 형태로 이동
          pathname + '?' + createQueryString('sort', 'desc')
        }
      >
        DESC
      </Link>
    </>
  )
}

Version History (버전 히스토리)

VersionChanges (변경 사항)
v13.0.0useSearchParams 훅이 처음 도입되었습니다.

모든 문서의 구조적 개요를 보시려면 https://nextjs.org/docs/sitemap.md 를 확인해주세요.

사용 가능한 모든 문서의 전체 인덱스를 보시려면 https://nextjs.org/docs/llms.txt 를 확인해주세요.


자, 이렇게 useSearchParams에 대한 공식문서 번역과 부가 설명을 모두 마쳤습니다! 클라이언트 컴포넌트에서만 사용해야 한다는 점과 Suspense로 꼭 감싸줘야 한다는 사실만 잘 기억하셔도 큰 에러들을 피하실 수 있을 거예요.

혹시 이 문서를 읽고 나서 직접 코드를 작성해 보시다 막히는 부분이 생기면 언제든 질문해 주실래요? 제가 실무에서 어떻게 해결하는지 더 자세히 알려드릴게요!

profile
프론트에_가까운_풀스택_개발자

0개의 댓글