nextjs searchParams promise 사용법: 개념과 실무 적용 가이드
nextjs searchParams promise 사용법을 실무 관점에서 정리합니다. 개념, 코드 예제, 비교표, 체크리스트와 보안·성능 주의점을 포함해 바로 적용 가능한 단계별 가이드를 제공합니다.
nextjs searchParams promise 사용법이 필요한 상황
nextjs searchParams promise 사용법을 알고 있으면 App Router 환경에서 쿼리 파라미터를 안전하고 일관되게 읽고 비동기 흐름과 결합할 수 있습니다. 이 글에서는 개념 설명, 서버·클라이언트 예제, 비교표, 실무 체크리스트, 보안/성능 주의점까지 정리해 바로 적용할 수 있게 안내합니다.
nextjs searchParams promise 사용법 핵심 개념
Next.js App Router에서 route handler나 서버/서버 컴포넌트는 searchParams 객체(또는 URLSearchParams)를 통해 쿼리를 읽습니다. 클라이언트 컴포넌트에서는 useSearchParams 훅을 사용합니다. "promise" 맥락은 다음 두 가지 상황에서 중요합니다:
- 서버 측에서 쿼리를 읽고 그에 따라 비동기 데이터(예: DB, API)를 await해야 할 때
- 클라이언트 라우팅 중 쿼리 변경을 기반으로 비동기 작업을 트리거할 때
핵심 포인트:
- 서버 컴포넌트는 sync하게 props로 searchParams를 받고, 내부에서 async 작업을 자유롭게 await할 수 있습니다.
- 클라이언트 컴포넌트는 useSearchParams로 읽되, 쿼리 변경에 대응해 useEffect에서 비동기 처리를 수행합니다.
- 직접 URLSearchParams 생성은 안전하지만, 사용자 입력은 반드시 검증해야 합니다.
nextjs searchParams promise 사용법 실무 예제
아래 예제는 App Router의 서버 컴포넌트에서 searchParams를 받고, 이 값으로 API를 호출해 데이터를 렌더링하는 패턴입니다.
// app/products/page.tsx (Server Component) import React from 'react'; type Props = { searchParams?: { [key: string]: string | string[] | undefined } }; export default async function ProductsPage({ searchParams }: Props) { const query = (searchParams?.q as string) ?? ''; const page = Number(searchParams?.page ?? 1); // 비동기 API 호출을 await(여기서 promise를 사용) const products = await fetchProducts({ q: query, page }); return ( <main> <h1>Products</h1> <p>검색어: {query}</p> <ul> {products.map(p => ( <li key={p.id}>{p.name}</li> ))} </ul> </main> ); } async function fetchProducts({ q, page }: { q: string; page: number }) { const res = await fetch(`https://api.example.com/products?q=${encodeURIComponent(q)}&page=${page}`); if (!res.ok) throw new Error('API error'); return res.json(); }
클라이언트에서 쿼리 변경을 감지해 비동기 작업을 하는 예:
// components/SearchClient.tsx (Client Component) 'use client'; import React, { useEffect, useState } from 'react'; import { useSearchParams } from 'next/navigation'; export default function SearchClient() { const searchParams = useSearchParams(); const q = searchParams.get('q') ?? ''; const [results, setResults] = useState<any[]>([]); useEffect(() => { let mounted = true; const controller = new AbortController(); async function load() { if (!q) { setResults([]); return; } try { const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`, { signal: controller.signal }); if (!res.ok) throw new Error('search failed'); const data = await res.json(); if (mounted) setResults(data.items); } catch (e) { if (mounted) setResults([]); } } load(); return () => { mounted = false; controller.abort(); }; }, [q]); return ( <div> <p>검색: {q}</p> <ul>{results.map(r => <li key={r.id}>{r.title}</li>)}</ul> </div> ); }
nextjs searchParams 비교: 서버 vs 클라이언트
아래 표는 서버 컴포넌트와 클라이언트 컴포넌트에서 searchParams를 다루는 비교 기준입니다.
| 구분 | 항목1 | 항목2 | 항목3 |
|---|---|---|---|
| 내용 | 서버 컴포넌트 (Props) | 클라이언트 컴포넌트 (useSearchParams) | 실무 권장 패턴 |
| 내용 | sync로 전달, 바로 await 가능한 비동기 로직과 결합 | 라우터 훅으로 변화 감지, useEffect로 비동기 처리 | 데이터 페칭은 서버에서, UI/인터랙션은 클라이언트에서 |
언제 쓰면 좋고 언제 피해야 할까
언제 쓰면 좋은가:
- 검색 쿼리나 페이지 번호로 서버 사이드 데이터 페칭을 하고 싶을 때
- 초기 렌더 시 SEO를 고려해 서버에서 쿼리 기반 콘텐츠를 생성할 때
- 쿼리 값으로 서버 사이드 렌더링된 결과를 캐싱/프리패치할 때
언제 피해야 하는가:
- 민감한 토큰(예: API 키, 세션 식별자)을 쿼리에 담아 클라이언트/URL로 전달할 때
- 대용량의 정보를 쿼리 스트링으로 전달해야 할 때(POST 바디 사용 권장)
- 클라이언트에서만 동작하는 빠른 인터랙션을 서버 왕복으로 처리하려 할 때
실무 체크리스트
- searchParams 값은 항상 존재 여부와 타입을 검증했는가?
- 사용자 입력은 encodeURIComponent로 인코딩했는가?
- 쿼리 기반 데이터 요청은 서버에서 처리 가능하면 서버로 옮겼는가?
- 클라이언트에서 fetch 요청에는 AbortController를 적용했는가?
- URL에 민감한 정보가 포함되어 있지 않은가?
- 캐시 정책(immutable, revalidate 등)을 명확히 설정했는가?
성능·보안·운영 주의점
성능:
- 쿼리 값이 자주 변경되면 불필요한 네트워크 요청이 발생할 수 있으니 디바운스나 캐싱 전략을 적용하세요.
- 서버 측에서 쿼리에 따라 복잡한 DB 조회가 필요한 경우, 인덱스와 쿼리 계획을 재검토하세요.
보안:
- 쿼리는 브라우저 히스토리에 남고 로그에 기록될 수 있으므로 민감 정보 사용 금지.
- 검색어를 그대로 DB 쿼리에 삽입하지 말고, 파라미터 바인딩 또는 검증을 필수로 하세요.
운영:
- 쿼리 값에 따른 캐시 키 설계(예: q, page 포함)를 명확히 하여 캐시 적중률을 확보하세요.
- 모니터링: 쿼리 기반 엔드포인트의 에러율과 응답 시간을 분리해 추적하세요.
자주 묻는 질문
Q1: 서버 컴포넌트에서 searchParams를 바로 수정할 수 있나요? A1: 서버 컴포넌트에 전달되는 searchParams는 읽기용으로 다루고, URL을 변경하려면 클라이언트 측에서 router.push나 링크를 사용하세요.
Q2: useSearchParams는 SSR에서 동작하나요? A2: useSearchParams는 클라이언트 훅입니다. SSR 초기 렌더에서는 서버 컴포넌트의 searchParams를 사용하고, 클라이언트 인터랙션은 useSearchParams로 처리하세요.
Q3: searchParams를 사용해 페이징을 구현할 때 성능 팁이 있나요? A3: 페이지 번호를 쿼리로 두는 것은 일반적입니다. 성능을 위해 페이지별 캐시, DB 옵티마이징(인덱스, LIMIT/OFFSET 대신 커서 기반 페이징)과 함께 사용하세요.
Q4: 쿼리 값에 배열이 포함된 경우 어떻게 처리하나요? A4: Next.js의 searchParams는 문자열이나 문자열 배열을 받을 수 있습니다. 서버에서 Array인지 체크 후 적절히 처리하세요.
정리
-
nextjs searchParams promise 사용법을 통해 서버/클라이언트 각각의 역할을 명확히 하고 비동기 처리를 안전하게 결합하세요.
-
서버 컴포넌트는 초기 데이터 페칭, 클라이언트는 UI 반응성에 집중하세요.
-
쿼리 값 검증, 인코딩, 민감 정보 금지, 캐시/모니터링 정책을 수립하세요.
-
핵심 요약:
- 서버에서 쿼리 기반 데이터는 await로 안전하게 처리
- 클라이언트는 useSearchParams + useEffect로 비동기 로직 수행
- 보안 및 성능 체크리스트를 항상 따르기