본문으로 건너뛰기

일반적인 라우터 문제 디버깅 방법

이 가이드에서는 라우트 매칭 실패부터 내비게이션 문제와 성능 문제까지 TanStack Router의 일반적인 문제를 디버깅하는 방법을 다룹니다.

빠른 시작

실시간 디버깅에는 TanStack Router DevTools를 사용하고, 필요한 위치에 콘솔 로그를 추가하며, 체계적인 문제 해결 패턴을 따라 라우터 문제를 빠르게 식별하고 해결합니다.


필수 디버깅 도구

1. TanStack Router DevTools

최상의 디버깅 경험을 위해 DevTools를 설치하고 구성합니다.

npm install @tanstack/router-devtools
// src/App.tsx
import { TanStackRouterDevtools } from '@tanstack/router-devtools'

function App() {
return (
<div>
<RouterProvider router={router} />
{/* Only shows in development */}
<TanStackRouterDevtools router={router} />
</div>
)
}

DevTools 기능:

  • 라우트 트리 시각화 - 전체 라우트 구조 확인
  • 현재 라우트 상태 - 활성 라우트 데이터, params 및 search 검사
  • 내비게이션 기록 - 내비게이션 이벤트와 타이밍 추적
  • 라우트 매칭 - 현재 URL과 매칭되는 라우트 확인
  • 성능 지표 - 라우트 로드 시간과 다시 렌더링되는 횟수 모니터링

2. 디버그 모드 구성

자세한 콘솔 로깅을 위해 디버그 모드를 활성화합니다.

const router = createRouter({
routeTree,
defaultPreload: 'intent',
context: {
// your context
},
// Enable debug mode
debug: true,
})

3. 브라우저 DevTools 설정

디버깅을 위해 전역 범위에 라우터 추가:

// In development only
if (import.meta.env.DEV) {
window.router = router
}

// Console debugging commands:
// router.state - current router state
// router.navigate() - programmatic navigation
// router.history - navigation history

라우트 매칭 문제

문제: 라우트를 찾을 수 없음(404)

증상:

  • 라우트가 존재하지만 404 또는 "Not Found"가 표시됨
  • 콘솔에 라우트 매칭 실패가 표시됨

디버깅 단계:

  1. 라우트 경로 정의 확인
// ❌ Common mistake - missing leading slash
const route = createRoute({
path: 'about', // Should be '/about'
// ...
})

// ✅ Correct
const route = createRoute({
path: '/about',
// ...
})
  1. 라우트 트리 구조 확인
// Debug route tree in console
console.log('Route tree:', router.routeTree)
console.log('All routes:', router.routesById)
  1. 부모 라우트 구성 확인
// Ensure parent route is properly defined
const childRoute = createRoute({
getParentRoute: () => parentRoute, // Must return correct parent
path: '/child',
// ...
})

문제: 라우트 매개변수가 작동하지 않음

증상:

  • useParams()가 undefined 또는 잘못된 값을 반환함
  • 라우트 params가 올바르게 파싱되지 않음

디버깅 단계:

  1. 매개변수 구문 확인
// ❌ Wrong parameter syntax
path: '/users/{id}' // Should use $

// ✅ Correct parameter syntax
path: '/users/$userId'
  1. 매개변수 파싱 확인
const route = createRoute({
path: '/users/$userId',
// Add parameter validation/parsing
params: {
parse: (params) => ({
userId: Number(params.userId), // Convert to number
}),
stringify: (params) => ({
userId: String(params.userId), // Convert back to string
}),
},
component: () => {
const { userId } = Route.useParams()
console.log('User ID:', userId, typeof userId) // Debug output
return <div>User {userId}</div>
},
})
  1. 현재 URL 및 params 디버깅
function DebugParams() {
const location = useLocation()
const params = Route.useParams()

console.log('Current pathname:', location.pathname)
console.log('Parsed params:', params)

return null // Just for debugging
}

문제: 내비게이션이 작동하지 않음

증상:

  • 링크를 클릭해도 이동하지 않음
  • 프로그래밍 방식 내비게이션이 조용히 실패함
  • 브라우저 URL이 업데이트되지 않음

디버깅 단계:

  1. 링크 구성 확인
// ❌ Common mistakes
<Link to="about">About</Link> // Missing leading slash
<Link href="/about">About</Link> // Wrong prop (href instead of to)

// ✅ Correct
<Link to="/about">About</Link>
  1. 내비게이션 호출 디버깅
function NavigationDebug() {
const navigate = useNavigate()

const handleNavigate = () => {
console.log('Attempting navigation...')
navigate({
to: '/dashboard',
search: { tab: 'settings' },
})
.then(() => console.log('Navigation successful'))
.catch((err) => console.error('Navigation failed:', err))
}

return <button onClick={handleNavigate}>Navigate</button>
}
  1. 라우터 컨텍스트 확인
// Ensure component is inside RouterProvider
function ComponentWithNavigation() {
const router = useRouter() // Will throw error if outside provider
console.log('Router state:', router.state)

return <div>...</div>
}

문제: 내비게이션이 예기치 않게 리디렉션됨

증상:

  • 한 라우트로 이동했지만 다른 곳에 도착함
  • 무한 리디렉션 루프

디버깅 단계:

  1. 라우트 가드 확인
const route = createRoute({
path: '/dashboard',
beforeLoad: ({ context, location }) => {
console.log('Before load - location:', location.pathname)
console.log('Auth state:', context.auth)

if (!context.auth.isAuthenticated) {
console.log('Redirecting to login...')
throw redirect({ to: '/login' })
}
},
// ...
})
  1. 리디렉션 체인 디버깅
// Add to router configuration
const router = createRouter({
routeTree,
context: {
/* ... */
},
// Log all navigation events
onNavigate: ({ location, type }) => {
console.log(`Navigation (${type}):`, location.pathname)
},
})

데이터 로딩 문제

문제: 라우트 데이터가 로드되지 않음

증상:

  • useLoaderData()가 undefined를 반환함
  • 로딩 상태가 올바르게 작동하지 않음
  • 데이터가 새로고침되지 않음

디버깅 단계:

  1. 로더 구현 확인
const route = createRoute({
path: '/posts',
loader: async ({ params, context }) => {
console.log('Loader called with params:', params)

try {
const data = await fetchPosts()
console.log('Loader data:', data)
return data
} catch (error) {
console.error('Loader error:', error)
throw error
}
},
component: () => {
const data = Route.useLoaderData()
console.log('Component data:', data)

return <div>{/* render data */}</div>
},
})
  1. 로딩 상태 디버깅
function DataLoadingDebug() {
const state = useRouterState()

console.log('Route status:', {
status: state.status,
isLoading: state.isLoading,
matches: state.matches.map((match) => ({
routeId: match.routeId,
status: match.status,
isFetching: match.isFetching,
})),
})

return null
}
  1. 로더 종속성 확인
const route = createRoute({
path: '/posts/$postId',
loader: async ({ params }) => {
// Loader will re-run when params change
console.log('Loading post:', params.postId)
return fetchPost(params.postId)
},
// Add dependencies for explicit re-loading
loaderDeps: ({ search }) => ({
refresh: search.refresh,
}),
})

검색 매개변수 문제

문제: 검색 매개변수가 업데이트되지 않음

증상:

  • URL 검색 매개변수가 업데이트되지 않음
  • useSearch()가 오래된 데이터를 반환함
  • 검색 검증 오류

디버깅 단계:

  1. 검색 검증 스키마 확인
const route = createRoute({
path: '/search',
validateSearch: (search) => {
console.log('Raw search params:', search)

const validated = {
q: (search.q as string) || '',
page: Number(search.page) || 1,
}

console.log('Validated search params:', validated)
return validated
},
component: () => {
const search = Route.useSearch()
console.log('Component search:', search)

return <div>Query: {search.q}</div>
},
})
  1. 검색 내비게이션 디버깅
function SearchDebug() {
const navigate = useNavigate()
const currentSearch = Route.useSearch()

const updateSearch = (newSearch: any) => {
console.log('Current search:', currentSearch)
console.log('New search:', newSearch)

navigate({
to: '.',
search: (prev) => {
const updated = { ...prev, ...newSearch }
console.log('Final search:', updated)
return updated
},
})
}

return (
<button onClick={() => updateSearch({ q: 'test' })}>Update Search</button>
)
}

성능 문제

문제: 과도한 다시 렌더링

증상:

  • 컴포넌트가 너무 자주 다시 렌더링됨
  • 내비게이션 중 성능 지연
  • 메모리 사용량 증가

디버깅 단계:

  1. React DevTools Profiler 사용
// Wrap your app for profiling
import { Profiler } from 'react'

function App() {
return (
<Profiler
id="Router"
onRender={(id, phase, actualDuration) => {
console.log(`${id} ${phase} took ${actualDuration}ms`)
}}
>
<RouterProvider router={router} />
</Profiler>
)
}
  1. 라우트 구독 최적화
// ❌ Subscribes to all search params
function MyComponent() {
const search = Route.useSearch()
return <div>{search.someSpecificField}</div>
}

// ✅ Subscribe only to specific field
function MyComponent() {
const someSpecificField = Route.useSearch({
select: (search) => search.someSpecificField,
})
return <div>{someSpecificField}</div>
}
  1. 라우트 상태 변경 모니터링
// Add to router configuration
const router = createRouter({
routeTree,
context: {
/* ... */
},
onUpdate: (router) => {
console.log('Router state updated:', {
pathname: router.state.location.pathname,
isLoading: router.state.isLoading,
matches: router.state.matches.length,
})
},
})

문제: 메모리 누수

증상:

  • 메모리 사용량이 계속 증가함
  • 시간이 지나면서 브라우저가 느려짐
  • 라우트 컴포넌트가 정리되지 않음

디버깅 단계:

  1. 컴포넌트 정리 확인
function MyComponent() {
const [data, setData] = useState(null)

useEffect(() => {
const subscription = someService.subscribe(setData)

// ✅ Always clean up subscriptions
return () => {
subscription.unsubscribe()
}
}, [])

return <div>{data}</div>
}
  1. 라우트 언마운트 모니터링
function DebuggableComponent() {
useEffect(() => {
console.log('Component mounted')

return () => {
console.log('Component unmounted')
}
}, [])

return <div>Content</div>
}

TypeScript 문제

문제: 라우터의 타입 오류

증상:

  • 라우트 정의에서 TypeScript 오류 발생
  • 타입 추론이 작동하지 않음
  • 매개변수 타입이 잘못됨

디버깅 단계:

  1. 라우트 트리 타입 등록 확인
// Ensure this declaration exists
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}
  1. 라우트 타입 생성 디버깅
# Check if route types are being generated
ls src/routeTree.gen.ts

# Regenerate route types if needed
npx @tanstack/router-cli generate
  1. 디버깅에 타입 단언 사용
function TypeDebugComponent() {
const params = Route.useParams()
const search = Route.useSearch()

// Add type assertions to check what TypeScript infers
console.log('Params type:', params as any)
console.log('Search type:', search as any)

return null
}

체계적인 디버깅 프로세스

1. 정보 수집

라우터 문제를 디버깅할 때는 다음 정보를 수집하는 것부터 시작합니다.

function RouterDebugInfo() {
const router = useRouter()
const location = useLocation()

useEffect(() => {
console.group('🐛 Router Debug Info')
console.log('Current pathname:', location.pathname)
console.log('Search params:', location.search)
console.log('Router state:', router.state)
console.log('Active matches:', router.state.matches)
console.log('Route tree:', router.routeTree)
console.groupEnd()
}, [location.pathname])

return null
}

// Add to your app during debugging
;<RouterDebugInfo />

2. 격리 테스트

최소 재현을 만듭니다.

// Minimal route for testing
const testRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/debug',
component: () => {
console.log('Test route rendered')
return <div>Debug Route</div>
},
})

// Add to route tree temporarily
const routeTree = rootRoute.addChildren([
// ... other routes
testRoute, // Add test route
])

3. 단계별 디버깅

  1. 기본 설정 확인 - 라우터 공급자, 라우트 트리 구조
  2. 라우트 정의 확인 - 경로, 부모 라우트, 구성
  3. 내비게이션 테스트 - 링크, 프로그래밍 방식 내비게이션
  4. 데이터 흐름 검증 - 로더, 검색 매개변수, 컨텍스트
  5. 성능 모니터링 - 다시 렌더링, 메모리 사용량

브라우저 디버깅 팁

콘솔 명령

// In browser console (when router is on window)

// Current router state
router.state

// Navigate programmatically
router.navigate({ to: '/some-path' })

// Get route by path
router.getRoute('/users/$userId')

// Check if route exists
router.buildLocation({ to: '/some-path' })

// View all registered routes
Object.keys(router.routesById)

네트워크 탭

디버깅할 때 다음 요청을 모니터링합니다.

  • 라우트 코드 청크 - 지연 로딩 라우트가 로드되는지 확인
  • 로더 데이터 요청 - 로더의 API 호출 확인
  • 실패한 요청 - 404 또는 실패한 API 호출 확인

React DevTools

  1. Components 탭 - 라우터 컴포넌트를 찾고 props 검사
  2. Profiler 탭 - 성능 병목 식별
  3. 컴포넌트 검색 - 특정 라우트 컴포넌트를 빠르게 찾기

일반적인 오류 메시지

"Route not found"

  • 라우트 경로의 철자와 대소문자 구분 확인
  • 라우트가 라우트 트리에 추가되었는지 확인
  • 부모 라우트가 올바르게 구성되었는지 확인

"Cannot read property 'useParams' of undefined"

  • 컴포넌트가 RouterProvider 외부에 있을 가능성이 큼
  • 라우트가 올바르게 등록되지 않았을 수 있음
  • 올바른 Route 객체를 사용하는지 확인

"Invalid search params"

  • validateSearch 스키마 확인
  • 검색 매개변수 타입이 스키마와 일치하는지 확인
  • 필수 매개변수와 선택적 매개변수 구분 확인
  • 일반적으로 beforeLoad의 리디렉션으로 인해 발생
  • 리디렉션 루프 확인
  • 인증 로직 확인

성능 모니터링

성능 추적 활성화

const router = createRouter({
routeTree,
context: {
/* ... */
},
onUpdate: (router) => {
performance.mark('router-update')
},
onLoad: (router) => {
performance.mark('router-load')
performance.measure('router-load-time', 'router-update', 'router-load')
},
})

라우트 로딩 시간 모니터링

const route = createRoute({
path: '/slow-route',
loader: async () => {
const start = performance.now()
const data = await fetchData()
const end = performance.now()

console.log(`Loader took ${end - start}ms`)
return data
},
})

일반적인 다음 단계

라우터 문제를 디버깅한 후 다음 작업을 진행할 수 있습니다.