검색 매개변수
TanStack Query가 React 및 Solid 애플리케이션의 서버 상태 처리를 쉽게 만든 것처럼, TanStack Router는 애플리케이션에서 URL 검색 매개변수의 강력한 기능을 활용할 수 있도록 합니다.
🧠 IE11과 같은 매우 오래된 브라우저를 사용한다면
URLSearchParams용 폴리필을 사용해야 할 수 있습니다.
URLSearchParams만 사용하면 안 되나요?
최근 "플랫폼을 사용하세요"라는 말을 많이 들었고 대부분 이에 동의한다는 점을 이해합니다. 하지만 더 고급 사용 사례에서는 플랫폼이 부족한 부분도 있다는 점을 인식하는 것이 중요하며, URLSearchParams가 이러한 경우 중 하나라고 생각합니다.
기존 검색 매개변수 API는 일반적으로 다음과 같이 가정합니다.
- 검색 매개변수는 항상 문자열입니다.
- 검색 매개변수는 대부분 평면적입니다.
URLSearchParams를 사용한 직렬화와 역직렬화로 충분합니다(스포일러: 그렇지 않습니다).- 검색 매개변수 수정은 URL의 pathname과 밀접하게 연결되어 있으므로 pathname이 변경되지 않더라도 함께 업데이트해야 합니다.
하지만 실제 상황은 이러한 가정과 매우 다릅니다.
- 검색 매개변수는 애플리케이션 상태를 나타내므로 다른 상태 관리자와 동일한 DX를 기대하게 됩니다. 즉 원시 값 타입을 구분하고 중첩 배열과 객체 같은 복잡한 데이터 구조를 효율적으로 저장하고 조작할 수 있어야 합니다.
- 서로 다른 트레이드오프를 가진 상태 직렬화 및 역직렬화 방법은 다양합니다. 애플리케이션에 가장 적합한 방법을 선택할 수 있어야 하며, 최소한
URLSearchParams보다 나은 기본값을 사용할 수 있어야 합니다. - 불변성 및 구조적 공유. URL 검색 매개변수를 문자열화하고 파싱할 때마다 새로운 파싱이 고유한 메모리 참조를 가진 완전히 새로운 데이터 구조를 만들기 때문에 참조 무결성과 객체 동일성이 손실됩니다. 수명 동안 적절히 관리하지 않으면 이러한 지속적인 직렬화와 파싱으로 예기치 않고 바람직하지 않은 성능 문제가 발생할 수 있습니다. 특히 불변성을 통해 반응성을 추적하는 React 같은 프레임워크나, 역직렬화된 데이터 소스의 변경을 감지할 때 일반적으로 조정에 의존하는 Solid에서 문제가 될 수 있습니다.
- 검색 매개변수는 URL의 중요한 부분이지만 URL의 pathname과 독립적으로 자주 변경됩니다. 예를 들어 사용자가 URL의 pathname을 건드리지 않고 페이지가 매겨진 목록의 페이지 번호를 변경할 수 있습니다.
검색 매개변수, "원조" 상태 관리자
URL에서 ?page=3 또는 ?filter-name=tanner와 같은 검색 매개변수를 본 적이 있을 것입니다. 이는 URL 내부에 존재하는 전역 상태의 한 형태임이 분명합니다. URL에 특정 상태를 저장하면 다음과 같은 이점이 있습니다.
- 사용자는 다음을 할 수 있어야 합니다.
- Cmd/Ctrl을 누른 채 클릭해 링크를 새 탭에서 열고 예상한 상태를 안정적으로 확인합니다.
- 애플리케이션의 링크를 북마크하고 다른 사람과 공유해 링크를 복사했을 때와 정확히 동일한 상태를 확인할 수 있도록 합니다.
- 상태를 잃지 않고 앱을 새로 고치거나 페이지 사이를 앞뒤로 이동합니다.
- 개발자는 다음을 쉽게 할 수 있어야 합니다.
- 다른 상태 관리자와 동일한 훌륭한 DX로 URL의 상태를 추가, 제거 또는 수정합니다.
- URL에서 가져온 검색 매개변수를 애플리케이션이 안전하게 사용할 수 있는 형식과 타입으로 쉽게 검증합니다.
- 기본 직렬화 형식을 걱정하지 않고 검색 매개변수를 읽고 씁니다.
JSON 우선 검색 매개변수
이를 위해 TanStack Router에 내장된 첫 단계는 URL의 검색 문자열을 구조화된 JSON으로 자동 변환하는 강력한 검색 매개변수 파서입니다. 즉 검색 매개변수에 JSON으로 직렬화할 수 있는 모든 데이터 구조를 저장할 수 있으며 JSON으로 파싱되고 직렬화됩니다. 이는 배열과 유사한 구조 및 중첩 데이터 지원이 제한적인 URLSearchParams에 비해 크게 개선된 방식입니다.
예를 들어 다음 라우트로 이동하면:
const link = (
<Link
to="/shop"
search={{
pageIndex: 3,
includeCategories: ['electronics', 'gifts'],
sortBy: 'price',
desc: true,
}}
/>
)
다음 URL이 생성됩니다.
/shop?pageIndex=3&includeCategories=%5B%22electronics%22%2C%22gifts%22%5D&sortBy=price&desc=true
이 URL을 파싱하면 검색 매개변수가 다음 JSON으로 정확히 다시 변환됩니다.
{
"pageIndex": 3,
"includeCategories": ["electronics", "gifts"],
"sortBy": "price",
"desc": true
}
여기에는 몇 가지 동작이 있습니다.
- 검색 매개변수의 첫 번째 수준은
URLSearchParams와 마찬가지로 평면적인 문자열 기반입니다. - 문자열이 아닌 첫 번째 수준의 값은 실제 숫자와 불리언으로 정확하게 보존됩니다.
- 중첩 데이터 구조는 URL에서 안전하게 사용할 수 있는 JSON 문자열로 자동 변환됩니다.
🧠 다른 도구가 검색 매개변수를 항상 평면적이고 문자열 기반이라고 가정하는 경우가 많기 때문에 첫 번째 수준에서는 URLSearchParam 규격을 준수하도록 유지했습니다. 따라서 TanStack Router가 중첩 검색 매개변수를 JSON으로 관리하더라도 다른 도구에서 URL에 쓰고 첫 번째 수준의 매개변수를 정상적으로 읽을 수 있습니다.
검색 매개변수 검증 및 타입 지정
TanStack Router가 검색 매개변수를 신뢰할 수 있는 JSON으로 파싱할 수 있더라도, 검색 매개변수는 궁극적으로 사용자에게 노출된 원시 텍스트 입력에서 비롯됩니다. 다른 직렬화 경계와 마찬가지로 검색 매개변수를 사용하기 전에 애플리케이션이 신뢰하고 의존할 수 있는 형식으로 검증해야 합니다.
검증과 TypeScript 사용
TanStack Router는 검색 매개변수를 검증하고 타입을 지정하는 편리한 API를 제공합니다. 이 작업은 Route의 validateSearch 옵션에서 시작합니다.
type ProductSearchSortOptions = 'newest' | 'oldest' | 'price'
type ProductSearch = {
page: number
filter: string
sort: ProductSearchSortOptions
}
export const Route = createFileRoute('/shop/products')({
validateSearch: (search: Record<string, unknown>): ProductSearch => {
// validate and parse the search params into a typed state
return {
page: Number(search?.page ?? 1),
filter: (search.filter as string) || '',
sort: (search.sort as ProductSearchSortOptions) || 'newest',
}
},
})
위 예시에서는 Route의 검색 매개변수를 검증하고 타입이 지정된 ProductSearch 객체를 반환합니다. 이렇게 타입이 지정된 객체는 이 라우트의 다른 옵션과 모든 자식 라우트에서도 사용할 수 있습니다!
검색 매개변수 검증
validateSearch 옵션은 JSON으로 파싱되었지만 검증되지 않은 검색 매개변수를 Record<string, unknown>으로 받아 원하는 타입의 객체를 반환하는 함수입니다. 잘못되었거나 예상하지 못한 검색 매개변수에 합리적인 대체값을 제공하면 사용자 경험이 중단되지 않으므로 일반적으로 가장 좋습니다.
예시는 다음과 같습니다.
type ProductSearchSortOptions = 'newest' | 'oldest' | 'price'
type ProductSearch = {
page: number
filter: string
sort: ProductSearchSortOptions
}
export const Route = createFileRoute('/shop/products')({
validateSearch: (search: Record<string, unknown>): ProductSearch => {
// validate and parse the search params into a typed state
return {
page: Number(search?.page ?? 1),
filter: (search.filter as string) || '',
sort: (search.sort as ProductSearchSortOptions) || 'newest',
}
},
})
Zod 라이브러리를 사용해 검색 매개변수를 한 단계에서 검증하고 타입을 지정하는 예시입니다(원하는 검증 라이브러리를 자유롭게 사용해도 됩니다).
import { z } from 'zod'
const productSearchSchema = z.object({
page: z.number().catch(1),
filter: z.string().catch(''),
sort: z.enum(['newest', 'oldest', 'price']).catch('newest'),
})
type ProductSearch = z.infer<typeof productSearchSchema>
export const Route = createFileRoute('/shop/products')({
validateSearch: (search) => productSearchSchema.parse(search),
})
validateSearch는 parse 속성이 있는 객체도 허용하므로 다음과 같이 줄일 수 있습니다.
validateSearch: productSearchSchema
위 예시에서는 사용자에게 오류를 표시하지 않기 위해 Zod의 .default() 대신 .catch() 수정자를 사용했습니다. 검색 매개변수가 잘못되었다고 해서 큰 오류 메시지를 표시하기 위해 앱 사용을 중단하고 싶지는 않을 것이기 때문입니다. 다만 오류 메시지를 표시하려는 경우도 있을 수 있습니다. 이 경우 .catch() 대신 .default()를 사용할 수 있습니다.
이 동작의 내부 원리는 validateSearch 함수가 오류를 throw하는 데 기반합니다. 오류가 throw되면 라우트의 onError 옵션이 트리거되고(error.routerCode는 VALIDATE_SEARCH로 설정됨) 라우트의 component 대신 errorComponent가 렌더링되므로 원하는 방식으로 검색 매개변수 오류를 처리할 수 있습니다.
어댑터
검색 매개변수를 검증하기 위해 Zod와 같은 라이브러리를 사용할 때 URL에 커밋하기 전에 검색 매개변수를 transform하고 싶을 수 있습니다. 예를 들어 일반적인 zod transform은 default입니다.
import { z } from 'zod'
const productSearchSchema = z.object({
page: z.number().default(1),
filter: z.string().default(''),
sort: z.enum(['newest', 'oldest', 'price']).default('newest'),
})
export const Route = createFileRoute('/shop/products/')({
validateSearch: productSearchSchema,
})
이 라우트로 이동하려고 할 때 search가 필수라는 점이 놀라울 수 있습니다. 다음 Link는 search가 없으므로 타입 오류가 발생합니다.
<Link to="/shop/products" />
검증 라이브러리에는 올바른 input 및 output 타입을 추론하는 어댑터를 사용하는 것이 좋습니다.
Zod
Zod용 어댑터가 제공되며 올바른 input 타입과 output 타입을 전달합니다.
Zod v3에서는 다음과 같습니다.
import { zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'
const productSearchSchema = z.object({
page: z.number().default(1),
filter: z.string().default(''),
sort: z.enum(['newest', 'oldest', 'price']).default('newest'),
})
export const Route = createFileRoute('/shop/products/')({
validateSearch: zodValidator(productSearchSchema),
})
Zod v4에서는 validateSearch에서 스키마를 직접 사용해야 합니다.
import { z } from 'zod'
const productSearchSchema = z.object({
page: z.number().default(1),
filter: z.string().default(''),
sort: z.enum(['newest', 'oldest', 'price']).default('newest'),
})
export const Route = createFileRoute('/shop/products/')({
// With Zod v4, we can use the schema without the adapter
validateSearch: productSearchSchema,
})
여기서 중요한 부분은 다음 Link 사용 시 더 이상 search 매개변수가 필요하지 않다는 점입니다.
<Link to="/shop/products" />
Zod v3에서는 여기서 catch를 사용하면 타입을 재정의해 page, filter, sort가 unknown이 되므로 타입이 손실됩니다. 이 경우에는 타입을 유지하면서 검증에 실패할 때 fallback 값을 제공하는 fallback 제네릭 함수를 제공해 처리합니다.
import { fallback, zodValidator } from '@tanstack/zod-adapter'
import { z } from 'zod'
const productSearchSchema = z.object({
page: fallback(z.number(), 1).default(1),
filter: fallback(z.string(), '').default(''),
sort: fallback(z.enum(['newest', 'oldest', 'price']), 'newest').default(
'newest',
),
})
export const Route = createFileRoute('/shop/products/')({
validateSearch: zodValidator(productSearchSchema),
})
따라서 이 라우트로 이동할 때 search는 선택 사항이며 올바른 타입을 유지합니다.
Zod v4에서는 스키마에서 fallback 대신 catch를 사용할 수 있으며 전체 과정에서 타입 추론을 유지합니다.
권장하지는 않지만 output 타입이 input 타입보다 정확한 경우 input 및 output 타입을 구성할 수도 있습니다.
const productSearchSchema = z.object({
page: fallback(z.number(), 1).default(1),
filter: fallback(z.string(), '').default(''),
sort: fallback(z.enum(['newest', 'oldest', 'price']), 'newest').default(
'newest',
),
})
export const Route = createFileRoute('/shop/products/')({
validateSearch: zodValidator({
schema: productSearchSchema,
input: 'output',
output: 'input',
}),
})
이를 통해 내비게이션에서 추론할 타입과 검색 매개변수를 읽을 때 추론할 타입을 유연하게 선택할 수 있습니다.
Valibot
[!WARNING] Router는 valibot 1.0 패키지가 설치되어 있어야 합니다.
Valibot을 사용할 때는 내비게이션 및 검색 매개변수 읽기에 올바른 input과 output 타입이 사용되도록 하는 어댑터가 필요하지 않습니다. valibot이 Standard Schema를 구현하기 때문입니다.
import * as v from 'valibot'
const productSearchSchema = v.object({
page: v.optional(v.fallback(v.number(), 1), 1),
filter: v.optional(v.fallback(v.string(), ''), ''),
sort: v.optional(
v.fallback(v.picklist(['newest', 'oldest', 'price']), 'newest'),
'newest',
),
})
export const Route = createFileRoute('/shop/products/')({
validateSearch: productSearchSchema,
})
Arktype
[!WARNING] Router는 arktype 2.0-rc 패키지가 설치되어 있어야 합니다.
ArkType을 사용할 때는 내비게이션 및 검색 매개변수 읽기에 올바른 input과 output 타입이 사용되도록 하는 어댑터가 필요하지 않습니다. ArkType이 Standard Schema를 구현하기 때문입니다.
import { type } from 'arktype'
const productSearchSchema = type({
page: 'number = 1',
filter: 'string = ""',
sort: '"newest" | "oldest" | "price" = "newest"',
})
export const Route = createFileRoute('/shop/products/')({
validateSearch: productSearchSchema,
})
Effect/Schema
Effect/Schema을 사용할 때는 내비게이션 및 검색 매개변수 읽기에 올바른 input과 output 타입이 사용되도록 하는 어댑터가 필요하지 않습니다. Effect/Schema가 Standard Schema를 구현하기 때문입니다.
import { Schema as S } from 'effect'
const productSearchSchema = S.standardSchemaV1(
S.Struct({
page: S.NumberFromString.pipe(
S.optional,
S.withDefaults({
constructor: () => 1,
decoding: () => 1,
}),
),
filter: S.String.pipe(
S.optional,
S.withDefaults({
constructor: () => '',
decoding: () => '',
}),
),
sort: S.Literal('newest', 'oldest', 'price').pipe(
S.optional,
S.withDefaults({
constructor: () => 'newest' as const,
decoding: () => 'newest' as const,
}),
),
}),
)
export const Route = createFileRoute('/shop/products/')({
validateSearch: productSearchSchema,
})
검색 매개변수 읽기
검색 매개변수를 검증하고 타입을 지정했다면 이제 검색 매개변수를 읽고 쓸 준비가 되었습니다. TanStack Router에서 이를 수행하는 몇 가지 방법을 살펴보겠습니다.
로더에서 검색 매개변수 사용
loaderDeps 옵션을 사용해 로더에서 검색 매개변수를 읽는 방법은 로더에서 검색 매개변수 사용 섹션을 참조합니다.
검색 매개변수는 부모 라우트에서 상속됩니다
라우트 트리를 내려갈수록 부모의 검색 매개변수와 타입이 병합되므로 자식 라우트에서도 부모의 검색 매개변수에 접근할 수 있습니다.
const productSearchSchema = z.object({
page: z.number().catch(1),
filter: z.string().catch(''),
sort: z.enum(['newest', 'oldest', 'price']).catch('newest'),
})
type ProductSearch = z.infer<typeof productSearchSchema>
export const Route = createFileRoute('/shop/products')({
validateSearch: productSearchSchema,
})
export const Route = createFileRoute('/shop/products/$productId')({
beforeLoad: ({ search }) => {
search
// ^? ProductSearch ✅
},
})
컴포넌트에서 검색 매개변수 사용
useSearch 훅을 통해 라우트의 component에서 해당 라우트의 검증된 검색 매개변수에 접근할 수 있습니다.
export const Route = createFileRoute('/shop/products')({
validateSearch: productSearchSchema,
})
const ProductList = () => {
const { page, filter, sort } = Route.useSearch()
return <div>...</div>
}
[!TIP] 컴포넌트가 코드 분할되어 있다면 getRouteApi 함수를 사용해
Route구성을 가져오지 않고 타입이 지정된useSearch()훅에 접근할 수 있습니다.
라우트 컴포넌트 외부에서 검색 매개변수 사용
useSearch 훅을 사용하면 앱 어디서든 라우트의 검증된 검색 매개변수에 접근할 수 있습니다. 시작 라우트의 from id/path를 전달하면 더 나은 타입 안전성을 얻을 수 있습니다.
// src/routes/shop.products.tsx
export const Route = createFileRoute('/shop/products')({
validateSearch: productSearchSchema,
// ...
})
// Somewhere else...
// src/components/product-list-sidebar.tsx
const routeApi = getRouteApi('/shop/products')
const ProductList = () => {
const routeSearch = routeApi.useSearch()
// OR
const { page, filter, sort } = useSearch({
from: Route.fullPath,
})
return <div>...</div>
}
또는 strict: false를 전달해 타입 안전성을 완화하고 선택적인 search 객체를 얻을 수 있습니다.
function ProductList() {
const search = useSearch({
strict: false,
})
// {
// page: number | undefined
// filter: string | undefined
// sort: 'newest' | 'oldest' | 'price' | undefined
// }
return <div>...</div>
}
검색 매개변수 쓰기
라우트의 검색 매개변수를 읽는 방법을 배웠으므로, 검색 매개변수를 수정하고 업데이트하는 주요 API를 이미 살펴봤다는 사실을 알 수 있습니다. 간단히 되짚어 보겠습니다.
<Link search />
검색 매개변수를 업데이트하는 가장 좋은 방법은 <Link /> 컴포넌트에서 search prop을 사용하는 것입니다.
현재 페이지의 검색 매개변수를 업데이트하고 from prop을 지정했다면 to prop을 생략할 수 있습니다.
예시는 다음과 같습니다.
export const Route = createFileRoute('/shop/products')({
validateSearch: productSearchSchema,
})
const ProductList = () => {
return (
<div>
<Link from={Route.fullPath} search={(prev) => ({ page: prev.page + 1 })}>
Next Page
</Link>
</div>
)
}
여러 라우트에서 렌더링되는 제네릭 컴포넌트의 검색 매개변수를 업데이트하려는 경우 from을 지정하기가 어려울 수 있습니다.
이 경우 to="."로 설정하면 느슨하게 타입이 지정된 검색 매개변수에 접근할 수 있습니다.
이를 보여주는 예시는 다음과 같습니다.
// `page` is a search param that is defined in the __root route and hence available on all routes.
const PageSelector = () => {
return (
<div>
<Link to="." search={(prev) => ({ ...prev, page: prev.page + 1 })}>
Next Page
</Link>
</div>
)
}
제네릭 컴포넌트가 라우트 트리의 특정 하위 트리에서만 렌더링된다면 from을 사용해 해당 하위 트리를 지정할 수 있습니다. 이 경우 원하면 to='.'를 생략할 수 있습니다.
// `page` is a search param that is defined in the /posts route and hence available on all of its child routes.
const PageSelector = () => {
return (
<div>
<Link
from="/posts"
to="."
search={(prev) => ({ ...prev, page: prev.page + 1 })}
>
Next Page
</Link>
</div>
)
useNavigate(), navigate({ search })
navigate 함수도 <Link />의 search prop과 동일하게 작동하는 search 옵션을 허용합니다.
export const Route = createFileRoute('/shop/products/$productId')({
validateSearch: productSearchSchema,
})
const ProductList = () => {
const navigate = useNavigate({ from: Route.fullPath })
return (
<div>
<button
onClick={() => {
navigate({
search: (prev) => ({ page: prev.page + 1 }),
})
}}
>
Next Page
</button>
</div>
)
}
router.navigate({ search })
router.navigate 함수는 위의 useNavigate/navigate 훅/함수와 정확히 동일하게 작동합니다.
<Navigate search />
<Navigate search /> 컴포넌트는 위의 useNavigate/navigate 훅/함수와 정확히 동일하게 작동하지만 함수 인수 대신 props로 옵션을 허용합니다.
검색 미들웨어로 검색 매개변수 변환
링크 href를 빌드할 때 기본적으로 쿼리 문자열 부분에서 중요한 것은 <Link>의 search 속성뿐입니다.
TanStack Router는 검색 미들웨어를 통해 href가 생성되기 전에 검색 매개변수를 조작하는 방법을 제공합니다. 검색 미들웨어는 라우트 또는 하위 라우트의 새 링크를 생성할 때 검색 매개변수를 변환하는 함수입니다. 또한 검색 검증 후 내비게이션할 때 실행되어 쿼리 문자열을 조작할 수 있습니다.
다음 예시에서는 빌드되는 모든 링크에 현재 검색 매개변수의 일부인 경우에만 rootValue 검색 매개변수를 추가하는 방법을 보여줍니다. 링크가 search 내부에 rootValue를 지정하면 해당 값을 사용해 링크를 빌드합니다.
import { z } from 'zod'
import { zodValidator } from '@tanstack/zod-adapter'
const searchSchema = z.object({
rootValue: z.string().optional(),
})
export const Route = createRootRoute({
validateSearch: zodValidator(searchSchema),
search: {
middlewares: [
({ search, next }) => {
const result = next(search)
return {
rootValue: search.rootValue,
...result,
}
},
],
},
})
이 사용 사례는 매우 일반적이므로 TanStack Router는 retainSearchParams를 통해 검색 매개변수를 유지하는 일반적인 구현을 제공합니다.
React
import { z } from 'zod'
import { createFileRoute, retainSearchParams } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'
const searchSchema = z.object({
rootValue: z.string().optional(),
})
export const Route = createRootRoute({
validateSearch: zodValidator(searchSchema),
search: {
middlewares: [retainSearchParams(['rootValue'])],
},
})
Solid
import { z } from 'zod'
import { createFileRoute, retainSearchParams } from '@tanstack/solid-router'
import { zodValidator } from '@tanstack/zod-adapter'
const searchSchema = z.object({
rootValue: z.string().optional(),
})
export const Route = createRootRoute({
validateSearch: zodValidator(searchSchema),
search: {
middlewares: [retainSearchParams(['rootValue'])],
},
})
또 다른 일반적인 사용 사례는 검색 매개변수가 기본값으로 설정되어 있을 때 링크에서 제거하는 것입니다. TanStack Router는 stripSearchParams를 통해 이 사용 사례에 대한 일반적인 구현을 제공합니다.
React
import { z } from 'zod'
import { createFileRoute, stripSearchParams } from '@tanstack/react-router'
import { zodValidator } from '@tanstack/zod-adapter'
const defaultValues = {
one: 'abc',
two: 'xyz',
}
const searchSchema = z.object({
one: z.string().default(defaultValues.one),
two: z.string().default(defaultValues.two),
})
export const Route = createFileRoute('/hello')({
validateSearch: zodValidator(searchSchema),
search: {
// strip default values
middlewares: [stripSearchParams(defaultValues)],
},
})
Solid
import { z } from 'zod'
import { createFileRoute, stripSearchParams } from '@tanstack/solid-router'
import { zodValidator } from '@tanstack/zod-adapter'
const defaultValues = {
one: 'abc',
two: 'xyz',
}
const searchSchema = z.object({
one: z.string().default(defaultValues.one),
two: z.string().default(defaultValues.two),
})
export const Route = createFileRoute('/hello')({
validateSearch: zodValidator(searchSchema),
search: {
// strip default values
middlewares: [stripSearchParams(defaultValues)],
},
})
여러 미들웨어를 연결할 수 있습니다. 다음 예시에서는 retainSearchParams와 stripSearchParams를 함께 사용하는 방법을 보여줍니다.
React
import {
Link,
createFileRoute,
retainSearchParams,
stripSearchParams,
} from '@tanstack/react-router'
import { z } from 'zod'
import { zodValidator } from '@tanstack/zod-adapter'
const defaultValues = ['foo', 'bar']
export const Route = createFileRoute('/search')({
validateSearch: zodValidator(
z.object({
retainMe: z.string().optional(),
arrayWithDefaults: z.string().array().default(defaultValues),
required: z.string(),
}),
),
search: {
middlewares: [
retainSearchParams(['retainMe']),
stripSearchParams({ arrayWithDefaults: defaultValues }),
],
},
})
Solid
import {
Link,
createFileRoute,
retainSearchParams,
stripSearchParams,
} from '@tanstack/solid-router'
import { z } from 'zod'
import { zodValidator } from '@tanstack/zod-adapter'
const defaultValues = ['foo', 'bar']
export const Route = createFileRoute('/search')({
validateSearch: zodValidator(
z.object({
retainMe: z.string().optional(),
arrayWithDefaults: z.string().array().default(defaultValues),
required: z.string(),
}),
),
search: {
middlewares: [
retainSearchParams(['retainMe']),
stripSearchParams({ arrayWithDefaults: defaultValues }),
],
},
})