본문으로 건너뛰기

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

이 가이드에서는 라우트 매칭 실패부터 내비게이션 문제와 성능 문제까지 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
},
})

일반적인 다음 단계​

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

<!-- TODO: Uncomment as guides are created