기본적으로 TanStack Router는 JSON.stringify와 JSON.parse를 사용해 URL 검색 매개변수를 자동으로 파싱하고 직렬화합니다. 이 과정에는 검색 객체의 직렬화 및 역직렬화뿐 아니라 URL 검색 매개변수에서 일반적으로 사용하는 검색 문자열의 이스케이프 및 이스케이프 해제가 포함됩니다.
예를 들어 기본 구성을 사용하고 다음 검색 객체가 있다면:
const search = {
page: 1,
sort: 'asc',
filters: { author: 'tanner', min_words: 800 },
}
다음 검색 문자열로 직렬화되고 이스케이프됩니다.
?page=1&sort=asc&filters=%7B%22author%22%3A%22tanner%22%2C%22min_words%22%3A800%7D
다음 코드로 기본 동작을 구현할 수 있습니다.
React
import {
createRouter,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/react-router'
const router = createRouter({
// ...
parseSearch: parseSearchWith(JSON.parse),
stringifySearch: stringifySearchWith(JSON.stringify),
})
Solid
import {
createRouter,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/solid-router'
const router = createRouter({
// ...
parseSearch: parseSearchWith(JSON.parse),
stringifySearch: stringifySearchWith(JSON.stringify),
})
하지만 이 기본 동작이 모든 사용 사례에 적합하지는 않을 수 있습니다. 예를 들어 base64 인코딩과 같은 다른 직렬화 형식을 사용하거나, query-string, JSURL2, Zipson처럼 직렬화/역직렬화 전용 라이브러리를 사용하려 할 수 있습니다.
Router 구성의 parseSearch 및 stringifySearch 옵션에 직접 직렬화 및 역직렬화 함수를 제공하면 됩니다. 이때 TanStack Router에 내장된 헬퍼 함수인 parseSearchWith와 stringifySearchWith를 활용해 과정을 단순화할 수 있습니다.
[!TIP] 직렬화 및 역직렬화에서 중요한 점은 역직렬화 후 동일한 객체를 다시 얻을 수 있어야 한다는 것입니다. 직렬화 및 역직렬화 과정이 올바르게 수행되지 않으면 일부 정보가 손실될 수 있으므로 중요합니다. 예를 들어 중첩 객체를 지원하지 않는 라이브러리를 사용하면 검색 문자열을 역직렬화할 때 중첩 객체가 손실될 수 있습니다.

다음은 TanStack Router에서 검색 매개변수 직렬화를 사용자 지정하는 방법의 예시입니다.
Base64 사용
브라우저와 URL 언펄러 등에서 최대한 호환되도록 검색 매개변수를 base64로 인코딩하는 것이 일반적입니다. 다음 코드로 이를 수행할 수 있습니다.
React
import {
Router,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/react-router'
const router = createRouter({
parseSearch: parseSearchWith((value) => JSON.parse(decodeFromBinary(value))),
stringifySearch: stringifySearchWith((value) =>
encodeToBinary(JSON.stringify(value)),
),
})
function decodeFromBinary(str: string): string {
return decodeURIComponent(
Array.prototype.map
.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
})
.join(''),
)
}
function encodeToBinary(str: string): string {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16))
}),
)
}
Solid
import {
Router,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/solid-router'
const router = createRouter({
parseSearch: parseSearchWith((value) => JSON.parse(decodeFromBinary(value))),
stringifySearch: stringifySearchWith((value) =>
encodeToBinary(JSON.stringify(value)),
),
})
function decodeFromBinary(str: string): string {
return decodeURIComponent(
Array.prototype.map
.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
})
.join(''),
)
}
function encodeToBinary(str: string): string {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16))
}),
)
}
따라서 이 구성을 사용해 앞의 객체를 검색 문자열로 변환하면 다음과 같습니다.
?page=1&sort=asc&filters=eyJhdXRob3IiOiJ0YW5uZXIiLCJtaW5fd29yZHMiOjgwMH0%3D
[!WARNING] 사용자 입력을 Base64로 직렬화하면 URL 역직렬화와 충돌할 위험이 있습니다. URL이 올바르게 파싱되지 않거나 다른 값으로 해석되는 등 예기치 않은 동작이 발생할 수 있습니다. 이를 방지하려면 안전한 바이너리 인코딩/디코딩 방법을 사용해 검색 매개변수를 인코딩해야 합니다(아래 참고).
query-string 라이브러리 사용
query-string 라이브러리는 쿼리 문자열을 안정적으로 파싱하고 문자열화할 수 있어 널리 사용됩니다. 이 라이브러리를 사용해 검색 매개변수의 직렬화 형식을 사용자 지정할 수 있습니다. 다음 코드로 이를 수행할 수 있습니다.
React
import { createRouter } from '@tanstack/react-router'
import qs from 'query-string'
const router = createRouter({
// ...
stringifySearch: stringifySearchWith((value) =>
qs.stringify(value, {
// ...options
}),
),
parseSearch: parseSearchWith((value) =>
qs.parse(value, {
// ...options
}),
),
})
Solid
import { createRouter } from '@tanstack/solid-router'
import qs from 'query-string'
const router = createRouter({
// ...
stringifySearch: stringifySearchWith((value) =>
qs.stringify(value, {
// ...options
}),
),
parseSearch: parseSearchWith((value) =>
qs.parse(value, {
// ...options
}),
),
})
따라서 이 구성을 사용해 앞의 객체를 검색 문자열로 변환하면 다음과 같습니다.
?page=1&sort=asc&filters=author%3Dtanner%26min_words%3D800
JSURL2 라이브러리 사용
JSURL2는 가독성을 유지하면서 URL을 압축할 수 있는 비표준 라이브러리입니다. 다음 코드로 이를 수행할 수 있습니다.
React
import {
Router,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/react-router'
import { parse, stringify } from 'jsurl2'
const router = createRouter({
// ...
parseSearch: parseSearchWith(parse),
stringifySearch: stringifySearchWith(stringify),
})
Solid
import {
Router,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/solid-router'
import { parse, stringify } from 'jsurl2'
const router = createRouter({
// ...
parseSearch: parseSearchWith(parse),
stringifySearch: stringifySearchWith(stringify),
})
따라서 이 구성을 사용해 앞의 객체를 검색 문자열로 변환하면 다음과 같습니다.
?page=1&sort=asc&filters=(author~tanner~min*_words~800)~
Zipson 라이브러리 사용
Zipson은 사용하기 쉽고 성능이 뛰어난 JSON 압축 라이브러리입니다(런타임 성능과 최종 압축 성능 모두). 이 라이브러리로 검색 매개변수를 압축하려면 이스케이프/이스케이프 해제와 base64 인코딩/디코딩도 필요하며, 다음 코드를 사용할 수 있습니다.
React
import {
Router,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/react-router'
import { stringify, parse } from 'zipson'
const router = createRouter({
parseSearch: parseSearchWith((value) => parse(decodeFromBinary(value))),
stringifySearch: stringifySearchWith((value) =>
encodeToBinary(stringify(value)),
),
})
function decodeFromBinary(str: string): string {
return decodeURIComponent(
Array.prototype.map
.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
})
.join(''),
)
}
function encodeToBinary(str: string): string {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16))
}),
)
}
Solid
import {
Router,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/solid-router'
import { stringify, parse } from 'zipson'
const router = createRouter({
parseSearch: parseSearchWith((value) => parse(decodeFromBinary(value))),
stringifySearch: stringifySearchWith((value) =>
encodeToBinary(stringify(value)),
),
})
function decodeFromBinary(str: string): string {
return decodeURIComponent(
Array.prototype.map
.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
})
.join(''),
)
}
function encodeToBinary(str: string): string {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16))
}),
)
}
따라서 이 구성을 사용해 앞의 객체를 검색 문자열로 변환하면 다음과 같습니다.
?page=1&sort=asc&filters=JTdCJUMyJUE4YXV0aG9yJUMyJUE4JUMyJUE4dGFubmVyJUMyJUE4JUMyJUE4bWluX3dvcmRzJUMyJUE4JUMyJUEyQ3UlN0Q%3D
안전한 바이너리 인코딩/디코딩
브라우저에서 atob 및 btoa 함수가 UTF-8이 아닌 문자와 제대로 작동한다고 보장할 수 없습니다. 대신 다음 인코딩/디코딩 유틸리티를 사용하는 것이 좋습니다.
문자열을 바이너리 문자열로 인코딩하려면:
export function encodeToBinary(str: string): string {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16))
}),
)
}
바이너리 문자열을 문자열로 디코딩하려면:
export function decodeFromBinary(str: string): string {
return decodeURIComponent(
Array.prototype.map
.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
})
.join(''),
)
}