내비게이션
모든 것은 상대적입니다
믿기 어려울 수 있지만 앱 안의 모든 내비게이션은 상대적입니다. 명시적인 상대 경로 구문(../../somewhere)을 사용하지 않는 경우에도 마찬가지입니다. 링크를 클릭하거나 명령형 내비게이션을 호출할 때마다 항상 출발지 경로와 목적지 경로가 있으며, 한 라우트에서 다른 라우트로 이동합니다.
TanStack Router는 모든 내비게이션에서 이러한 상대 내비게이션 개념을 일관되게 적용하므로 API에서 다음 두 속성을 자주 보게 됩니다.
from- 출발지 라우트 경로to- 목적지 라우트 경로
⚠️
from라우트 경로를 제공하지 않으면 라우터는 루트/라우트에서 이동한다고 가정하고 절대 경로만 자동 완성합니다. 어디로 가는지 알려면 어디에서 출발하는지 알아야 하기 때문입니다 😉.
공유 내비게이션 API
TanStack Router의 모든 내비게이션 및 라우트 매칭 API는 API에 따른 사소한 차이를 제외하고 동일한 핵심 인터페이스를 사용합니다. 따라서 내비게이션과 라우트 매칭을 한 번 익히면 라이브러리 전반에서 동일한 구문과 개념을 사용할 수 있습니다.
ToOptions 인터페이스
다음은 모든 내비게이션 및 라우트 매칭 API에서 사용하는 핵심 ToOptions 인터페이스입니다.
type ToOptions<
TRouteTree extends AnyRoute = AnyRoute,
TFrom extends RoutePaths<TRouteTree> | string = string,
TTo extends string = '',
> = {
// `from` is an optional route ID or path. If it is not supplied, only absolute paths will be auto-completed and type-safe. It's common to supply the route.fullPath of the origin route you are rendering from for convenience. If you don't know the origin route, leave this empty and work with absolute paths or unsafe relative paths.
from?: string
// `to` can be an absolute route path or a relative path from the `from` option to a valid route path. ⚠️ Do not interpolate path params, hash or search params into the `to` options. Use the `params`, `search`, and `hash` options instead.
to: string
// `params` is either an object of path params to interpolate into the `to` option or a function that supplies the previous params and allows you to return new ones. This is the only way to interpolate dynamic parameters into the final URL. Depending on the `from` and `to` route, you may need to supply none, some or all of the path params. TypeScript will notify you of the required params if there are any.
params:
| Record<string, unknown>
| ((prevParams: Record<string, unknown>) => Record<string, unknown>)
// `search` is either an object of query params or a function that supplies the previous search and allows you to return new ones. Depending on the `from` and `to` route, you may need to supply none, some or all of the query params. TypeScript will notify you of the required search params if there are any.
search:
| Record<string, unknown>
| ((prevSearch: Record<string, unknown>) => Record<string, unknown>)
// `hash` is either a string or a function that supplies the previous hash and allows you to return a new one.
hash?: string | ((prevHash: string) => string)
// `state` is either an object of state or a function that supplies the previous state and allows you to return a new one. State is stored in the history API and can be useful for passing data between routes that you do not want to permanently store in URL search params.
state?:
| Record<string, any>
| ((prevState: Record<string, unknown>) => Record<string, unknown>)
// `mask` is another navigation object used to mask the URL shown in the browser for this navigation.
mask?: ToMaskOptions<TRouteTree>
}
type ToMaskOptions<TRouteTree extends AnyRoute = AnyRoute> = {
// `from`, `to`, `params`, `search`, `hash`, and `state` behave the same as in `ToOptions`.
// `mask` itself is not allowed inside `ToMaskOptions`.
from?: string
to: string
params:
| Record<string, unknown>
| ((prevParams: Record<string, unknown>) => Record<string, unknown>)
search:
| Record<string, unknown>
| ((prevSearch: Record<string, unknown>) => Record<string, unknown>)
hash?: string | ((prevHash: string) => string)
state?:
| Record<string, any>
| ((prevState: Record<string, unknown>) => Record<string, unknown>)
// If true, the URL will unmask on page reload.
unmaskOnReload?: boolean
}
🧠 모든 라우트 객체에는
to속성이 있으며, 이를 모든 내비게이션 또는 라우트 매칭 API의to로 사용할 수 있습니다. 가능한 경우 일반 문자열 대신 타입 안전한 라우트 참조를 사용할 수 있습니다.
import { Route as aboutRoute } from './routes/about.tsx'
function Comp() {
return <Link to={aboutRoute.to}>About</Link>
}
NavigateOptions 인터페이스
다음은 ToOptions를 확장하는 핵심 NavigateOptions 인터페이스입니다. 실제로 내비게이션을 수행하는 모든 API가 이 인터페이스를 사용합니다.
export type NavigateOptions<
TRouteTree extends AnyRoute = AnyRoute,
TFrom extends RoutePaths<TRouteTree> | string = string,
TTo extends string = '',
> = ToOptions<TRouteTree, TFrom, TTo> & {
// `replace` is a boolean that determines whether the navigation should replace the current history entry or push a new one.
replace?: boolean
// `resetScroll` is a boolean that determines whether scroll position will be reset to 0,0 after the location is committed to browser history.
resetScroll?: boolean
// `hashScrollIntoView` is a boolean or object that determines whether an id matching the hash will be scrolled into view after the location is committed to history.
hashScrollIntoView?: boolean | ScrollIntoViewOptions
// `viewTransition` is either a boolean or function that determines if and how the browser will call document.startViewTransition() when navigating.
viewTransition?: boolean | ViewTransitionOptions
// `ignoreBlocker` is a boolean that determines if navigation should ignore any blockers that might prevent it.
ignoreBlocker?: boolean
// `reloadDocument` is a boolean that determines if navigation to a route inside of router will trigger a full page load instead of the traditional SPA navigation.
reloadDocument?: boolean
// `href` is a string that can be used in place of `to` to navigate to a full built href, e.g. pointing to an external target.
href?: string
}
NavigateOptions에는 mask를 비롯한 모든 ToOptions 필드가 포함됩니다.
LinkOptions 인터페이스
실제 <a> 태그를 사용하는 곳에서는 NavigateOptions를 확장하는 LinkOptions 인터페이스를 사용할 수 있습니다.
export type LinkOptions<
TRouteTree extends AnyRoute = AnyRoute,
TFrom extends RoutePaths<TRouteTree> | string = string,
TTo extends string = '',
> = NavigateOptions<TRouteTree, TFrom, TTo> & {
// The standard anchor tag target attribute
target?: HTMLAnchorElement['target']
// Defaults to `{ exact: false, includeHash: false }`
activeOptions?: {
exact?: boolean
includeHash?: boolean
includeSearch?: boolean
explicitUndefined?: boolean
}
// Choose the preload strategy for this link. `false` disables preloading;
// `'intent'`, `'viewport'`, and `'render'` select when it begins.
preload?: false | 'intent' | 'viewport' | 'render'
// Delay focus/hover intent by this many milliseconds. Touch intent preloads immediately.
preloadDelay?: number
// If true, will render the link without the href attribute
disabled?: boolean
}
LinkOptions는 NavigateOptions를 확장하므로 mask도 지원합니다.
내비게이션 API
상대 내비게이션과 모든 인터페이스를 살펴보았으니 이제 사용할 수 있는 다양한 내비게이션 API를 알아보겠습니다.
<Link>컴포넌트- 클릭하거나 cmd/ctrl 키를 누른 채 클릭해 새 탭에서 열 수 있는 유효한
href가 포함된 실제<a>태그를 생성합니다.
- 클릭하거나 cmd/ctrl 키를 누른 채 클릭해 새 탭에서 열 수 있는 유효한
useNavigate()훅- 가능하면 내비게이션에
Link컴포넌트를 사용해야 하지만, 사이드 이펙트의 결과로 명령형 내비게이션이 필요한 경우도 있습니다.useNavigate는 즉시 클라이언트 측 내비게이션을 수행할 수 있는 함수를 반환합니다.
- 가능하면 내비게이션에
<Navigate>컴포넌트- 아무것도 렌더링하지 않고 즉시 클라이언트 측 내비게이션을 수행합니다.
Router.navigate()메서드- TanStack Router에서 가장 강력한 내비게이션 API입니다.
useNavigate와 마찬가지로 명령형으로 이동하지만 router에 접근할 수 있는 모든 곳에서 사용할 수 있습니다.
- TanStack Router에서 가장 강력한 내비게이션 API입니다.
⚠️ 이러한 API는 서버 측 리디렉션을 대체하지 않습니다. 애플리케이션을 마운트하기 전에 사용자를 한 라우트에서 다른 라우트로 즉시 리디렉션해야 한다면 클라이언트 측 내비게이션 대신 서버 측 리디렉션을 사용합니다.
<Link> 컴포넌트
Link 컴포넌트는 앱 내부에서 이동하는 가장 일반적인 방법입니다. 클릭하거나 cmd/ctrl 키를 누른 채 클릭해 새 탭에서 열 수 있는 유효한 href 속성이 포함된 실제 <a> 태그를 렌더링합니다. 새 창에서 링크를 여는 target을 비롯해 일반적인 <a> 속성도 지원합니다.
Link 컴포넌트는 LinkOptions 인터페이스 외에 다음 props도 지원합니다.
export type LinkProps<
TFrom extends RoutePaths<RegisteredRouter['routeTree']> | string = string,
TTo extends string = '',
> = LinkOptions<RegisteredRouter['routeTree'], TFrom, TTo> & {
// A function that returns additional props for the `active` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
activeProps?:
| FrameworkHTMLAnchorTagAttributes
| (() => FrameworkHTMLAnchorAttributes)
// A function that returns additional props for the `inactive` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
inactiveProps?:
| FrameworkHTMLAnchorAttributes
| (() => FrameworkHTMLAnchorAttributes)
}
절대 링크
간단한 정적 링크를 만들어 보겠습니다.
React
import { Link } from '@tanstack/react-router'
const link = <Link to="/about">About</Link>
Solid
import { Link } from '@tanstack/solid-router'
const link = <Link to="/about">About</Link>
동적 링크
동적 링크는 동적 세그먼트를 포함하는 링크입니다. 예를 들어 블로그 게시물 링크는 다음과 같을 수 있습니다.
const link = (
<Link
to="/blog/post/$postId"
params={{
postId: 'my-first-blog-post',
}}
>
Blog Post
</Link>
)
일반적으로 동적 세그먼트 params는 string 값이지만, 라우트 옵션에서 파싱하는 다른 타입일 수도 있습니다. 어느 경우든 올바른 타입을 전달하는지 컴파일 시간에 확인합니다.
상대 링크
기본적으로 from 라우트 경로를 제공하지 않으면 모든 링크는 절대 링크입니다. 따라서 위 링크는 현재 어떤 라우트에 있는지와 관계없이 항상 /about 라우트로 이동합니다.
상대 링크는 from 라우트 경로와 함께 사용할 수 있습니다. from 라우트 경로를 제공하지 않으면 상대 경로는 현재 활성 위치를 기본값으로 사용합니다.
[!NOTE] 라우트의 메서드로 useNavigate를 호출하는 경우(예:
Route.useNavigate)from위치는 호출된 라우트로 미리 정의됩니다.또 다른 흔한 문제는 경로 없는 레이아웃 라우트에서 이를 사용하는 경우입니다. 경로 없는 레이아웃 라우트에는 실제 경로가 없으므로
from위치는 경로 없는 레이아웃 라우트의 부모로 간주됩니다. 따라서 상대 라우팅은 이 부모를 기준으로 확인됩니다.
const postIdRoute = createRoute({
path: '/blog/post/$postId',
})
const link = (
<Link from={postIdRoute.fullPath} to="../categories">
Categories
</Link>
)
위에서 본 것처럼 route.fullPath를 from 라우트 경로로 제공하는 것이 일반적입니다. route.fullPath는 애플리케이션을 리팩터링할 때 업데이트되는 참조이기 때문입니다. 하지만 라우트를 직접 가져올 수 없는 경우도 있으며, 이때는 라우트 경로를 문자열로 직접 제공해도 됩니다. 평소처럼 타입 검사도 수행됩니다.
특수 상대 경로: "." 및 ".."
현재 위치나 다른 from 경로를 다시 로드하고 싶을 때가 많습니다. 예를 들어 현재 라우트 및 부모 라우트의 로더를 다시 실행하거나 부모 라우트로 돌아가려는 경우입니다. to 라우트 경로를 "."으로 지정하면 현재 위치 또는 제공된 from 경로를 다시 로드할 수 있습니다.
현재 위치 또는 다른 경로를 기준으로 한 라우트 뒤로 이동해야 할 때도 있습니다. to 라우트 경로를 ".."으로 지정하면 현재 위치 바로 앞의 첫 번째 부모 라우트로 이동합니다.
export const Route = createFileRoute('/posts/$postId')({
component: PostComponent,
})
function PostComponent() {
return (
<div>
<Link to=".">Reload the current route of /posts/$postId</Link>
<Link to="..">Navigate back to /posts</Link>
// the below are all equivalent
<Link to="/posts">Navigate back to /posts</Link>
<Link from="/posts" to=".">
Navigate back to /posts
</Link>
// the below are all equivalent
<Link to="/">Navigate to root</Link>
<Link from="/posts" to="..">
Navigate to root
</Link>
</div>
)
}
검색 매개변수 링크
검색 매개변수는 라우트에 추가 컨텍스트를 제공하는 좋은 방법입니다. 예를 들어 검색 페이지에 검색 쿼리를 제공할 수 있습니다.
const link = (
<Link
to="/search"
search={{
query: 'tanstack',
}}
>
Search
</Link>
)
기존 라우트에 관한 다른 정보를 제공하지 않고 검색 매개변수 하나만 업데이트하려는 경우도 많습니다. 예를 들어 검색 결과의 페이지 번호를 업데이트할 수 있습니다.
const link = (
<Link
to="."
search={(prev) => ({
...prev,
page: prev.page + 1,
})}
>
Next Page
</Link>
)
검색 매개변수 타입 안전성
검색 매개변수는 매우 동적인 상태 관리 메커니즘이므로 올바른 타입을 전달하는지 확인하는 것이 중요합니다. 뒤의 섹션에서 다른 유용한 기능과 함께 검색 매개변수를 검증하고 타입 안전성을 보장하는 방법을 자세히 살펴봅니다.
해시 링크
해시 링크는 페이지의 특정 섹션으로 연결하는 좋은 방법입니다. 예를 들어 블로그 게시물의 특정 섹션으로 연결할 수 있습니다.
const link = (
<Link
to="/blog/post/$postId"
params={{
postId: 'my-first-blog-post',
}}
hash="section-1"
>
Section 1
</Link>
)
⚠️ 해시 프래그먼트가 포함된 URL로 직접 이동할 때 프래그먼트는 클라이언트에서만 사용할 수 있습니다. 브라우저는 요청 URL의 일부로 프래그먼트를 서버에 보내지 않습니다.
즉, 서버 측 렌더링 방식을 사용하면 서버 측에서 해시 프래그먼트를 사용할 수 없으며, 해시를 사용해 마크업을 렌더링할 때 하이드레이션 불일치가 발생할 수 있습니다.
예시는 다음과 같습니다.
- 마크업에서 해시 값을 반환하는 경우
- 해시 값을 기반으로 조건부 렌더링을 하는 경우
- 해시 값을 기반으로 Link를 활성 상태로 설정하는 경우
선택적 매개변수로 내비게이션
선택적 경로 매개변수를 사용하면 필요에 따라 매개변수를 포함하거나 생략하는 유연한 내비게이션 패턴을 만들 수 있습니다. 선택적 매개변수는 {-$paramName} 구문을 사용하며 URL 구조를 세밀하게 제어할 수 있습니다.
매개변수 상속과 제거
선택적 매개변수로 이동할 때는 다음 두 가지 주요 전략을 사용할 수 있습니다.
현재 매개변수 상속
params: {}를 사용해 현재 라우트의 모든 매개변수를 상속합니다.
// Inherits current route parameters
<Link to="/posts/{-$category}" params={{}}>
All Posts
</Link>
매개변수 제거
매개변수를 undefined로 설정해 명시적으로 제거합니다.
// Removes the category parameter
<Link to="/posts/{-$category}" params={{ category: undefined }}>
All Posts
</Link>
기본 선택적 매개변수 내비게이션
// Navigate with optional parameter
<Link
to="/posts/{-$category}"
params={{ category: 'tech' }}
>
Tech Posts
</Link>
// Navigate without optional parameter
<Link
to="/posts/{-$category}"
params={{ category: undefined }}
>
All Posts
</Link>
// Navigate using parameter inheritance
<Link
to="/posts/{-$category}"
params={{}}
>
Current Category
</Link>
함수 스타일 매개변수 업데이트
함수 스타일 매개변수 업데이트는 선택적 매개변수와 함께 사용할 때 특히 유용합니다.
// Remove a parameter using function syntax
<Link
to="/posts/{-$category}"
params={(prev) => ({ ...prev, category: undefined })}
>
Clear Category
</Link>
// Update a parameter while keeping others
<Link
to="/articles/{-$category}/{-$slug}"
params={(prev) => ({ ...prev, category: 'news' })}
>
News Articles
</Link>
// Conditionally set parameters
<Link
to="/posts/{-$category}"
params={(prev) => ({
...prev,
category: someCondition ? 'tech' : undefined
})}
>
Conditional Category
</Link>
여러 선택적 매개변수
여러 선택적 매개변수를 사용할 때 포함할 매개변수를 자유롭게 조합할 수 있습니다.
// Navigate with some optional parameters
<Link
to="/posts/{-$category}/{-$slug}"
params={{ category: 'tech', slug: undefined }}
>
Tech Posts
</Link>
// Remove all optional parameters
<Link
to="/posts/{-$category}/{-$slug}"
params={{ category: undefined, slug: undefined }}
>
All Posts
</Link>
// Set multiple parameters
<Link
to="/posts/{-$category}/{-$slug}"
params={{ category: 'tech', slug: 'react-tips' }}
>
Specific Post
</Link>
필수 및 선택적 매개변수 혼합
선택적 매개변수는 필수 매개변수와 원활하게 함께 작동합니다.
// Required 'id', optional 'tab'
<Link
to="/users/$id/{-$tab}"
params={{ id: '123', tab: 'settings' }}
>
User Settings
</Link>
// Remove optional parameter while keeping required
<Link
to="/users/$id/{-$tab}"
params={{ id: '123', tab: undefined }}
>
User Profile
</Link>
// Use function style with mixed parameters
<Link
to="/users/$id/{-$tab}"
params={(prev) => ({ ...prev, tab: 'notifications' })}
>
User Notifications
</Link>
고급 선택적 매개변수 패턴
접두사 및 접미사 매개변수 접두사 또는 접미사가 있는 선택적 매개변수도 내비게이션에서 작동합니다.
// Navigate to file with optional name
<Link
to="/files/prefix{-$name}.txt"
params={{ name: 'document' }}
>
Document File
</Link>
// Navigate to file without optional name
<Link
to="/files/prefix{-$name}.txt"
params={{ name: undefined }}
>
Default File
</Link>
모든 매개변수가 선택 사항인 경우 모든 매개변수가 선택적인 라우트입니다.
// Navigate to specific date
<Link
to="/{-$year}/{-$month}/{-$day}"
params={{ year: '2023', month: '12', day: '25' }}
>
Christmas 2023
</Link>
// Navigate to partial date
<Link
to="/{-$year}/{-$month}/{-$day}"
params={{ year: '2023', month: '12', day: undefined }}
>
December 2023
</Link>
// Navigate to root with all parameters removed
<Link
to="/{-$year}/{-$month}/{-$day}"
params={{ year: undefined, month: undefined, day: undefined }}
>
Home
</Link>
검색 매개변수와 선택적 매개변수를 함께 사용하는 내비게이션
선택적 매개변수는 검색 매개변수와 함께 사용하면 더욱 유용합니다.
// Combine optional path params with search params
<Link
to="/posts/{-$category}"
params={{ category: 'tech' }}
search={{ page: 1, sort: 'newest' }}
>
Tech Posts - Page 1
</Link>
// Remove path param but keep search params
<Link
to="/posts/{-$category}"
params={{ category: undefined }}
search={(prev) => prev}
>
All Posts - Same Filters
</Link>
선택적 매개변수를 사용하는 명령형 내비게이션
명령형 내비게이션에서도 동일한 패턴을 모두 사용할 수 있습니다.
function Component() {
const navigate = useNavigate()
const clearFilters = () => {
navigate({
to: '/posts/{-$category}/{-$tag}',
params: { category: undefined, tag: undefined },
})
}
const setCategory = (category: string) => {
navigate({
to: '/posts/{-$category}/{-$tag}',
params: (prev) => ({ ...prev, category }),
})
}
const applyFilters = (category?: string, tag?: string) => {
navigate({
to: '/posts/{-$category}/{-$tag}',
params: { category, tag },
})
}
}
활성 및 비활성 Props
Link 컴포넌트는 activeProps와 inactiveProps라는 두 가지 추가 props를 지원합니다. 이 props는 링크의 active 및 inactive 상태에 사용할 추가 props를 반환하는 함수입니다. 여기에서 전달한 props 중 스타일과 클래스를 제외한 모든 props는 Link에 전달된 원래 props를 재정의합니다. 전달한 스타일이나 클래스는 서로 병합됩니다.
예시는 다음과 같습니다.
const link = (
<Link
to="/blog/post/$postId"
params={{
postId: 'my-first-blog-post',
}}
activeProps={{
style: {
fontWeight: 'bold',
},
}}
>
Section 1
</Link>
)
data-status 속성
activeProps 및 inactiveProps props 외에도 Link 컴포넌트는 활성 상태일 때 렌더링된 요소에 data-status 속성을 추가합니다. 이 속성은 링크의 현재 상태에 따라 active 또는 undefined가 됩니다. props 대신 데이터 속성을 사용해 링크 스타일을 지정하려는 경우 유용합니다.
활성 옵션
Link 컴포넌트에는 링크의 활성 여부를 판단하는 몇 가지 옵션을 제공하는 activeOptions 속성이 있습니다. 다음 인터페이스에서 해당 옵션을 설명합니다.
export interface ActiveOptions {
// If true, the link will be active if the current route matches the `to` route path exactly (no children routes)
// Defaults to `false`
exact?: boolean
// If true, the link will only be active if the current URL hash matches the `hash` prop
// Defaults to `false`
includeHash?: boolean // Defaults to false
// If true, the link will only be active if the current URL search params inclusively match the `search` prop
// Defaults to `true`
includeSearch?: boolean
// This modifies the `includeSearch` behavior.
// If true, properties in `search` that are explicitly `undefined` must NOT be present in the current URL search params for the link to be active.
// defaults to `false`
explicitUndefined?: boolean
}
기본적으로 결과 pathname이 현재 라우트의 접두사인지 확인합니다. 검색 매개변수가 제공되면 현재 위치의 검색 매개변수와 포괄적으로 일치하는지 확인합니다. 해시는 기본적으로 확인하지 않습니다.
예를 들어 /blog/post/my-first-blog-post 라우트에 있다면 다음 링크가 활성 상태가 됩니다.
const link1 = (
<Link to="/blog/post/$postId" params={{ postId: 'my-first-blog-post' }}>
Blog Post
</Link>
)
const link2 = <Link to="/blog/post">Blog Post</Link>
const link3 = <Link to="/blog">Blog Post</Link>
하지만 다음 링크는 활성 상태가 되지 않습니다.
const link4 = (
<Link to="/blog/post/$postId" params={{ postId: 'my-second-blog-post' }}>
Blog Post
</Link>
)
일부 링크는 정확히 일치할 때만 활성 상태가 되도록 하는 것이 일반적입니다. 홈 페이지 링크가 좋은 예입니다. 이런 경우 exact: true 옵션을 전달할 수 있습니다.
const link = (
<Link to="/" activeOptions={{ exact: true }}>
Home
</Link>
)
이렇게 하면 자식 라우트에 있을 때 링크가 활성 상태가 되지 않습니다.
알아 두어야 할 옵션이 몇 가지 더 있습니다.
- 매칭에 해시를 포함하려면
includeHash: true옵션을 전달할 수 있습니다. - 매칭에 검색 매개변수를 포함하지 않으려면
includeSearch: false옵션을 전달할 수 있습니다.
자식에 isActive 전달
Link 컴포넌트는 자식으로 함수를 받을 수 있으므로 isActive 속성을 자식에게 전달할 수 있습니다. 예를 들어 부모 링크의 활성 여부에 따라 자식 컴포넌트의 스타일을 지정할 수 있습니다.
const link = (
<Link to="/blog/post">
{({ isActive }) => {
return (
<>
<span>My Blog Post</span>
<icon className={isActive ? 'active' : 'inactive'} />
</>
)
}}
</Link>
)
Link 프리로딩
Link 컴포넌트는 다음 네 가지 preload 값을 지원합니다.
false는 자동 프리로딩을 비활성화합니다.'intent'는 링크에 포커스가 가거나 마우스를 올리거나 터치할 때 프리로딩합니다.'viewport'는 링크가 뷰포트에 들어올 때 프리로딩합니다.'render'는 링크가 렌더링되는 즉시 프리로딩합니다.
라우터 옵션에서 기본값으로 구성하거나(자세한 내용은 뒤에서 설명합니다) Link 컴포넌트에 preload prop을 전달해 설정할 수 있습니다. 다음은 intent 프리로딩 예시입니다.
const link = (
<Link to="/blog/post/$postId" preload="intent">
Blog Post
</Link>
)
프리로딩을 활성화하고 비동기 라우트 종속성이 비교적 빠르다면 이 간단한 방법만으로도 큰 노력 없이 애플리케이션의 체감 성능을 높일 수 있습니다.
더 좋은 점은 @tanstack/query와 같은 캐시 우선 라이브러리를 사용하면 프리로드된 라우트가 유지되어 사용자가 나중에 해당 라우트로 이동할 때 stale-while-revalidate 경험을 제공할 준비가 된다는 것입니다.
Link 프리로딩 지연
'intent' 및 'viewport' 프리로딩에서는 구성 가능한 지연 시간으로 포커스, 마우스 오버 또는 뷰포트 진입 후 프리로딩을 시작하기까지 기다리는 시간을 정합니다. 지연 시간 전에 포커스나 마우스 오버가 끝나거나 링크가 뷰포트를 벗어나면 대기 중인 프리로드가 취소됩니다. 터치 intent는 지연 시간 없이 즉시 프리로딩합니다. 기본 지연 시간은 50밀리초이며 Link 컴포넌트에 preloadDelay prop을 전달해 변경할 수 있습니다.
const link = (
<Link to="/blog/post/$postId" preload="intent" preloadDelay={100}>
Blog Post
</Link>
)
useNavigate
⚠️
Link컴포넌트에는href, cmd/ctrl 키를 누른 채 클릭하는 기능, 활성/비활성 기능이 기본 제공되므로 사용자가 상호작용할 수 있는 요소(예: 링크, 버튼)에는useNavigate대신Link컴포넌트를 사용하는 것이 좋습니다. 하지만 사이드 이펙트 내비게이션(예: 성공한 비동기 작업의 결과로 발생하는 내비게이션)을 처리하려면useNavigate가 필요한 경우도 있습니다.
useNavigate 훅은 명령형으로 이동할 때 호출할 수 있는 navigate 함수를 반환합니다. 사이드 이펙트(예: 성공한 비동기 작업)에서 라우트로 이동할 때 유용합니다. 예시는 다음과 같습니다.
function Component() {
const navigate = useNavigate({ from: '/posts/$postId' })
const handleSubmit = async (e: FrameworkFormEvent) => {
e.preventDefault()
const response = await fetch('/posts', {
method: 'POST',
body: JSON.stringify({ title: 'My First Post' }),
})
const { id: postId } = await response.json()
if (response.ok) {
navigate({ to: '/posts/$postId', params: { postId } })
}
}
}
🧠 위와 같이 훅을 호출할 때
from옵션을 전달해 이동할 출발 라우트를 지정할 수 있습니다. 반환된navigate함수를 호출할 때마다 전달할 수도 있지만, 잠재적인 오류를 줄이고 입력량도 줄일 수 있으므로 여기에서 전달하는 것이 좋습니다.
navigate 옵션
useNavigate가 반환하는 navigate 함수는 NavigateOptions 인터페이스를 받습니다.
Navigate 컴포넌트
컴포넌트가 마운트될 때 즉시 이동해야 하는 경우가 있습니다. 처음에는 useNavigate와 즉시 실행되는 사이드 이펙트(예: useEffect)를 사용하려 할 수 있지만 그럴 필요는 없습니다. 대신 Navigate 컴포넌트를 렌더링해 같은 결과를 얻을 수 있습니다.
function Component() {
return <Navigate to="/posts/$postId" params={{ postId: 'my-first-post' }} />
}
Navigate 컴포넌트는 컴포넌트가 마운트될 때 즉시 라우트로 이동하는 방법이라고 생각하면 됩니다. 클라이언트 전용 리디렉션을 처리하는 데 유용합니다. 서버를 고려한 리디렉션을 서버에서 올바르게 처리하는 방법을 절대 대체하지 않습니다.
router.navigate
router.navigate 메서드는 useNavigate가 반환하는 navigate 함수와 같으며 동일한 NavigateOptions 인터페이스를 받습니다. useNavigate 훅과 달리 router 인스턴스를 사용할 수 있는 모든 곳에서 사용할 수 있으므로 프레임워크 외부를 포함해 애플리케이션 어디에서나 명령형으로 이동하는 데 유용합니다.
useMatchRoute 및 <MatchRoute>
useMatchRoute 훅과 <MatchRoute> 컴포넌트는 같은 기능을 하지만 훅이 조금 더 유연합니다. 둘 다 표준 내비게이션 ToOptions 인터페이스를 옵션 또는 props로 받아 현재 라우트가 매칭되었는지 확인합니다. pending 옵션은 라우트가 현재 대기 중인지(예: 라우터가 해당 라우트로 전환 중인지) 확인합니다. 사용자가 이동 중인 위치 주변에 낙관적 UI를 표시할 때 매우 유용합니다.
function Component() {
return (
<div>
<Link to="/users">
Users
<MatchRoute to="/users" pending>
<Spinner />
</MatchRoute>
</Link>
</div>
)
}
컴포넌트 버전인 <MatchRoute>는 자식으로 함수를 사용해 라우트가 매칭될 때 무언가를 렌더링할 수도 있습니다.
function Component() {
return (
<div>
<Link to="/users">
Users
<MatchRoute to="/users" pending>
{(match) => {
return <Spinner show={match} />
}}
</MatchRoute>
</Link>
</div>
)
}
훅 버전인 useMatchRoute는 라우트가 매칭되었는지 확인하는 함수를 반환합니다. 매칭에 사용되는 라우터 상태를 컴포넌트가 구독하도록 하므로 결과가 렌더링에 영향을 주거나 이펙트를 트리거할 때 사용합니다. 이 구독으로 현재 위치나 대기 중인 위치가 변경될 때 컴포넌트가 업데이트됩니다.
function Component() {
const matchRoute = useMatchRoute()
useEffect(() => {
if (matchRoute({ to: '/users', pending: true })) {
console.info('The /users route is matched and pending')
}
}, [matchRoute])
return (
<div>
<Link to="/users">Users</Link>
</div>
)
}
useMatchRoute가 반환하는 matchRoute 함수는 관련 라우터 상태가 변경될 때 식별자가 바뀝니다. 이벤트가 발생한 시점에만 라우트를 확인하면 된다면 useRouter가 반환하는 안정적인 router 인스턴스를 사용하고 router.matchRoute를 직접 호출합니다. 이렇게 하면 렌더링 중 사용하지 않는 매칭 상태를 컴포넌트가 구독하지 않고 최신 라우터 상태를 읽을 수 있습니다.
function Component() {
const router = useRouter()
return (
<button
onClick={() => {
if (router.matchRoute({ to: '/users' }, { fuzzy: true })) {
console.info('The users route is active')
}
}}
>
Check current route
</button>
)
}
휴, 내비게이션이 정말 많았습니다! 그래도 이제 애플리케이션 안에서 이동하는 방법을 충분히 이해했기를 바랍니다. 다음으로 넘어가겠습니다!