@kood/claude-code 0.1.6 → 0.1.9

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 (107) hide show
  1. package/dist/index.js +109 -216
  2. package/package.json +8 -2
  3. package/templates/hono/CLAUDE.md +59 -328
  4. package/templates/hono/docs/architecture/architecture.md +93 -747
  5. package/templates/hono/docs/deployment/cloudflare.md +59 -513
  6. package/templates/hono/docs/deployment/docker.md +41 -356
  7. package/templates/hono/docs/deployment/index.md +54 -190
  8. package/templates/hono/docs/deployment/railway.md +36 -306
  9. package/templates/hono/docs/deployment/vercel.md +49 -434
  10. package/templates/hono/docs/library/ai-sdk/index.md +53 -290
  11. package/templates/hono/docs/library/ai-sdk/openrouter.md +19 -387
  12. package/templates/hono/docs/library/ai-sdk/providers.md +28 -394
  13. package/templates/hono/docs/library/ai-sdk/streaming.md +52 -353
  14. package/templates/hono/docs/library/ai-sdk/structured-output.md +63 -395
  15. package/templates/hono/docs/library/ai-sdk/tools.md +62 -431
  16. package/templates/hono/docs/library/hono/env-setup.md +24 -313
  17. package/templates/hono/docs/library/hono/error-handling.md +34 -295
  18. package/templates/hono/docs/library/hono/index.md +29 -121
  19. package/templates/hono/docs/library/hono/middleware.md +21 -188
  20. package/templates/hono/docs/library/hono/rpc.md +40 -341
  21. package/templates/hono/docs/library/hono/validation.md +35 -195
  22. package/templates/hono/docs/library/pino/index.md +42 -333
  23. package/templates/hono/docs/library/prisma/cloudflare-d1.md +64 -367
  24. package/templates/hono/docs/library/prisma/config.md +19 -260
  25. package/templates/hono/docs/library/prisma/index.md +67 -320
  26. package/templates/hono/docs/library/zod/index.md +53 -257
  27. package/templates/npx/CLAUDE.md +62 -274
  28. package/templates/npx/docs/references/patterns.md +160 -0
  29. package/templates/tanstack-start/CLAUDE.md +100 -256
  30. package/templates/tanstack-start/docs/architecture/architecture.md +44 -589
  31. package/templates/tanstack-start/docs/deployment/cloudflare.md +37 -424
  32. package/templates/tanstack-start/docs/deployment/index.md +57 -286
  33. package/templates/tanstack-start/docs/deployment/nitro.md +36 -318
  34. package/templates/tanstack-start/docs/deployment/railway.md +40 -409
  35. package/templates/tanstack-start/docs/deployment/vercel.md +43 -465
  36. package/templates/tanstack-start/docs/design/components.md +77 -311
  37. package/templates/tanstack-start/docs/design/index.md +113 -69
  38. package/templates/tanstack-start/docs/design/safe-area.md +51 -250
  39. package/templates/tanstack-start/docs/design/tailwind-setup.md +45 -359
  40. package/templates/tanstack-start/docs/guides/conventions.md +103 -0
  41. package/templates/tanstack-start/docs/guides/env-setup.md +34 -340
  42. package/templates/tanstack-start/docs/guides/getting-started.md +22 -209
  43. package/templates/tanstack-start/docs/guides/hooks.md +166 -0
  44. package/templates/tanstack-start/docs/guides/routes.md +166 -0
  45. package/templates/tanstack-start/docs/guides/services.md +143 -0
  46. package/templates/tanstack-start/docs/library/better-auth/2fa.md +27 -115
  47. package/templates/tanstack-start/docs/library/better-auth/advanced.md +22 -105
  48. package/templates/tanstack-start/docs/library/better-auth/index.md +17 -66
  49. package/templates/tanstack-start/docs/library/better-auth/plugins.md +11 -88
  50. package/templates/tanstack-start/docs/library/better-auth/session.md +12 -92
  51. package/templates/tanstack-start/docs/library/better-auth/setup.md +9 -91
  52. package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +30 -358
  53. package/templates/tanstack-start/docs/library/prisma/config.md +27 -327
  54. package/templates/tanstack-start/docs/library/prisma/crud.md +46 -174
  55. package/templates/tanstack-start/docs/library/prisma/index.md +23 -113
  56. package/templates/tanstack-start/docs/library/prisma/relations.md +31 -153
  57. package/templates/tanstack-start/docs/library/prisma/schema.md +40 -217
  58. package/templates/tanstack-start/docs/library/prisma/setup.md +12 -112
  59. package/templates/tanstack-start/docs/library/prisma/transactions.md +20 -110
  60. package/templates/tanstack-start/docs/library/tanstack-query/index.md +26 -97
  61. package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +28 -107
  62. package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +44 -146
  63. package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +33 -127
  64. package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +49 -149
  65. package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +19 -112
  66. package/templates/tanstack-start/docs/library/tanstack-start/index.md +33 -80
  67. package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +28 -106
  68. package/templates/tanstack-start/docs/library/tanstack-start/routing.md +21 -118
  69. package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +34 -246
  70. package/templates/tanstack-start/docs/library/tanstack-start/setup.md +6 -39
  71. package/templates/tanstack-start/docs/library/zod/complex-types.md +32 -156
  72. package/templates/tanstack-start/docs/library/zod/index.md +31 -144
  73. package/templates/tanstack-start/docs/library/zod/transforms.md +20 -129
  74. package/templates/tanstack-start/docs/library/zod/validation.md +39 -155
  75. package/templates/hono/docs/commands/git.md +0 -145
  76. package/templates/hono/docs/mcp/context7.md +0 -106
  77. package/templates/hono/docs/mcp/index.md +0 -176
  78. package/templates/hono/docs/mcp/sequential-thinking.md +0 -101
  79. package/templates/hono/docs/mcp/serena.md +0 -269
  80. package/templates/hono/docs/mcp/sgrep.md +0 -105
  81. package/templates/hono/docs/skills/gemini-review/SKILL.md +0 -220
  82. package/templates/hono/docs/skills/gemini-review/references/checklists.md +0 -136
  83. package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +0 -303
  84. package/templates/npx/docs/commands/git.md +0 -145
  85. package/templates/npx/docs/mcp/index.md +0 -60
  86. package/templates/npx/docs/skills/gemini-review/SKILL.md +0 -220
  87. package/templates/npx/docs/skills/gemini-review/references/checklists.md +0 -134
  88. package/templates/npx/docs/skills/gemini-review/references/prompt-templates.md +0 -301
  89. package/templates/tanstack-start/docs/commands/git.md +0 -145
  90. package/templates/tanstack-start/docs/design/accessibility.md +0 -433
  91. package/templates/tanstack-start/docs/design/color.md +0 -235
  92. package/templates/tanstack-start/docs/design/spacing.md +0 -341
  93. package/templates/tanstack-start/docs/design/typography.md +0 -324
  94. package/templates/tanstack-start/docs/guides/best-practices.md +0 -950
  95. package/templates/tanstack-start/docs/guides/husky-lint-staged.md +0 -303
  96. package/templates/tanstack-start/docs/guides/prettier.md +0 -189
  97. package/templates/tanstack-start/docs/guides/project-templates.md +0 -710
  98. package/templates/tanstack-start/docs/library/tanstack-query/setup.md +0 -107
  99. package/templates/tanstack-start/docs/library/zod/basic-types.md +0 -186
  100. package/templates/tanstack-start/docs/mcp/context7.md +0 -204
  101. package/templates/tanstack-start/docs/mcp/index.md +0 -177
  102. package/templates/tanstack-start/docs/mcp/sequential-thinking.md +0 -180
  103. package/templates/tanstack-start/docs/mcp/serena.md +0 -269
  104. package/templates/tanstack-start/docs/mcp/sgrep.md +0 -174
  105. package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +0 -220
  106. package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +0 -144
  107. package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +0 -292
@@ -1,29 +1,21 @@
1
1
  # Getting Started
2
2
 
3
- TanStack Start 프로젝트 시작 가이드입니다.
3
+ TanStack Start 프로젝트 시작 가이드.
4
4
 
5
5
  ## Prerequisites
6
6
 
7
7
  - Node.js 18+
8
8
  - Yarn
9
- - Claude Code CLI
10
9
 
11
- ## Project Setup
12
-
13
- ### 1. Create TanStack Start Project
10
+ ## 프로젝트 생성
14
11
 
15
12
  ```bash
16
13
  npx create-tsrouter-app@latest my-app --template start
17
14
  cd my-app
18
- ```
19
-
20
- ### 2. Install Dependencies
21
-
22
- ```bash
23
15
  yarn install
24
16
  ```
25
17
 
26
- ### 3. Add Essential Packages
18
+ ## 필수 패키지 설치
27
19
 
28
20
  ```bash
29
21
  # Database (Prisma 7.x)
@@ -35,85 +27,9 @@ yarn add zod
35
27
 
36
28
  # TanStack Query
37
29
  yarn add @tanstack/react-query
38
-
39
- # UI (optional)
40
- yarn add tailwindcss postcss autoprefixer
41
- npx tailwindcss init -p
42
- ```
43
-
44
- ### 4. Initialize Prisma
45
-
46
- ```bash
47
- npx prisma init
48
- ```
49
-
50
- ## Project Structure
51
-
52
- ```
53
- my-app/
54
- ├── src/
55
- │ ├── routes/
56
- │ │ ├── __root.tsx
57
- │ │ ├── index.tsx
58
- │ │ └── users/
59
- │ │ ├── index.tsx
60
- │ │ ├── route.tsx # 필요시 route 설정
61
- │ │ ├── -components/ # 페이지 전용 컴포넌트
62
- │ │ │ └── user-card.tsx
63
- │ │ ├── -sections/ # 섹션 분리 (복잡한 경우)
64
- │ │ │ ├── user-list-section.tsx
65
- │ │ │ └── user-filter-section.tsx
66
- │ │ └── -hooks/ # 페이지 전용 훅
67
- │ │ ├── use-users.ts
68
- │ │ └── use-user-filter.ts
69
- │ ├── components/ # 공통 컴포넌트
70
- │ │ └── ui/
71
- │ │ ├── button.tsx
72
- │ │ └── input.tsx
73
- │ ├── database/ # 데이터베이스 관련
74
- │ │ └── prisma.ts # Prisma Client 인스턴스
75
- │ ├── services/ # 도메인별 SDK/서비스 레이어
76
- │ │ ├── user/
77
- │ │ │ ├── index.ts # 진입점 (re-export)
78
- │ │ │ ├── schemas.ts # Zod 스키마
79
- │ │ │ ├── queries.ts # GET 요청
80
- │ │ │ └── mutations.ts # POST 요청
81
- │ │ └── auth/
82
- │ │ ├── index.ts
83
- │ │ ├── schemas.ts
84
- │ │ ├── queries.ts
85
- │ │ └── mutations.ts
86
- │ ├── lib/ # 공통 유틸리티
87
- │ │ ├── query-client.ts
88
- │ │ └── utils.ts
89
- │ └── styles/
90
- │ └── app.css
91
- ├── generated/
92
- │ └── prisma/ # Prisma Client 출력
93
- ├── prisma/
94
- │ └── schema.prisma
95
- ├── app.config.ts
96
- ├── package.json
97
- └── tsconfig.json
98
- ```
99
-
100
- ## Route Folder Convention
101
-
102
- TanStack Start에서 `-` 접두사는 라우트에서 제외됩니다:
103
-
104
- ```
105
- routes/users/
106
- ├── index.tsx # /users 페이지
107
- ├── route.tsx # route 설정 (loader, beforeLoad 등)
108
- ├── -components/ # ❌ 라우트 아님, 컴포넌트 폴더
109
- │ └── user-card.tsx
110
- ├── -sections/ # ❌ 라우트 아님, 섹션 폴더
111
- │ └── user-list-section.tsx
112
- └── -hooks/ # ❌ 라우트 아님, 훅 폴더
113
- └── use-users.ts
114
30
  ```
115
31
 
116
- ## Core Configuration
32
+ ## 초기 설정
117
33
 
118
34
  ### app.config.ts
119
35
 
@@ -132,7 +48,6 @@ export default defineConfig({
132
48
  ```tsx
133
49
  // src/routes/__root.tsx
134
50
  import { createRootRoute, Outlet } from '@tanstack/react-router'
135
- import { TanStackRouterDevtools } from '@tanstack/router-devtools'
136
51
 
137
52
  export const Route = createRootRoute({
138
53
  component: RootComponent,
@@ -140,17 +55,14 @@ export const Route = createRootRoute({
140
55
 
141
56
  const RootComponent = (): JSX.Element => {
142
57
  return (
143
- <>
144
- <div className="min-h-screen">
145
- <nav className="border-b p-4">
146
- <a href="/" className="font-bold">My App</a>
147
- </nav>
148
- <main className="container mx-auto p-4">
149
- <Outlet />
150
- </main>
151
- </div>
152
- <TanStackRouterDevtools />
153
- </>
58
+ <div className="min-h-screen">
59
+ <nav className="border-b p-4">
60
+ <a href="/" className="font-bold">My App</a>
61
+ </nav>
62
+ <main className="container mx-auto p-4">
63
+ <Outlet />
64
+ </main>
65
+ </div>
154
66
  )
155
67
  }
156
68
  ```
@@ -174,29 +86,6 @@ const HomePage = (): JSX.Element => {
174
86
  }
175
87
  ```
176
88
 
177
- ## Services Setup
178
-
179
- ### Database Setup
180
-
181
- ```typescript
182
- // src/database/prisma.ts
183
- import { PrismaClient } from '../../generated/prisma'
184
-
185
- const globalForPrisma = globalThis as unknown as {
186
- prisma: PrismaClient | undefined
187
- }
188
-
189
- export const prisma =
190
- globalForPrisma.prisma ??
191
- new PrismaClient({
192
- log: process.env.NODE_ENV === 'development' ? ['query'] : [],
193
- })
194
-
195
- if (process.env.NODE_ENV !== 'production') {
196
- globalForPrisma.prisma = prisma
197
- }
198
- ```
199
-
200
89
  ### Query Client
201
90
 
202
91
  ```typescript
@@ -206,99 +95,23 @@ import { QueryClient } from '@tanstack/react-query'
206
95
  export const createQueryClient = (): QueryClient => {
207
96
  return new QueryClient({
208
97
  defaultOptions: {
209
- queries: {
210
- staleTime: 60 * 1000,
211
- retry: 1,
212
- },
98
+ queries: { staleTime: 60 * 1000, retry: 1 },
213
99
  },
214
100
  })
215
101
  }
216
102
  ```
217
103
 
218
- ### User Service (도메인 폴더 구조)
219
-
220
- ```typescript
221
- // src/services/user/schemas.ts
222
- import { z } from 'zod'
223
-
224
- export const createUserSchema = z.object({
225
- email: z.email(),
226
- name: z.string().min(1).max(100).trim(),
227
- })
228
-
229
- export const updateUserSchema = z.object({
230
- id: z.string(),
231
- email: z.email().optional(),
232
- name: z.string().min(1).max(100).trim().optional(),
233
- })
234
-
235
- export type CreateUserInput = z.infer<typeof createUserSchema>
236
- export type UpdateUserInput = z.infer<typeof updateUserSchema>
237
- ```
238
-
239
- ```typescript
240
- // src/services/user/queries.ts
241
- import { createServerFn } from '@tanstack/react-start'
242
- import { prisma } from '@/database/prisma'
243
-
244
- export const getUsers = createServerFn({ method: 'GET' })
245
- .handler(async () => {
246
- return prisma.user.findMany({
247
- orderBy: { createdAt: 'desc' },
248
- })
249
- })
250
-
251
- export const getUserById = createServerFn({ method: 'GET' })
252
- .handler(async ({ data: id }: { data: string }) => {
253
- const user = await prisma.user.findUnique({ where: { id } })
254
- if (!user) throw new Error('User not found')
255
- return user
256
- })
257
- ```
258
-
259
- ```typescript
260
- // src/services/user/mutations.ts
261
- import { createServerFn } from '@tanstack/react-start'
262
- import { prisma } from '@/database/prisma'
263
- import { createUserSchema, updateUserSchema } from './schemas'
264
-
265
- export const createUser = createServerFn({ method: 'POST' })
266
- .inputValidator(createUserSchema)
267
- .handler(async ({ data }) => {
268
- return prisma.user.create({ data })
269
- })
270
-
271
- export const updateUser = createServerFn({ method: 'POST' })
272
- .inputValidator(updateUserSchema)
273
- .handler(async ({ data }) => {
274
- const { id, ...updateData } = data
275
- return prisma.user.update({ where: { id }, data: updateData })
276
- })
277
-
278
- export const deleteUser = createServerFn({ method: 'POST' })
279
- .handler(async ({ data: id }: { data: string }) => {
280
- return prisma.user.delete({ where: { id } })
281
- })
282
- ```
283
-
284
- ```typescript
285
- // src/services/user/index.ts
286
- export * from './schemas'
287
- export * from './queries'
288
- export * from './mutations'
289
- ```
290
-
291
- ## Development Commands
104
+ ## 개발 명령어
292
105
 
293
106
  | Command | Description |
294
107
  |---------|-------------|
295
- | `yarn dev` | Start development server |
296
- | `yarn build` | Build for production |
297
- | `yarn start` | Start production server |
298
- | `yarn test` | Run tests |
299
- | `yarn lint` | Check code quality |
108
+ | `yarn dev` | 개발 서버 시작 |
109
+ | `yarn build` | 프로덕션 빌드 |
110
+ | `yarn start` | 프로덕션 서버 |
300
111
 
301
- ## Next Steps
112
+ ## 다음 단계
302
113
 
303
- - [Best Practices](./best-practices.md) - 개발 모범 사례
304
- - [Project Templates](./project-templates.md) - 프로젝트 템플릿
114
+ - [conventions.md](./conventions.md) - 코드 컨벤션
115
+ - [routes.md](./routes.md) - 라우트 구조
116
+ - [hooks.md](./hooks.md) - Custom Hook 패턴
117
+ - [services.md](./services.md) - Service Layer
@@ -0,0 +1,166 @@
1
+ # Custom Hook 패턴
2
+
3
+ 페이지/섹션의 모든 로직, 상태, 라이프사이클을 중앙화.
4
+
5
+ ## 필수: Hook 내부 순서
6
+
7
+ ```
8
+ 1. State (useState, zustand store)
9
+ 2. Global Hooks (useParams, useNavigate, useQueryClient)
10
+ 3. React Query (useQuery → useMutation)
11
+ 4. Event Handlers & Functions
12
+ 5. useMemo
13
+ 6. useEffect
14
+ ```
15
+
16
+ ## Page Hook 예시
17
+
18
+ ```typescript
19
+ // routes/users/-hooks/use-users.ts
20
+ import { useState, useMemo, useEffect, useCallback } from 'react'
21
+ import { useParams, useNavigate } from '@tanstack/react-router'
22
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
23
+ import { useAuthStore } from '@/stores/auth'
24
+ import { getUsers, createUser, deleteUser } from '@/services/user'
25
+ import type { User } from '@/types'
26
+
27
+ interface UseUsersReturn {
28
+ users: User[] | undefined
29
+ filteredUsers: User[]
30
+ isLoading: boolean
31
+ error: Error | null
32
+ search: string
33
+ setSearch: (value: string) => void
34
+ handleCreate: (data: { email: string; name: string }) => void
35
+ handleDelete: (id: string) => void
36
+ isCreating: boolean
37
+ isDeleting: boolean
38
+ }
39
+
40
+ export const useUsers = (): UseUsersReturn => {
41
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
42
+ // 1. State
43
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
44
+ const [search, setSearch] = useState('')
45
+ const { user: currentUser } = useAuthStore()
46
+
47
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
48
+ // 2. Global Hooks
49
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
50
+ const params = useParams({ from: '/users/$id' })
51
+ const navigate = useNavigate()
52
+ const queryClient = useQueryClient()
53
+
54
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
55
+ // 3. React Query
56
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
57
+ const { data: users, isLoading, error } = useQuery({
58
+ queryKey: ['users'],
59
+ queryFn: () => getUsers(),
60
+ })
61
+
62
+ const createMutation = useMutation({
63
+ mutationFn: createUser,
64
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
65
+ })
66
+
67
+ const deleteMutation = useMutation({
68
+ mutationFn: deleteUser,
69
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
70
+ })
71
+
72
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
73
+ // 4. Event Handlers
74
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
75
+ const handleCreate = useCallback(
76
+ (data: { email: string; name: string }) => {
77
+ createMutation.mutate({ data })
78
+ },
79
+ [createMutation]
80
+ )
81
+
82
+ const handleDelete = useCallback(
83
+ (id: string) => {
84
+ deleteMutation.mutate({ data: id })
85
+ },
86
+ [deleteMutation]
87
+ )
88
+
89
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
90
+ // 5. useMemo
91
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
92
+ const filteredUsers = useMemo(() => {
93
+ if (!users) return []
94
+ if (!search) return users
95
+ return users.filter((user) =>
96
+ user.name.toLowerCase().includes(search.toLowerCase())
97
+ )
98
+ }, [users, search])
99
+
100
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
101
+ // 6. useEffect
102
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
103
+ useEffect(() => {
104
+ if (!currentUser) {
105
+ navigate({ to: '/login' })
106
+ }
107
+ }, [currentUser, navigate])
108
+
109
+ return {
110
+ users,
111
+ filteredUsers,
112
+ isLoading,
113
+ error,
114
+ search,
115
+ setSearch,
116
+ handleCreate,
117
+ handleDelete,
118
+ isCreating: createMutation.isPending,
119
+ isDeleting: deleteMutation.isPending,
120
+ }
121
+ }
122
+ ```
123
+
124
+ ## 잘못된 순서 (금지)
125
+
126
+ ```typescript
127
+ // ❌ 순서가 뒤섞인 잘못된 예시
128
+ export const useBadHook = () => {
129
+ const queryClient = useQueryClient() // ❌ Global Hook이 먼저
130
+
131
+ useEffect(() => { /* ... */ }, []) // ❌ useEffect가 중간에
132
+
133
+ const [state, setState] = useState() // ❌ State가 나중에
134
+
135
+ const { data } = useQuery({ /* ... */ }) // ❌ Query가 Effect 다음에
136
+
137
+ const computed = useMemo(() => {}, []) // ❌ useMemo 위치 잘못됨
138
+ }
139
+ ```
140
+
141
+ ## Filter Hook 예시
142
+
143
+ ```typescript
144
+ // routes/users/-hooks/use-user-filter.ts
145
+ import { useState, useCallback } from 'react'
146
+
147
+ interface UseUserFilterReturn {
148
+ search: string
149
+ setSearch: (value: string) => void
150
+ role: string
151
+ setRole: (value: string) => void
152
+ clearFilters: () => void
153
+ }
154
+
155
+ export const useUserFilter = (): UseUserFilterReturn => {
156
+ const [search, setSearch] = useState('')
157
+ const [role, setRole] = useState('')
158
+
159
+ const clearFilters = useCallback(() => {
160
+ setSearch('')
161
+ setRole('')
162
+ }, [])
163
+
164
+ return { search, setSearch, role, setRole, clearFilters }
165
+ }
166
+ ```
@@ -0,0 +1,166 @@
1
+ # 라우트 구조
2
+
3
+ TanStack Start 파일 기반 라우팅 패턴.
4
+
5
+ ## Route 폴더 구조
6
+
7
+ ```
8
+ routes/<route-name>/
9
+ ├── index.tsx # 페이지 컴포넌트
10
+ ├── route.tsx # route 설정 (필요시)
11
+ ├── -components/ # 페이지 전용 컴포넌트
12
+ │ ├── user-card.tsx
13
+ │ └── user-form.tsx
14
+ ├── -sections/ # 섹션 분리 (복잡한 경우)
15
+ │ ├── user-list-section.tsx
16
+ │ └── user-filter-section.tsx
17
+ └── -hooks/ # 페이지 전용 훅
18
+ ├── use-users.ts
19
+ └── use-user-filter.ts
20
+ ```
21
+
22
+ ## `-` 접두사
23
+
24
+ `-` 접두사가 있는 폴더는 라우트에서 제외:
25
+
26
+ ```
27
+ routes/users/
28
+ ├── index.tsx # /users ✅ 라우트
29
+ ├── $id.tsx # /users/:id ✅ 라우트
30
+ ├── -components/ # ❌ 라우트 아님
31
+ ├── -sections/ # ❌ 라우트 아님
32
+ └── -hooks/ # ❌ 라우트 아님
33
+ ```
34
+
35
+ ## 기본 Route 패턴
36
+
37
+ ```tsx
38
+ // routes/users/index.tsx
39
+ import { createFileRoute } from '@tanstack/react-router'
40
+ import { UserListSection } from './-sections/user-list-section'
41
+ import { UserFilterSection } from './-sections/user-filter-section'
42
+
43
+ export const Route = createFileRoute('/users/')({
44
+ component: UsersPage,
45
+ })
46
+
47
+ const UsersPage = (): JSX.Element => {
48
+ return (
49
+ <div className="container mx-auto p-4">
50
+ <h1 className="text-2xl font-bold mb-4">Users</h1>
51
+ <UserFilterSection />
52
+ <UserListSection />
53
+ </div>
54
+ )
55
+ }
56
+ ```
57
+
58
+ ## Section 패턴
59
+
60
+ ```tsx
61
+ // routes/users/-sections/user-list-section.tsx
62
+ import { useUsers } from '../-hooks/use-users'
63
+ import { UserCard } from '../-components/user-card'
64
+
65
+ export const UserListSection = (): JSX.Element => {
66
+ const { users, isLoading, error, deleteUser, isDeleting } = useUsers()
67
+
68
+ if (isLoading) return <div className="text-center py-8">Loading...</div>
69
+ if (error) return <div className="text-red-600 py-8">Error: {error.message}</div>
70
+ if (!users?.length) return <div className="text-gray-500 py-8">No users found</div>
71
+
72
+ return (
73
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
74
+ {users.map((user) => (
75
+ <UserCard
76
+ key={user.id}
77
+ user={user}
78
+ onDelete={deleteUser}
79
+ isDeleting={isDeleting}
80
+ />
81
+ ))}
82
+ </div>
83
+ )
84
+ }
85
+ ```
86
+
87
+ ## Filter Section 패턴
88
+
89
+ ```tsx
90
+ // routes/users/-sections/user-filter-section.tsx
91
+ import { useUserFilter } from '../-hooks/use-user-filter'
92
+ import { Input } from '@/components/ui/input'
93
+ import { Button } from '@/components/ui/button'
94
+
95
+ export const UserFilterSection = (): JSX.Element => {
96
+ const { search, setSearch, role, setRole, clearFilters } = useUserFilter()
97
+
98
+ return (
99
+ <div className="flex gap-4 mb-6">
100
+ <Input
101
+ placeholder="Search users..."
102
+ value={search}
103
+ onChange={(e) => setSearch(e.target.value)}
104
+ className="max-w-xs"
105
+ />
106
+ <select
107
+ value={role}
108
+ onChange={(e) => setRole(e.target.value)}
109
+ className="border rounded px-3 py-2"
110
+ >
111
+ <option value="">All Roles</option>
112
+ <option value="USER">User</option>
113
+ <option value="ADMIN">Admin</option>
114
+ </select>
115
+ <Button variant="outline" onClick={clearFilters}>
116
+ Clear
117
+ </Button>
118
+ </div>
119
+ )
120
+ }
121
+ ```
122
+
123
+ ## 컴포넌트 패턴
124
+
125
+ ```tsx
126
+ // routes/users/-components/user-card.tsx
127
+ import type { User } from '@/types'
128
+ import { Button } from '@/components/ui/button'
129
+
130
+ interface UserCardProps {
131
+ user: User
132
+ onDelete?: (id: string) => void
133
+ isDeleting?: boolean
134
+ }
135
+
136
+ export const UserCard = ({
137
+ user,
138
+ onDelete,
139
+ isDeleting,
140
+ }: UserCardProps): JSX.Element => {
141
+ return (
142
+ <div className="rounded-lg border p-4 shadow-sm">
143
+ <div className="flex items-center gap-4">
144
+ <div className="h-12 w-12 rounded-full bg-gray-200" />
145
+ <div>
146
+ <h3 className="font-semibold">{user.name}</h3>
147
+ <p className="text-sm text-gray-600">{user.email}</p>
148
+ </div>
149
+ </div>
150
+
151
+ {onDelete && (
152
+ <div className="mt-4">
153
+ <Button
154
+ variant="outline"
155
+ size="sm"
156
+ onClick={() => onDelete(user.id)}
157
+ disabled={isDeleting}
158
+ >
159
+ {isDeleting ? 'Deleting...' : 'Delete'}
160
+ </Button>
161
+ </div>
162
+ )}
163
+ </div>
164
+ )
165
+ }
166
+ ```