인증된 라우트
웹 애플리케이션에서 인증은 매우 일반적인 요구 사항입니다. 이 가이드에서는 TanStack Router를 사용해 보호된 라우트를 만들고, 사용자가 보호된 라우트에 접근하려 할 때 로그인으로 리디렉션하는 방법을 살펴봅니다.
라우트 가드는 데이터 권한 부여 경계가 아닙니다. 이 가이드에서는
beforeLoad뒤에서 라우트 UI에 대한 접근을 제한하는 방법을 보여줍니다. 비공개 데이터를 반환하는 모든 서버 함수, 서버 라우트 또는 API 엔드포인트는 요청 자체를 인증해야 합니다. 해당 요청은 이를 호출하는 라우트와 독립적으로 요청될 수 있기 때문입니다. Start 문서의 인증 서버 기본 요소를 참고합니다.
route.beforeLoad 옵션
route.beforeLoad 옵션을 사용하면 라우트를 로드하기 전에 호출할 함수를 지정할 수 있습니다. 이 함수는 route.loader 함수와 동일한 모든 인수를 받습니다. 사용자의 인증 여부를 확인하고 인증되지 않은 경우 로그인 페이지로 리디렉션하기에 적합한 위치입니다.
beforeLoad 함수는 다음의 다른 라우트 로딩 함수와 상대적인 순서로 실행됩니다.
- 라우트 매칭(위에서 아래로)
route.params.parseroute.validateSearch
- 라우트 로딩(프리로딩 포함)
route.beforeLoadroute.onError
- 라우트 로딩(병렬)
route.component.preload?route.load
라우트의 beforeLoad 함수는 모든 자식 라우트의 beforeLoad 함수보다 먼저 호출된다는 점이 중요합니다. 이는 해당 라우트와 모든 자식 라우트에 적용되는 미들웨어 함수와 같습니다.
beforeLoad에서 오류를 던지면 자식 라우트는 아무것도 로드하려 하지 않습니다.
리디렉션
필수는 아니지만 일부 인증 흐름에서는 로그인 페이지로 리디렉션해야 합니다. 이 경우 beforeLoad에서 redirect()를 던질 수 있습니다.
// src/routes/_authenticated.tsx
export const Route = createFileRoute('/_authenticated')({
beforeLoad: async ({ location }) => {
if (!isAuthenticated()) {
throw redirect({
to: '/login',
search: {
// Use the current location to power a redirect after login
// (Do not use `router.state.resolvedLocation` as it can
// potentially lag behind the actual current location)
redirect: location.href,
},
})
}
},
})
[!TIP]
redirect()함수는navigate함수와 동일한 모든 옵션을 받습니다. 따라서 새 기록 항목을 추가하는 대신 현재 기록 항목을 바꾸려면replace: true와 같은 옵션을 전달할 수 있습니다.
인증 확인 실패 처리
인증 확인에서 오류(네트워크 실패, 토큰 검증 등)가 발생할 수 있다면 try/catch로 감쌉니다.
React
import { createFileRoute, redirect, isRedirect } from '@tanstack/react-router'
// src/routes/_authenticated.tsx
export const Route = createFileRoute('/_authenticated')({
beforeLoad: async ({ location }) => {
try {
const user = await verifySession() // might throw on network error
if (!user) {
throw redirect({
to: '/login',
search: { redirect: location.href },
})
}
return { user }
} catch (error) {
// Re-throw redirects (they're intentional, not errors)
if (isRedirect(error)) throw error
// Auth check failed (network error, etc.) - redirect to login
throw redirect({
to: '/login',
search: { redirect: location.href },
})
}
},
})
Solid
import { createFileRoute, redirect, isRedirect } from '@tanstack/solid-router'
// src/routes/_authenticated.tsx
export const Route = createFileRoute('/_authenticated')({
beforeLoad: async ({ location }) => {
try {
const user = await verifySession() // might throw on network error
if (!user) {
throw redirect({
to: '/login',
search: { redirect: location.href },
})
}
return { user }
} catch (error) {
// Re-throw redirects (they're intentional, not errors)
if (isRedirect(error)) throw error
// Auth check failed (network error, etc.) - redirect to login
throw redirect({
to: '/login',
search: { redirect: location.href },
})
}
},
})
isRedirect() 헬퍼는 실제 오류와 의도적인 리디렉션을 구분합니다.
사용자를 인증한 후 접근하려던 페이지로 다시 리디렉션하는 것도 일반적인 방식입니다. 이를 위해 최초 리디렉션에서 추가한 redirect 검색 매개변수를 활용할 수 있습니다. 전체 URL을 원래 값으로 바꾸므로 이 경우에는 router.navigate보다 router.history.push가 더 적합합니다.
router.history.push(search.redirect)
리디렉션하지 않는 인증
일부 애플리케이션은 사용자를 로그인 페이지로 리디렉션하지 않고 같은 페이지에 둔 채, 주 콘텐츠를 대체하거나 모달로 숨기는 로그인 폼을 표시합니다. TanStack Router에서도 일반적으로 자식 라우트를 렌더링하는 <Outlet />의 렌더링을 간단히 중단해 이 방식을 사용할 수 있습니다.
// src/routes/_authenticated.tsx
export const Route = createFileRoute('/_authenticated')({
component: () => {
if (!isAuthenticated()) {
return <Login />
}
return <Outlet />
},
})
이렇게 하면 사용자를 같은 페이지에 유지하면서 로그인 폼을 렌더링할 수 있습니다. 사용자가 인증되면 <Outlet />을 렌더링하기만 하면 자식 라우트가 렌더링됩니다.
React 컨텍스트/훅을 사용한 인증
인증 흐름이 React 컨텍스트 및/또는 훅과의 상호작용에 의존한다면 router.context 옵션을 사용해 인증 상태를 TanStack Router에 전달해야 합니다.
[!IMPORTANT] React 훅은 React 컴포넌트 외부에서 사용하도록 설계되지 않았습니다. React 컴포넌트 외부에서 훅을 사용해야 한다면
<RouterProvider />를 감싸는 컴포넌트에서 훅의 반환 상태를 추출한 다음 반환값을 TanStack Router에 전달해야 합니다.
router.context 옵션은 라우터 컨텍스트 섹션에서 자세히 다룹니다.
다음은 React 컨텍스트와 훅을 사용해 TanStack Router에서 인증된 라우트를 보호하는 예시입니다. 전체 동작 설정은 인증된 라우트 예시에서 확인할 수 있습니다.
React
import { createRootRouteWithContext } from '@tanstack/react-router'
interface MyRouterContext {
// The ReturnType of your useAuth hook or the value of your AuthContext
auth: AuthState
}
export const Route = createRootRouteWithContext<MyRouterContext>()({
component: () => <Outlet />,
})
import { createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export const router = createRouter({
routeTree,
context: {
// auth will initially be undefined
// We'll be passing down the auth state from within a React component
auth: undefined!,
},
})
import { RouterProvider } from '@tanstack/react-router'
import { AuthProvider, useAuth } from './auth'
import { router } from './router'
function InnerApp() {
const auth = useAuth()
return <RouterProvider router={router} context={{ auth }} />
}
function App() {
return (
<AuthProvider>
<InnerApp />
</AuthProvider>
)
}
Solid
import { createRootRouteWithContext } from '@tanstack/solid-router'
interface MyRouterContext {
// The ReturnType of your useAuth hook or the value of your AuthContext
auth: AuthState
}
export const Route = createRootRouteWithContext<MyRouterContext>()({
component: () => <Outlet />,
})
import { createRouter } from '@tanstack/solid-router'
import { routeTree } from './routeTree.gen'
export const router = createRouter({
routeTree,
context: {
// auth will initially be undefined
// We'll be passing down the auth state from within a React component
auth: undefined!,
},
})
import { RouterProvider } from '@tanstack/solid-router'
import { AuthProvider, useAuth } from './auth'
import { router } from './router'
function InnerApp() {
const auth = useAuth()
return <RouterProvider router={router} context={{ auth }} />
}
function App() {
return (
<AuthProvider>
<InnerApp />
</AuthProvider>
)
}
그런 다음 인증된 라우트에서 beforeLoad 함수를 사용해 인증 상태를 확인하고, 사용자가 로그인하지 않았다면 로그인 라우트로 redirect()를 던질 수 있습니다.
React
import { createFileRoute, redirect } from '@tanstack/react-router'
export const Route = createFileRoute('/dashboard')({
beforeLoad: ({ context, location }) => {
if (!context.auth.isAuthenticated) {
throw redirect({
to: '/login',
search: {
redirect: location.href,
},
})
}
},
})
Solid
import { createFileRoute, redirect } from '@tanstack/solid-router'
export const Route = createFileRoute('/dashboard')({
beforeLoad: ({ context, location }) => {
if (!context.auth.isAuthenticated()) {
throw redirect({
to: '/login',
search: {
redirect: location.href,
},
})
}
},
})
리디렉션을 호출하는 대신 리디렉션하지 않는 인증 방식을 사용해 로그인 폼을 표시할 수도 있습니다.
이 방식은 Pathless 또는 Layout Route와 함께 사용해 부모 라우트 아래의 모든 라우트를 보호할 수도 있습니다.
관련 사용 방법 가이드
단계별 자세한 구현 가이드는 다음을 참고합니다.
- 기본 인증 설정 방법 - React 컨텍스트와 보호된 라우트를 사용한 전체 설정
- 인증 공급자 통합 방법 - Auth0, Clerk 또는 Supabase 사용
- 역할 기반 접근 제어 설정 방법 - 권한 및 역할 기반 라우팅 구현
예시
저장소에서 동작하는 인증 예시를 확인할 수 있습니다.
- 기본 인증 예시 - 컨텍스트를 사용한 간단한 인증
- Firebase 인증 - Firebase Auth 통합
- TanStack Start 인증 예시 - TanStack Start를 사용한 다양한 인증 구현