본문으로 건너뛰기

라우트 마스킹

라우트 마스킹은 브라우저 기록과 URL 표시줄에 저장되는 라우트의 실제 URL을 가리는 방법입니다. 실제로 이동하는 URL과 다른 URL을 표시한 다음, URL을 공유할 때와 (선택적으로) 페이지를 다시 로드할 때 표시된 URL로 돌아가려는 상황에 유용합니다. 몇 가지 예시는 다음과 같습니다.

  • /photo/5/modal과 같은 모달 라우트로 이동하지만 실제 URL은 /photos/5로 가립니다.
  • /post/5/comments와 같은 모달 라우트로 이동하지만 실제 URL은 /posts/5로 가립니다.
  • ?showLogin=true 검색 매개변수가 있는 라우트로 이동하지만 URL에서는 검색 매개변수가 표시되지 않도록 가립니다.
  • ?modal=settings 검색 매개변수가 있는 라우트로 이동하지만 URL은 `/settings'로 가립니다.

이러한 상황은 모두 라우트 마스킹으로 구현할 수 있으며 병렬 라우트와 같은 고급 패턴을 지원하도록 확장할 수도 있습니다.

라우트 마스킹은 어떻게 작동하나요?​

[!IMPORTANT] 라우트 마스킹을 사용하기 위해 작동 방식을 이해할 필요는 없습니다. 이 섹션은 내부 작동 방식이 궁금한 경우를 위한 것입니다. 사용 방법을 알아보려면 라우트 마스킹은 어떻게 사용하나요?로 건너뜁니다!.

라우트 마스킹은 location.state API를 사용해 URL에 기록될 위치 안에 원하는 런타임 위치를 저장합니다. 이 런타임 위치를 __tempLocation 상태 속성에 저장합니다.

const location = {
pathname: '/photos/5',
search: '',
hash: '',
state: {
key: 'wesdfs',
__tempKey: 'sadfasd',
__tempLocation: {
pathname: '/photo/5/modal',
search: '',
hash: '',
state: {},
},
},
}

라우터가 location.state.__tempLocation 속성이 있는 위치를 기록에서 파싱하면 URL에서 파싱한 위치 대신 해당 위치를 사용합니다. 따라서 /photos/5와 같은 라우트로 이동해도 라우터가 실제로는 /photo/5/modal로 이동하도록 할 수 있습니다. 이때 실제 URL을 알아야 할 경우를 대비해 기록 위치를 location.maskedLocation 속성에 다시 저장합니다. Devtools에서 라우트가 마스킹되었는지 감지하고 마스킹된 URL 대신 실제 URL을 표시할 때 이 기능을 사용합니다!

이 모든 내용을 신경 쓸 필요는 없습니다. 내부에서 모두 자동으로 처리됩니다!

라우트 마스킹은 어떻게 사용하나요?​

라우트 마스킹은 다음 두 가지 방법으로 사용할 수 있는 간단한 API입니다.

  • <Link> 및 navigate() API에서 사용할 수 있는 mask 옵션으로 명령형으로 사용
  • Router의 routeMasks 옵션으로 선언적으로 사용

두 라우트 마스킹 API 중 하나를 사용할 때 mask 옵션은 <Link> 및 navigate() API가 받는 것과 동일한 내비게이션 객체를 받습니다. 따라서 익숙한 to, replace, state, search 옵션을 그대로 사용할 수 있습니다. 유일한 차이는 mask 옵션이 이동 대상 라우트의 URL을 가리는 데 사용된다는 점입니다.

🧠 mask 옵션도 타입 안전합니다! TypeScript를 사용한다면 mask 옵션에 잘못된 내비게이션 객체를 전달할 때 타입 오류가 발생합니다. 좋습니다!

명령형 라우트 마스킹​

<Link> 및 navigate() API는 모두 이동 대상 라우트의 URL을 가리는 데 사용할 수 있는 mask 옵션을 받습니다. 다음은 <Link> 컴포넌트에서 사용하는 예시입니다.

<Link
to="/photos/$photoId/modal"
params={{ photoId: 5 }}
mask={{
to: '/photos/$photoId',
params: {
photoId: 5,
},
}}
>
Open Photo
</Link>

다음은 navigate() API에서 사용하는 예시입니다.

const navigate = useNavigate()

function onOpenPhoto() {
navigate({
to: '/photos/$photoId/modal',
params: { photoId: 5 },
mask: {
to: '/photos/$photoId',
params: {
photoId: 5,
},
},
})
}

선언적 라우트 마스킹​

명령형 API 외에도 Router의 routeMasks 옵션을 사용해 라우트를 선언적으로 마스킹할 수 있습니다. 모든 라우트에 mask 옵션을 전달하는 대신 <Link> 또는 navigate() 호출마다 Router에 라우트 마스크를 만들고 특정 패턴과 매칭되는 라우트를 가릴 수 있습니다. 다음은 앞의 라우트 마스크를 routeMasks 옵션으로 만드는 예시입니다.

<!-- ::start:framework -->

React

import { createRouteMask } from '@tanstack/react-router'

const photoModalToPhotoMask = createRouteMask({
routeTree,
from: '/photos/$photoId/modal',
to: '/photos/$photoId',
params: (prev) => ({
photoId: prev.photoId,
}),
})

const router = createRouter({
routeTree,
routeMasks: [photoModalToPhotoMask],
})

Solid

import { createRouteMask } from '@tanstack/solid-router'

const photoModalToPhotoMask = createRouteMask({
routeTree,
from: '/photos/$photoId/modal',
to: '/photos/$photoId',
params: (prev) => ({
photoId: prev.photoId,
}),
})

const router = createRouter({
routeTree,
routeMasks: [photoModalToPhotoMask],
})

<!-- ::end:framework -->

라우트 마스크를 만들 때 최소한 다음 인수를 포함한 인수 하나를 전달해야 합니다.

  • routeTree - 라우트 마스크를 적용할 라우트 트리
  • from - 라우트 마스크를 적용할 라우트 ID
  • ...navigateOptions - 표준 to, search, params, replace 등의 옵션으로 <Link> 및 navigate() API가 받습니다.

🧠 createRouteMask 옵션도 타입 안전합니다! TypeScript를 사용한다면 routeMasks 옵션에 잘못된 라우트 마스크를 전달할 때 타입 오류가 발생합니다.

URL 공유 시 마스킹 해제​

URL은 공유할 때 자동으로 마스킹이 해제됩니다. URL이 브라우저의 로컬 기록 스택에서 분리되는 즉시 URL 마스킹 데이터를 더 이상 사용할 수 없기 때문입니다. 즉 기록에서 URL을 복사해 붙여 넣는 순간 마스킹 데이터가 사라집니다. 결국 이것이 URL을 마스킹하는 목적입니다!

로컬 마스킹 해제 기본값​

기본적으로 페이지를 로컬에서 다시 로드해도 URL의 마스킹이 해제되지 않습니다. 마스킹 데이터는 기록 위치의 location.state 속성에 저장되므로 기록 스택에 기록 위치가 메모리에 남아 있는 동안 마스킹 데이터를 사용할 수 있고 URL은 계속 마스킹된 상태로 유지됩니다.

페이지 다시 로드 시 마스킹 해제​

앞서 설명했듯이 기본적으로 페이지를 다시 로드해도 URL의 마스킹이 해제되지 않습니다.

페이지를 다시 로드할 때 로컬에서 URL의 마스킹을 해제하려면 다음 세 가지 옵션이 있으며, 여러 옵션을 전달하면 우선순위가 앞선 옵션을 재정의합니다.

  • Router의 기본 unmaskOnReload 옵션을 true로 설정
  • createRouteMask()로 라우트 마스크를 만들 때 마스킹 함수에서 unmaskOnReload: true 옵션을 반환
  • <Link> 컴포넌트 또는 navigate() API에 unmaskOnReload: true 옵션 전달