기본 검색 매개변수 설정 방법
스키마 검증을 사용해 TanStack Router 라우트에 타입 안전하고 프로덕션에 적합한 검색 매개변수를 추가하는 방법을 알아봅니다. 이 가이드에서는 검색 매개변수 검증의 기본 사항, 값 읽기, 표준 스키마 호환 검증 라이브러리를 사용한 다양한 데이터 타입 처리를 다룹니다.
빠른 시작
스키마 검증으로 검색 매개변수를 설정합니다(프로덕션에 권장).
import { createFileRoute } from '@tanstack/react-router'
import { z } from 'zod'
const productSearchSchema = z.object({
page: z.number().default(1).catch(1),
category: z.string().default('all').catch('all'),
showSale: z.boolean().default(false).catch(false),
})
export const Route = createFileRoute('/products')({
validateSearch: productSearchSchema,
component: ProductsPage,
})
function ProductsPage() {
const { page, category, showSale } = Route.useSearch()
return (
<div>
<h1>Products</h1>
<p>Page: {page}</p>
<p>Category: {category}</p>
<p>Show Sale Items: {showSale ? 'Yes' : 'No'}</p>
</div>
)
}
검색 매개변수에 스키마 검증을 사용하는 이유
프로덕션에서의 이점:
- 타입 안전성: TypeScript 자동 추론
- 런타임 검증: 유효하지 않은 URL 매개변수를 안전하게 감지
- 기본값: 누락된 매개변수에 대한 대체값 처리
- 오류 처리: 내장 검증 오류 관리
- 유지 보수성: 명확하고 선언적인 스키마 정의
검증 라이브러리 설정
TanStack Router는 모든 표준 스키마 호환 검증 라이브러리를 지원합니다. 이 가이드에서는 예시에 Zod를 사용하지만 어떤 검증 라이브러리든 사용할 수 있습니다.
Zod v4 사용:
import { z } from 'zod'
const searchSchema = z.object({
page: z.number().default(1),
category: z.string().default('all').catch('all'),
})
export const Route = createFileRoute('/products')({
validateSearch: searchSchema,
component: ProductsPage,
})
Zod v3 사용:
npm install zod @tanstack/zod-adapter
import { zodValidator, fallback } from '@tanstack/zod-adapter'
import { z } from 'zod'
const searchSchema = z.object({
page: fallback(z.number(), 1).default(1),
category: fallback(z.string(), 'all').default('all'),
})
export const Route = createFileRoute('/products')({
validateSearch: zodValidator(searchSchema),
component: ProductsPage,
})
검증 라이브러리의 자세한 비교와 고급 검증 패턴은 다음을 참고하세요: 스키마로 검색 매개변수 검증
Zod를 사용한 단계별 설정
이 가이드의 나머지 예시에서는 Zod v4를 사용하지만 패턴은 모든 검증 라이브러리에 적용됩니다.
1단계: 검색 스키마 정의
먼저 라우트에 필요한 검색 매개변수를 확인합니다.
import { z } from 'zod'
const shopSearchSchema = z.object({
// Pagination
page: z.number().default(1),
limit: z.number().default(20),
// Filtering
category: z.string().default('all'),
minPrice: z.number().default(0),
maxPrice: z.number().default(1000),
// Settings
sort: z.enum(['name', 'price', 'date']).default('name'),
ascending: z.boolean().default(true),
// Optional parameters
searchTerm: z.string().optional(),
showOnlyInStock: z.boolean().default(false),
})
type ShopSearch = z.infer<typeof shopSearchSchema>
2단계: 라우트에 스키마 검증 추가
스키마를 라우트에 연결합니다.
export const Route = createFileRoute('/shop')({
validateSearch: shopSearchSchema,
component: ShopPage,
})
3단계: 컴포넌트에서 검색 매개변수 읽기
라우트의 useSearch() 훅을 사용해 검증되고 타입 지정된 검색 매개변수에 액세스합니다.
function ShopPage() {
const searchParams = Route.useSearch()
// All properties are fully type-safe and validated
const {
page,
limit,
category,
sort,
ascending,
searchTerm,
showOnlyInStock,
} = searchParams
return (
<div>
<h1>Shop - Page {page}</h1>
<div>Category: {category}</div>
<div>
Sort: {sort} ({ascending ? 'ascending' : 'descending'})
</div>
<div>Items per page: {limit}</div>
<div>In stock only: {showOnlyInStock ? 'Yes' : 'No'}</div>
{searchTerm && <div>Search: "{searchTerm}"</div>}
</div>
)
}
일반적인 검색 매개변수 패턴
제약 조건이 있는 페이지네이션
const paginationSchema = z.object({
page: z.number().min(1).default(1),
limit: z.number().min(10).max(100).default(20),
})
export const Route = createFileRoute('/posts')({
validateSearch: paginationSchema,
component: PostsPage,
})
function PostsPage() {
const { page, limit } = Route.useSearch()
// Calculate offset for API calls
const offset = (page - 1) * limit
return (
<div>
<h1>Posts (Page {page})</h1>
<p>Showing {limit} posts per page</p>
<p>Offset: {offset}</p>
{/* Render posts... */}
</div>
)
}
기본값을 사용하는 열거형 검증
const catalogSchema = z.object({
sort: z.enum(['name', 'date', 'price']).default('name'),
category: z.enum(['electronics', 'clothing', 'books', 'all']).default('all'),
ascending: z.boolean().default(true),
})
export const Route = createFileRoute('/catalog')({
validateSearch: catalogSchema,
component: CatalogPage,
})
복잡한 데이터 타입
const dashboardSchema = z.object({
// Numbers with validation
userId: z.number().positive().default(1),
refreshInterval: z.number().min(1000).max(60000).default(5000),
// Strings with validation
theme: z.enum(['light', 'dark']).default('light'),
timezone: z.string().optional(),
// Arrays with validation
selectedIds: z.number().array().default([]),
tags: z.string().array().default([]),
// Objects with validation
filters: z
.object({
status: z.enum(['active', 'inactive']).optional(),
type: z.string().optional(),
})
.prefault({}),
})
날짜 및 고급 타입
const reportSchema = z.object({
startDate: z.string().pipe(z.coerce.date()).optional(),
endDate: z.string().pipe(z.coerce.date()).optional(),
format: z.enum(['pdf', 'csv', 'excel']).default('pdf').catch('pdf'),
includeCharts: z.boolean().default(true),
})
컴포넌트 외부에서 검색 매개변수 읽기
getRouteApi 사용
코드 분할된 컴포넌트나 별도 파일에서는 다음과 같이 합니다.
// components/ProductFilters.tsx
import { getRouteApi } from '@tanstack/react-router'
const routeApi = getRouteApi('/products')
export function ProductFilters() {
const { category, sort, showSale } = routeApi.useSearch()
return (
<div>
<select value={category}>
<option value="all">All Categories</option>
<option value="electronics">Electronics</option>
<option value="clothing">Clothing</option>
</select>
{/* More filters... */}
</div>
)
}
from과 함께 useSearch 사용
import { useSearch } from '@tanstack/react-router'
function GenericSearchDisplay() {
const search = useSearch({ from: '/products' })
return <div>Current filters: {JSON.stringify(search, null, 2)}</div>
}
수동 검증(프리미티브 이해)
프로덕션에는 스키마 검증을 권장하지만, 수동 검증을 이해하면 검색 매개변수가 내부적으로 작동하는 방식을 파악하는 데 도움이 됩니다.
// Educational example - use schema validation for production
export const Route = createFileRoute('/example')({
validateSearch: (search: Record<string, unknown>) => ({
// Numbers need coercion from URL strings
page: Number(search.page) || 1,
// Strings can be cast with defaults
category: (search.category as string) || 'all',
// Booleans: TanStack Router auto-converts "true"/"false" to booleans
showSale: Boolean(search.showSale),
// Arrays need JSON parsing validation
selectedIds: Array.isArray(search.selectedIds)
? search.selectedIds.map(Number).filter(Boolean)
: [],
}),
component: ExamplePage,
})
프로덕션 체크리스트
- 타입 안전성과 런타임 검증을 위해 검증 라이브러리로 스키마 검증 사용
- 안전한 오류 처리를 위해 대체값 추가
- 선택적 매개변수에 기본값 설정
- 검증 라이브러리의 내장 검증기로 제약 조건 검증
- 선택적 매개변수 적절히 처리
- 올바르게 스키마를 설정하면 타입 추론이 자동으로 작동
- 검증 실패를 처리하도록 오류 경계 구성
일반적인 문제
문제: 검색 매개변수로 TypeScript 오류 발생
원인: 스키마 정의가 없거나 올바르지 않습니다.
해결 방법: 스키마가 모든 검색 매개변수를 포함하고 올바른 타입을 사용하는지 확인합니다.
// ❌ Missing schema or incorrect types
export const Route = createFileRoute('/page')({
component: MyPage,
})
// ✅ Complete schema with proper validation
const searchSchema = z.object({
page: z.number().default(1).catch(1),
category: z.string().default('all').catch('all'),
})
export const Route = createFileRoute('/page')({
validateSearch: searchSchema,
component: MyPage,
})
문제: 유효하지 않은 URL 매개변수로 앱이 중단됨
원인: 오류 상황에 대한 대체값 처리를 사용하지 않았습니다.
해결 방법: 안전한 기본값을 제공하도록 대체값을 사용합니다.
// ❌ No fallback handling
const schema = z.object({
page: z.number().default(1), // Will throw on invalid input
})
// ✅ Graceful fallback handling
const schema = z.object({
page: z.number().default(1).catch(1), // Safe fallback to 1
})
문제: TypeScript에서 선택적 매개변수가 필수로 처리됨
원인: .default()를 사용하면 탐색에서 매개변수가 필수가 됩니다.
해결 방법: 실제로 선택적인 매개변수에는 .optional()을 사용합니다.
const schema = z.object({
// Required with default (navigation can omit, but always present in component)
page: z.number().default(1).catch(1),
// Truly optional (can be undefined in component)
searchTerm: z.string().optional(),
})
문제: 복잡한 객체가 검증되지 않음
원인: 중첩 객체에는 명시적인 스키마 정의가 필요합니다.
해결 방법: 완전한 중첩 스키마를 정의합니다.
const schema = z.object({
filters: z
.object({
status: z.enum(['active', 'inactive']).optional(),
tags: z.string().array().optional(),
dateRange: z
.object({
start: z.string().pipe(z.coerce.date()),
end: z.string().pipe(z.coerce.date()),
})
.optional(),
})
.prefault({})
.catch({}),
})
일반적인 다음 단계
기본 검색 매개변수를 설정한 후 다음 작업을 고려할 수 있습니다.
- 스키마로 검색 매개변수 검증 - Zod, Valibot 또는 ArkType로 견고한 검증 추가
- 검색 매개변수로 탐색 - Link와 탐색으로 검색 매개변수를 업데이트하는 방법
- 배열, 객체, 날짜 다루기 - 배열, 객체, 날짜 및 중첩 데이터 구조 처리
관련 리소스
- 검증 라이브러리:
- Zod 문서 - 검증 라이브러리 전체 레퍼런스
- Valibot 문서 - 경량 검증 라이브러리
- Yup 문서 - 객체 스키마 검증
- TanStack Router:
- TanStack Zod Adapter - 공식 Zod 어댑터
- TanStack Valibot Adapter - 공식 Valibot 어댑터
- 검색 매개변수 가이드 - 검색 매개변수 종합 문서
- 타입 안전성 가이드 - TanStack Router의 타입 안전성 이해