@kood/claude-code 0.6.5 → 0.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/index.js +255 -149
  2. package/package.json +1 -1
  3. package/templates/.claude/agents/researcher.md +8 -1
  4. package/templates/.claude/instructions/sourcing/reliable-search.md +49 -2
  5. package/templates/.claude/scripts/deploy/build-run.sh +36 -0
  6. package/templates/.claude/scripts/deploy/deploy-check.sh +38 -0
  7. package/templates/.claude/scripts/git/git-all.sh +57 -0
  8. package/templates/.claude/scripts/git/git-clean-check.sh +31 -0
  9. package/templates/.claude/scripts/git/git-commit.sh +51 -0
  10. package/templates/.claude/scripts/git/git-info.sh +34 -0
  11. package/templates/.claude/scripts/git/git-push.sh +50 -0
  12. package/templates/.claude/scripts/lint/lint-check.sh +56 -0
  13. package/templates/.claude/scripts/lint/lint-file.sh +41 -0
  14. package/templates/.claude/scripts/pm/pm-detect.sh +25 -0
  15. package/templates/.claude/scripts/pm/pm-run.sh +41 -0
  16. package/templates/.claude/scripts/version/version-bump.sh +54 -0
  17. package/templates/.claude/scripts/version/version-find.sh +49 -0
  18. package/templates/.claude/skills/docs-fetch/SKILL.md +5 -4
  19. package/templates/.claude/skills/project-optimizer/AGENTS.md +275 -0
  20. package/templates/.claude/skills/project-optimizer/SKILL.md +374 -0
  21. package/templates/.claude/skills/project-optimizer/rules/arch-config-centralize.md +66 -0
  22. package/templates/.claude/skills/project-optimizer/rules/arch-hot-path.md +35 -0
  23. package/templates/.claude/skills/project-optimizer/rules/arch-interface-segregation.md +51 -0
  24. package/templates/.claude/skills/project-optimizer/rules/arch-module-boundary.md +42 -0
  25. package/templates/.claude/skills/project-optimizer/rules/build-cache.md +57 -0
  26. package/templates/.claude/skills/project-optimizer/rules/build-code-split.md +56 -0
  27. package/templates/.claude/skills/project-optimizer/rules/build-incremental.md +65 -0
  28. package/templates/.claude/skills/project-optimizer/rules/build-minify.md +61 -0
  29. package/templates/.claude/skills/project-optimizer/rules/build-tree-shake.md +60 -0
  30. package/templates/.claude/skills/project-optimizer/rules/code-complexity.md +65 -0
  31. package/templates/.claude/skills/project-optimizer/rules/code-dead-elimination.md +32 -0
  32. package/templates/.claude/skills/project-optimizer/rules/code-duplication.md +54 -0
  33. package/templates/.claude/skills/project-optimizer/rules/code-error-handling.md +75 -0
  34. package/templates/.claude/skills/project-optimizer/rules/code-naming.md +52 -0
  35. package/templates/.claude/skills/project-optimizer/rules/concurrency-defer-await.md +54 -0
  36. package/templates/.claude/skills/project-optimizer/rules/concurrency-parallel.md +90 -0
  37. package/templates/.claude/skills/project-optimizer/rules/concurrency-pipeline.md +68 -0
  38. package/templates/.claude/skills/project-optimizer/rules/concurrency-pool.md +68 -0
  39. package/templates/.claude/skills/project-optimizer/rules/deps-lightweight-alt.md +37 -0
  40. package/templates/.claude/skills/project-optimizer/rules/deps-peer-align.md +44 -0
  41. package/templates/.claude/skills/project-optimizer/rules/deps-security-audit.md +45 -0
  42. package/templates/.claude/skills/project-optimizer/rules/deps-unused-removal.md +25 -0
  43. package/templates/.claude/skills/project-optimizer/rules/deps-version-pin.md +40 -0
  44. package/templates/.claude/skills/project-optimizer/rules/dx-ci-speed.md +47 -0
  45. package/templates/.claude/skills/project-optimizer/rules/dx-dev-server.md +35 -0
  46. package/templates/.claude/skills/project-optimizer/rules/dx-lint-config.md +36 -0
  47. package/templates/.claude/skills/project-optimizer/rules/dx-test-coverage.md +34 -0
  48. package/templates/.claude/skills/project-optimizer/rules/dx-type-safety.md +49 -0
  49. package/templates/.claude/skills/project-optimizer/rules/io-batch-queries.md +67 -0
  50. package/templates/.claude/skills/project-optimizer/rules/io-cache-layer.md +67 -0
  51. package/templates/.claude/skills/project-optimizer/rules/io-connection-reuse.md +67 -0
  52. package/templates/.claude/skills/project-optimizer/rules/io-serialize-minimal.md +61 -0
  53. package/templates/.claude/skills/project-optimizer/rules/io-stream.md +75 -0
  54. package/templates/.claude/skills/project-optimizer/rules/memory-bounded-cache.md +65 -0
  55. package/templates/.claude/skills/project-optimizer/rules/memory-large-data.md +64 -0
  56. package/templates/.claude/skills/project-optimizer/rules/memory-lazy-init.md +78 -0
  57. package/templates/.claude/skills/project-optimizer/rules/memory-leak-prevention.md +79 -0
  58. package/templates/.claude/skills/project-optimizer/rules/memory-pool-reuse.md +70 -0
  59. package/templates/.claude/skills/sql-optimizer/SKILL.md +437 -0
  60. package/templates/.claude/skills/sql-optimizer/orm-patterns.md +218 -0
  61. package/templates/.claude/skills/tanstack-start-react-best-practices/AGENTS.md +53 -14
  62. package/templates/.claude/skills/tanstack-start-react-best-practices/SKILL.md +93 -27
  63. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-defer-third-party.md +42 -19
  64. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-optimistic-updates.md +109 -0
  65. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-suspense-query.md +74 -0
  66. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-use-hook.md +81 -0
  67. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-react-compiler.md +81 -0
  68. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-beforeload-auth.md +121 -0
  69. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-file-conventions.md +104 -0
  70. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-link-navigation.md +119 -0
  71. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-nested-layouts.md +155 -0
  72. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-path-params.md +89 -0
  73. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-pending-component.md +110 -0
  74. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-preload-strategy.md +91 -0
  75. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-router-context.md +120 -0
  76. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/routing-search-params.md +114 -0
  77. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-deferred-data.md +1 -1
  78. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-error-boundaries.md +79 -0
  79. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-middleware.md +85 -0
  80. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-serialization.md +56 -21
  81. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-streaming.md +84 -0
  82. package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-validator.md +71 -0
  83. package/templates/.claude/skills/tauri-react-best-practices/AGENTS.md +527 -0
  84. package/templates/.claude/skills/tauri-react-best-practices/SKILL.md +570 -0
  85. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-barrel-imports.md +140 -0
  86. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-cargo-profile.md +96 -0
  87. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-frontend-treeshake.md +242 -0
  88. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-lazy-components.md +255 -0
  89. package/templates/.claude/skills/tauri-react-best-practices/rules/bundle-remove-unused-commands.md +160 -0
  90. package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-ci-pipeline.md +269 -0
  91. package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-signing.md +207 -0
  92. package/templates/.claude/skills/tauri-react-best-practices/rules/deploy-updater.md +226 -0
  93. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-async-commands.md +172 -0
  94. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-batch-commands.md +133 -0
  95. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-binary-response.md +198 -0
  96. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-channel-streaming.md +186 -0
  97. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-error-handling.md +250 -0
  98. package/templates/.claude/skills/tauri-react-best-practices/rules/ipc-type-safe.md +227 -0
  99. package/templates/.claude/skills/tauri-react-best-practices/rules/perf-derived-state.md +231 -0
  100. package/templates/.claude/skills/tauri-react-best-practices/rules/perf-functional-setstate.md +191 -0
  101. package/templates/.claude/skills/tauri-react-best-practices/rules/perf-index-maps.md +276 -0
  102. package/templates/.claude/skills/tauri-react-best-practices/rules/perf-lazy-state-init.md +196 -0
  103. package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-lifecycle.md +265 -0
  104. package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-mobile-compat.md +199 -0
  105. package/templates/.claude/skills/tauri-react-best-practices/rules/plugin-permission-scope.md +193 -0
  106. package/templates/.claude/skills/tauri-react-best-practices/rules/react-error-boundary.md +239 -0
  107. package/templates/.claude/skills/tauri-react-best-practices/rules/react-event-listener.md +151 -0
  108. package/templates/.claude/skills/tauri-react-best-practices/rules/react-file-src.md +155 -0
  109. package/templates/.claude/skills/tauri-react-best-practices/rules/react-invoke-hook.md +139 -0
  110. package/templates/.claude/skills/tauri-react-best-practices/rules/react-optimistic-update.md +211 -0
  111. package/templates/.claude/skills/tauri-react-best-practices/rules/security-capability-split.md +205 -0
  112. package/templates/.claude/skills/tauri-react-best-practices/rules/security-csp.md +207 -0
  113. package/templates/.claude/skills/tauri-react-best-practices/rules/security-least-privilege.md +106 -0
  114. package/templates/.claude/skills/tauri-react-best-practices/rules/security-no-wildcard.md +253 -0
  115. package/templates/.claude/skills/tauri-react-best-practices/rules/security-scope-paths.md +160 -0
  116. package/templates/.claude/skills/tauri-react-best-practices/rules/state-async-mutex.md +270 -0
  117. package/templates/.claude/skills/tauri-react-best-practices/rules/state-mutex-pattern.md +265 -0
  118. package/templates/.claude/skills/tauri-react-best-practices/rules/state-react-sync.md +375 -0
  119. package/templates/.claude/skills/tauri-react-best-practices/rules/state-single-container.md +275 -0
  120. package/templates/tanstack-start/docs/architecture.md +238 -167
  121. package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +777 -38
  122. package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +549 -37
  123. package/templates/tanstack-start/docs/library/tanstack-router/index.md +895 -111
  124. package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +641 -43
  125. package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +889 -38
  126. package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +891 -29
  127. package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +972 -36
  128. package/templates/tanstack-start/docs/library/tanstack-start/index.md +1525 -881
  129. package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +1099 -20
  130. package/templates/tanstack-start/docs/library/tanstack-start/routing.md +796 -30
  131. package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +953 -35
  132. package/templates/tanstack-start/docs/library/tanstack-start/setup.md +371 -15
  133. package/templates/tauri/CLAUDE.md +189 -0
  134. package/templates/tauri/docs/guides/distribution.md +261 -0
  135. package/templates/tauri/docs/guides/getting-started.md +302 -0
  136. package/templates/tauri/docs/guides/mobile.md +288 -0
  137. package/templates/tauri/docs/library/tauri/index.md +510 -0
@@ -0,0 +1,155 @@
1
+ ---
2
+ title: Use Outlet and Pathless Layouts for Nested UI
3
+ impact: HIGH
4
+ impactDescription: enables shared UI structure without prop drilling
5
+ tags: routing, outlet, layouts, createRootRoute, nesting
6
+ ---
7
+
8
+ ## Outlet과 Pathless Layout으로 중첩 UI 구현
9
+
10
+ `<Outlet />`으로 하위 라우트를 렌더링하고, pathless layout(`_prefix`)으로 URL 변경 없이 공유 UI를 적용합니다.
11
+
12
+ **❌ 잘못된 예시 (레이아웃 중복):**
13
+
14
+ ```tsx
15
+ // routes/dashboard/overview.tsx
16
+ function OverviewPage() {
17
+ return (
18
+ <div className="dashboard">
19
+ <Sidebar /> {/* 중복 */}
20
+ <main><Overview /></main>
21
+ </div>
22
+ )
23
+ }
24
+
25
+ // routes/dashboard/analytics.tsx
26
+ function AnalyticsPage() {
27
+ return (
28
+ <div className="dashboard">
29
+ <Sidebar /> {/* 중복 */}
30
+ <main><Analytics /></main>
31
+ </div>
32
+ )
33
+ }
34
+ ```
35
+
36
+ **✅ 올바른 예시 (Outlet으로 레이아웃 공유):**
37
+
38
+ ```tsx
39
+ // src/routes/__root.tsx
40
+ import { createRootRoute, Outlet } from '@tanstack/react-router'
41
+
42
+ export const Route = createRootRoute({
43
+ component: () => (
44
+ <div>
45
+ <Header />
46
+ <Outlet /> {/* 하위 라우트가 여기에 렌더링 */}
47
+ <Footer />
48
+ </div>
49
+ ),
50
+ })
51
+
52
+ // src/routes/dashboard.tsx - 레이아웃 라우트
53
+ export const Route = createFileRoute('/dashboard')({
54
+ component: DashboardLayout,
55
+ })
56
+
57
+ function DashboardLayout() {
58
+ return (
59
+ <div className="dashboard">
60
+ <Sidebar />
61
+ <main>
62
+ <Outlet /> {/* dashboard/overview, dashboard/analytics 렌더링 */}
63
+ </main>
64
+ </div>
65
+ )
66
+ }
67
+
68
+ // src/routes/dashboard/overview.tsx
69
+ export const Route = createFileRoute('/dashboard/overview')({
70
+ component: () => <Overview />, // DashboardLayout 안에 렌더링
71
+ })
72
+
73
+ // src/routes/dashboard/analytics.tsx
74
+ export const Route = createFileRoute('/dashboard/analytics')({
75
+ component: () => <Analytics />,
76
+ })
77
+ ```
78
+
79
+ **Pathless Layout (URL 없는 레이아웃):**
80
+
81
+ ```
82
+ src/routes/
83
+ ├── _public.tsx ← /없음 (pathless, navbar+footer만)
84
+ ├── _public/
85
+ │ ├── index.tsx → /
86
+ │ ├── about.tsx → /about
87
+ │ └── pricing.tsx → /pricing
88
+ ├── _dashboard.tsx ← /없음 (pathless, sidebar 레이아웃)
89
+ └── _dashboard/
90
+ ├── overview.tsx → /overview
91
+ ├── analytics.tsx → /analytics
92
+ └── settings.tsx → /settings
93
+ ```
94
+
95
+ ```tsx
96
+ // src/routes/_dashboard.tsx
97
+ export const Route = createFileRoute('/_dashboard')({
98
+ component: () => (
99
+ <div className="flex">
100
+ <Sidebar />
101
+ <main className="flex-1">
102
+ <Outlet />
103
+ </main>
104
+ </div>
105
+ ),
106
+ })
107
+
108
+ // src/routes/_dashboard/overview.tsx
109
+ export const Route = createFileRoute('/_dashboard/overview')({
110
+ component: () => <h1>Overview</h1>, // URL: /overview
111
+ })
112
+ ```
113
+
114
+ **Route Group vs Pathless Layout:**
115
+
116
+ | 패턴 | 문법 | URL 영향 | 레이아웃 |
117
+ |------|------|---------|---------|
118
+ | **Route Group** | `(groupName)/` | 없음 | ❌ 불가 |
119
+ | **Pathless Layout** | `_layoutName.tsx` | 없음 | ✅ 가능 |
120
+
121
+ - Route Group `(public)/`: 파일 정리용, 레이아웃 없음
122
+ - Pathless Layout `_public.tsx`: 공유 UI 래핑 가능
123
+
124
+ **createRootRouteWithContext:**
125
+
126
+ ```tsx
127
+ // 타입 안전한 전역 context
128
+ interface RouterContext {
129
+ queryClient: QueryClient
130
+ auth: AuthState
131
+ }
132
+
133
+ export const Route = createRootRouteWithContext<RouterContext>()({
134
+ component: () => <Outlet />,
135
+ })
136
+
137
+ // 하위 라우트에서 context 접근
138
+ export const Route = createFileRoute('/dashboard')({
139
+ beforeLoad: ({ context }) => {
140
+ // context.queryClient, context.auth 타입 안전
141
+ },
142
+ })
143
+ ```
144
+
145
+ **component 생략 시 자동 Outlet:**
146
+
147
+ ```tsx
148
+ // component를 지정하지 않으면 자동으로 <Outlet /> 렌더링
149
+ export const Route = createFileRoute('/app')({
150
+ // component 생략 → Outlet 자동 렌더링
151
+ beforeLoad: async ({ context }) => { /* ... */ },
152
+ })
153
+ ```
154
+
155
+ 참고: [Outlets Guide](https://tanstack.com/router/latest/docs/framework/react/guide/outlets), [Route Trees](https://tanstack.com/router/latest/docs/framework/react/guide/route-trees)
@@ -0,0 +1,89 @@
1
+ ---
2
+ title: Use Type-Safe Path Params with useParams and getRouteApi
3
+ impact: HIGH
4
+ impactDescription: ensures correct param types across code-split components
5
+ tags: routing, path-params, useParams, getRouteApi, type-safety
6
+ ---
7
+
8
+ ## useParams와 getRouteApi로 타입 안전한 Path Params
9
+
10
+ TanStack Router의 `useParams`와 `getRouteApi`로 동적 라우트 파라미터를 타입 안전하게 접근합니다.
11
+
12
+ **❌ 잘못된 예시 (타입 안전성 없음):**
13
+
14
+ ```tsx
15
+ // strict: false는 타입 추론 약화
16
+ function PostDetail() {
17
+ const params = useParams({ strict: false })
18
+ const postId = params.postId // any 타입
19
+ }
20
+ ```
21
+
22
+ **✅ 올바른 예시 (Route 바인딩):**
23
+
24
+ ```tsx
25
+ import { createFileRoute } from '@tanstack/react-router'
26
+
27
+ export const Route = createFileRoute('/posts/$postId')({
28
+ loader: async ({ params }) => {
29
+ // params.postId는 string 타입으로 자동 추론
30
+ return { post: await getPost(params.postId) }
31
+ },
32
+ component: PostDetail,
33
+ })
34
+
35
+ function PostDetail() {
36
+ // Route에 바인딩된 useParams - 완전한 타입 추론
37
+ const { postId } = Route.useParams()
38
+ const { post } = Route.useLoaderData()
39
+
40
+ return <div>{post.title}</div>
41
+ }
42
+ ```
43
+
44
+ **✅ 올바른 예시 (getRouteApi - 코드 스플릿 컴포넌트):**
45
+
46
+ ```tsx
47
+ // components/PostDetail.tsx (별도 파일)
48
+ import { getRouteApi } from '@tanstack/react-router'
49
+
50
+ // Route import 없이 타입 안전하게 접근
51
+ const routeApi = getRouteApi('/posts/$postId')
52
+
53
+ export function PostDetail() {
54
+ const { postId } = routeApi.useParams()
55
+ const { post } = routeApi.useLoaderData()
56
+ const search = routeApi.useSearch()
57
+ const context = routeApi.useRouteContext()
58
+
59
+ return <div>{post.title}</div>
60
+ }
61
+ ```
62
+
63
+ **select로 리렌더 최적화:**
64
+
65
+ ```tsx
66
+ // postId만 변경 시 리렌더
67
+ const postId = useParams({
68
+ from: '/posts/$postId',
69
+ select: (params) => params.postId,
70
+ })
71
+ ```
72
+
73
+ **from으로 타입 컨텍스트 지정:**
74
+
75
+ ```tsx
76
+ // 다른 라우트의 params 접근
77
+ const { postId } = useParams({ from: '/posts/$postId' })
78
+
79
+ // 현재 라우트 컨텍스트에서 접근 (Route 바인딩)
80
+ const { postId } = Route.useParams()
81
+ ```
82
+
83
+ **타입 안전성 순위:**
84
+ 1. `Route.useParams()` - 라우트에 직접 바인딩, 최고 타입 안전성
85
+ 2. `getRouteApi('/path').useParams()` - 코드 스플릿 시 사용, 순환 의존성 방지
86
+ 3. `useParams({ from: '/path' })` - from으로 타입 컨텍스트 지정
87
+ 4. `useParams({ strict: false })` - 전역 접근, 타입 약화 (비권장)
88
+
89
+ 참고: [Path Params Guide](https://tanstack.com/router/latest/docs/framework/react/guide/path-params), [getRouteApi](https://tanstack.com/router/latest/docs/framework/react/api/router/getRouteApiFunction)
@@ -0,0 +1,110 @@
1
+ ---
2
+ title: Use pendingComponent for Route Loading States
3
+ impact: MEDIUM
4
+ impactDescription: eliminates layout shift during navigation
5
+ tags: routing, pendingComponent, loading, UX, transitions
6
+ ---
7
+
8
+ ## pendingComponent로 라우트 로딩 상태 처리
9
+
10
+ `pendingComponent`는 loader가 완료되기 전 보여주는 로딩 UI입니다. `pendingMs`/`pendingMinMs`로 플리커 없는 로딩 경험을 구현합니다.
11
+
12
+ **❌ 잘못된 예시 (로딩 상태 없음):**
13
+
14
+ ```tsx
15
+ export const Route = createFileRoute('/tasks')({
16
+ loader: async () => {
17
+ const tasks = await fetchTasks() // 2초 소요
18
+ return { tasks }
19
+ },
20
+ component: TasksPage,
21
+ // 로딩 UI 없음 → 사용자가 2초간 빈 화면 봄
22
+ })
23
+ ```
24
+
25
+ **✅ 올바른 예시 (pendingComponent + 타이밍 설정):**
26
+
27
+ ```tsx
28
+ export const Route = createFileRoute('/tasks')({
29
+ loader: async () => {
30
+ return { tasks: await fetchTasks() }
31
+ },
32
+ pendingComponent: () => (
33
+ <div className="p-4">
34
+ <div className="animate-pulse space-y-3">
35
+ <div className="h-8 bg-gray-200 rounded w-1/3" />
36
+ <div className="h-4 bg-gray-200 rounded" />
37
+ <div className="h-4 bg-gray-200 rounded" />
38
+ </div>
39
+ </div>
40
+ ),
41
+ pendingMs: 150, // 150ms 후에도 loader 미완료 시 로딩 UI 표시
42
+ pendingMinMs: 200, // 로딩 UI 최소 200ms 표시 (플리커 방지)
43
+ component: TasksPage,
44
+ })
45
+ ```
46
+
47
+ **타이밍 동작:**
48
+
49
+ ```
50
+ Time 0ms: 네비게이션 시작
51
+ Time 150ms: pendingMs 경과 → loader 미완료면 pendingComponent 표시
52
+ loader 완료면 즉시 컴포넌트 렌더링 (로딩 UI 없음)
53
+
54
+ pendingComponent 표시 후:
55
+ loader 완료 시 → 최소 200ms(pendingMinMs) 후 컴포넌트 전환
56
+ (짧은 플리커 방지)
57
+ ```
58
+
59
+ **Router 레벨 기본값:**
60
+
61
+ ```tsx
62
+ const router = createRouter({
63
+ routeTree,
64
+ defaultPendingComponent: () => <GlobalSpinner />,
65
+ defaultPendingMs: 150,
66
+ defaultPendingMinMs: 200,
67
+ })
68
+ ```
69
+
70
+ **pendingComponent vs Suspense:**
71
+
72
+ | 항목 | pendingComponent | Suspense |
73
+ |------|-----------------|----------|
74
+ | 대상 | 라우트 네비게이션 | 컴포넌트 내 비동기 |
75
+ | 트리거 | loader 미완료 | Promise throw |
76
+ | 타이밍 제어 | pendingMs/pendingMinMs | 없음 |
77
+ | 사용 시점 | 페이지 전환 | deferred data, lazy 컴포넌트 |
78
+
79
+ **조합 패턴 (pendingComponent + Suspense):**
80
+
81
+ ```tsx
82
+ export const Route = createFileRoute('/posts/$postId')({
83
+ loader: async ({ params }) => {
84
+ const post = await getPost(params.postId) // 빠른 데이터
85
+ const deferredComments = getComments(params.postId) // 느린 데이터
86
+ return { post, deferredComments }
87
+ },
88
+ // 페이지 전환 시 로딩 UI (post 로딩 중)
89
+ pendingComponent: () => <PostSkeleton />,
90
+ pendingMs: 150,
91
+ component: PostPage,
92
+ })
93
+
94
+ function PostPage() {
95
+ const { post, deferredComments } = Route.useLoaderData()
96
+ return (
97
+ <div>
98
+ <PostContent post={post} />
99
+ {/* 댓글은 별도 Suspense (이미 post는 로드됨) */}
100
+ <Suspense fallback={<CommentsSkeleton />}>
101
+ <Await promise={deferredComments}>
102
+ {(comments) => <Comments comments={comments} />}
103
+ </Await>
104
+ </Suspense>
105
+ </div>
106
+ )
107
+ }
108
+ ```
109
+
110
+ 참고: [Pending Component](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#pending-component)
@@ -0,0 +1,91 @@
1
+ ---
2
+ title: Preload Routes on User Intent for Instant Navigation
3
+ impact: MEDIUM-HIGH
4
+ impactDescription: eliminates perceived loading time on navigation
5
+ tags: routing, preload, performance, link, UX
6
+ ---
7
+
8
+ ## 사용자 의도 기반 라우트 프리로딩
9
+
10
+ `preload="intent"`로 hover/touch 시 라우트 데이터를 미리 로드하여 즉각적인 네비게이션을 구현합니다.
11
+
12
+ **❌ 잘못된 예시 (프리로딩 없음):**
13
+
14
+ ```tsx
15
+ // 클릭 후 loader 실행 → 사용자 대기
16
+ <Link to="/posts/$postId" params={{ postId: '123' }}>
17
+ View Post
18
+ </Link>
19
+ ```
20
+
21
+ **✅ 올바른 예시 (intent 프리로딩):**
22
+
23
+ ```tsx
24
+ // hover 시 미리 loader 실행 → 클릭 시 즉시 렌더링
25
+ <Link
26
+ to="/posts/$postId"
27
+ params={{ postId: '123' }}
28
+ preload="intent"
29
+ >
30
+ View Post
31
+ </Link>
32
+ ```
33
+
34
+ **Router 레벨 기본값 설정:**
35
+
36
+ ```tsx
37
+ const router = createRouter({
38
+ routeTree,
39
+ defaultPreload: 'intent', // 전역 기본값
40
+ defaultPreloadDelay: 50, // hover 후 50ms 대기
41
+ defaultPreloadStaleTime: 30_000, // 프리로드 데이터 30초간 fresh
42
+ })
43
+ ```
44
+
45
+ **프리로딩 모드:**
46
+
47
+ | 모드 | 트리거 | 사용 시점 |
48
+ |------|--------|---------|
49
+ | `'intent'` | hover/touch 50ms 후 | 일반적인 네비게이션 링크 (권장) |
50
+ | `'viewport'` | 뷰포트 진입 시 | 목록 페이지, 무한 스크롤 |
51
+ | `'render'` | 컴포넌트 마운트 시 | 확실히 이동할 링크 |
52
+ | `false` | 비활성 | 인증/외부 링크 |
53
+
54
+ **Link별 오버라이드:**
55
+
56
+ ```tsx
57
+ // 목록 아이템은 viewport 프리로딩
58
+ {posts.map(post => (
59
+ <Link
60
+ key={post.id}
61
+ to="/posts/$postId"
62
+ params={{ postId: post.id }}
63
+ preload="viewport"
64
+ >
65
+ {post.title}
66
+ </Link>
67
+ ))}
68
+
69
+ // 특정 링크는 프리로딩 비활성
70
+ <Link to="/settings" preload={false}>
71
+ Settings
72
+ </Link>
73
+ ```
74
+
75
+ **프로그래밍 방식 프리로딩:**
76
+
77
+ ```tsx
78
+ const router = useRouter()
79
+
80
+ // 검색 결과 hover 시 수동 프리로드
81
+ const handleMouseEnter = (postId: string) => {
82
+ router.preloadRoute({
83
+ to: '/posts/$postId',
84
+ params: { postId },
85
+ })
86
+ }
87
+ ```
88
+
89
+ **캐싱:** 프리로드된 데이터는 기본 30초(`defaultPreloadStaleTime`) 동안 fresh 상태로 유지됩니다.
90
+
91
+ 참고: [Preloading Guide](https://tanstack.com/router/latest/docs/framework/react/guide/preloading)
@@ -0,0 +1,120 @@
1
+ ---
2
+ title: Use Router Context for Dependency Injection
3
+ impact: MEDIUM-HIGH
4
+ impactDescription: enables type-safe shared state across all routes
5
+ tags: routing, context, createRootRouteWithContext, dependency-injection
6
+ ---
7
+
8
+ ## Router Context로 의존성 주입
9
+
10
+ `createRootRouteWithContext`로 QueryClient, 인증 상태 등을 타입 안전하게 모든 라우트에 전달합니다.
11
+
12
+ **❌ 잘못된 예시 (전역 변수/import):**
13
+
14
+ ```tsx
15
+ // 전역 import → 테스트 어려움, 타입 안전성 없음
16
+ import { queryClient } from '../lib/queryClient'
17
+ import { getAuth } from '../lib/auth'
18
+
19
+ export const Route = createFileRoute('/dashboard')({
20
+ loader: async () => {
21
+ const user = await getAuth() // 타입 추론 불가
22
+ return await queryClient.fetchQuery(...)
23
+ },
24
+ })
25
+ ```
26
+
27
+ **✅ 올바른 예시 (createRootRouteWithContext):**
28
+
29
+ ```tsx
30
+ // 1. Context 인터페이스 정의
31
+ interface RouterContext {
32
+ queryClient: QueryClient
33
+ auth: { user: User | null }
34
+ }
35
+
36
+ // 2. Root route에 context 타입 바인딩
37
+ // src/routes/__root.tsx
38
+ import { createRootRouteWithContext, Outlet } from '@tanstack/react-router'
39
+
40
+ export const Route = createRootRouteWithContext<RouterContext>()({
41
+ component: () => <Outlet />,
42
+ })
43
+
44
+ // 3. Router 생성 시 context 값 전달
45
+ // src/router.tsx
46
+ const router = createRouter({
47
+ routeTree,
48
+ context: {
49
+ queryClient: new QueryClient(),
50
+ auth: { user: null },
51
+ },
52
+ })
53
+
54
+ // 4. 하위 라우트에서 타입 안전하게 접근
55
+ export const Route = createFileRoute('/dashboard')({
56
+ beforeLoad: async ({ context }) => {
57
+ // context.queryClient: QueryClient ✅ 완전한 타입 추론
58
+ // context.auth: { user: User | null } ✅
59
+ if (!context.auth.user) {
60
+ throw redirect({ to: '/login' })
61
+ }
62
+ },
63
+ loader: async ({ context }) => {
64
+ return context.queryClient.ensureQueryData({
65
+ queryKey: ['dashboard'],
66
+ queryFn: fetchDashboard,
67
+ })
68
+ },
69
+ })
70
+ ```
71
+
72
+ **beforeLoad에서 context 확장:**
73
+
74
+ ```tsx
75
+ // 부모 라우트에서 context 확장 → 자식 라우트에서 접근
76
+ export const Route = createFileRoute('/admin')({
77
+ beforeLoad: async ({ context }) => {
78
+ const permissions = await fetchPermissions(context.auth.user!.id)
79
+ return { permissions } // context에 병합됨
80
+ },
81
+ })
82
+
83
+ // 자식 라우트
84
+ export const Route = createFileRoute('/admin/users')({
85
+ beforeLoad: async ({ context }) => {
86
+ // context.permissions 접근 가능 (부모에서 추가됨)
87
+ if (!context.permissions.includes('manage_users')) {
88
+ throw redirect({ to: '/admin' })
89
+ }
90
+ },
91
+ })
92
+ ```
93
+
94
+ **컴포넌트에서 context 접근:**
95
+
96
+ ```tsx
97
+ function Dashboard() {
98
+ // useRouteContext로 접근
99
+ const { queryClient, auth } = Route.useRouteContext()
100
+
101
+ // 또는 getRouteApi 사용 (코드 스플릿)
102
+ const routeApi = getRouteApi('/dashboard')
103
+ const context = routeApi.useRouteContext()
104
+ }
105
+ ```
106
+
107
+ **useRouterState (라우터 전역 상태):**
108
+
109
+ ```tsx
110
+ // 라우터 상태 반응형 구독
111
+ const status = useRouterState({ select: (s) => s.status })
112
+ // 'idle' | 'pending' → 전역 로딩 인디케이터에 사용
113
+
114
+ const location = useRouterState({ select: (s) => s.location })
115
+ // 현재 URL 정보
116
+
117
+ // ⚠️ useRouter().state는 반응형이 아님 → useRouterState 사용
118
+ ```
119
+
120
+ 참고: [Router Context](https://tanstack.com/router/latest/docs/framework/react/guide/router-context)
@@ -0,0 +1,114 @@
1
+ ---
2
+ title: Validate Search Params with Zod for Type Safety
3
+ impact: HIGH
4
+ impactDescription: prevents invalid URL state and runtime errors
5
+ tags: routing, search-params, useSearch, zod, validation, type-safety
6
+ ---
7
+
8
+ ## Zod로 Search Params 타입 안전하게 검증
9
+
10
+ TanStack Router의 `validateSearch`로 URL search params를 스키마 기반으로 검증하고, `useSearch`로 타입 안전하게 접근합니다.
11
+
12
+ **❌ 잘못된 예시 (타입 없는 search params):**
13
+
14
+ ```tsx
15
+ function PostsPage() {
16
+ const searchParams = new URLSearchParams(window.location.search)
17
+ const page = Number(searchParams.get('page')) || 1 // 타입 안전성 없음
18
+ const sort = searchParams.get('sort') ?? 'date' // 유효성 검증 없음
19
+ }
20
+ ```
21
+
22
+ **✅ 올바른 예시 (Zod 스키마 + validateSearch):**
23
+
24
+ ```tsx
25
+ import { createFileRoute } from '@tanstack/react-router'
26
+ import { z } from 'zod'
27
+ import { zodValidator, fallback } from '@tanstack/zod-adapter'
28
+
29
+ const postsSearchSchema = z.object({
30
+ page: fallback(z.number().int().positive(), 1),
31
+ sort: fallback(z.enum(['date', 'title', 'author']), 'date'),
32
+ q: fallback(z.string().optional(), undefined),
33
+ limit: fallback(z.number().int().min(1).max(100), 20),
34
+ })
35
+
36
+ export const Route = createFileRoute('/posts')({
37
+ validateSearch: zodValidator(postsSearchSchema),
38
+ component: PostsPage,
39
+ })
40
+
41
+ function PostsPage() {
42
+ // 모든 타입이 스키마에서 자동 추론
43
+ const { page, sort, q, limit } = Route.useSearch()
44
+
45
+ return <div>Page {page}, sorted by {sort}</div>
46
+ }
47
+ ```
48
+
49
+ **useSearch에서 select로 리렌더 최적화:**
50
+
51
+ ```tsx
52
+ // ❌ 전체 search 구독 - 아무 param 변경 시 리렌더
53
+ function Pagination() {
54
+ const { page, sort, q, limit } = Route.useSearch()
55
+ return <div>Page {page}</div> // page만 사용하는데 전체 리렌더
56
+ }
57
+
58
+ // ✅ select로 필요한 값만 구독
59
+ function Pagination() {
60
+ const page = useSearch({
61
+ from: '/posts',
62
+ select: (search) => search.page,
63
+ })
64
+ return <div>Page {page}</div> // page 변경 시에만 리렌더
65
+ }
66
+ ```
67
+
68
+ **네비게이션에서 search params 업데이트:**
69
+
70
+ ```tsx
71
+ function PostsFilter() {
72
+ const navigate = useNavigate({ from: '/posts' })
73
+ const { sort, q } = Route.useSearch()
74
+
75
+ const handleSortChange = (newSort: string) => {
76
+ navigate({
77
+ search: (prev) => ({ ...prev, sort: newSort, page: 1 }),
78
+ })
79
+ }
80
+
81
+ const clearFilters = () => {
82
+ navigate({
83
+ search: { page: 1, sort: 'date', limit: 20 }, // 전체 초기화
84
+ })
85
+ }
86
+
87
+ return (
88
+ <div>
89
+ <select value={sort} onChange={(e) => handleSortChange(e.target.value)}>
90
+ <option value="date">Date</option>
91
+ <option value="title">Title</option>
92
+ </select>
93
+ <button onClick={clearFilters}>Clear</button>
94
+ </div>
95
+ )
96
+ }
97
+ ```
98
+
99
+ **복잡한 스키마 패턴:**
100
+
101
+ ```tsx
102
+ const filterSchema = z.object({
103
+ search: z.string().trim().optional(),
104
+ page: z.coerce.number().int().min(1).default(1),
105
+ sort: z.enum(['name', 'date', 'popularity']).default('date'),
106
+ order: z.enum(['asc', 'desc']).default('asc'),
107
+ inStock: z.coerce.boolean().default(true),
108
+ categories: z.array(z.string()).default([]),
109
+ })
110
+ ```
111
+
112
+ **주의:** `@tanstack/zod-adapter`는 별도 패키지입니다. Valibot 사용 시 `@tanstack/valibot-adapter`.
113
+
114
+ 참고: [Search Params Guide](https://tanstack.com/router/latest/docs/framework/react/guide/search-params)
@@ -64,4 +64,4 @@ function PostPage() {
64
64
 
65
65
  페이지가 3초 대신 50ms에 렌더링됩니다. 댓글은 준비되면 스트리밍. 다음에 사용: 분석, 추천, 사용자 활동, 소셜 기능.
66
66
 
67
- 참고: [TanStack Router Deferred Data Loading](https://tanstack.com/router/v1/docs/framework/react/guide/deferred-data-loading)
67
+ 참고: [TanStack Router Deferred Data Loading](https://tanstack.com/router/latest/docs/framework/react/guide/deferred-data-loading)