본문으로 건너뛰기

라우트 마스킹

라우트 마스킹은 브라우저 기록과 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 옵션으로 만드는 예시입니다.

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],
})

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

  • 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 옵션 전달