@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,196 +1,94 @@
1
1
  # TanStack Query - Optimistic Updates
2
2
 
3
- > **상위 문서**: [TanStack Query](./index.md)
3
+ 서버 응답 UI 즉시 업데이트.
4
4
 
5
- 낙관적 업데이트는 서버 응답을 기다리지 않고 UI를 즉시 업데이트하는 패턴입니다.
6
-
7
- ## 기본 패턴
8
-
9
- ```tsx
10
- const addTodoMutation = useMutation({
11
- mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }),
12
- // mutation 완료 후 쿼리 무효화
13
- onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
14
- })
15
-
16
- const { isPending, submittedAt, variables, mutate, isError } = addTodoMutation
17
- ```
18
-
19
- ## 완전한 Optimistic Update 패턴
20
-
21
- ```tsx
22
- type Todo = { id: string; text: string }
23
- type TodosResponse = { items: Todo[]; ts: number }
24
-
25
- function TodoList() {
26
- const queryClient = useQueryClient()
27
- const [text, setText] = React.useState('')
28
-
29
- const addTodoMutation = useMutation({
30
- mutationFn: async (newTodo: string): Promise<Todo> => {
31
- const response = await fetch('/api/todos', {
32
- method: 'POST',
33
- headers: { 'Content-Type': 'application/json' },
34
- body: JSON.stringify({ text: newTodo }),
35
- })
36
- return response.json()
37
- },
38
- onMutate: async (newTodo) => {
39
- // 1. 진행 중인 refetch 취소 (optimistic update 덮어쓰기 방지)
40
- await queryClient.cancelQueries({ queryKey: ['todos'] })
41
-
42
- // 2. 롤백을 위한 이전 값 스냅샷
43
- const previousTodos = queryClient.getQueryData<TodosResponse>(['todos'])
44
-
45
- // 3. 캐시 optimistic 업데이트
46
- if (previousTodos) {
47
- queryClient.setQueryData<TodosResponse>(['todos'], {
48
- ...previousTodos,
49
- items: [
50
- ...previousTodos.items,
51
- { id: Math.random().toString(), text: newTodo },
52
- ],
53
- })
54
- }
55
-
56
- // 4. context로 이전 값 반환
57
- return { previousTodos }
58
- },
59
- onError: (err, variables, context) => {
60
- // 5. 에러 시 롤백
61
- if (context?.previousTodos) {
62
- queryClient.setQueryData(['todos'], context.previousTodos)
63
- }
64
- },
65
- onSettled: () => {
66
- // 6. 서버와 캐시 동기화를 위해 refetch
67
- queryClient.invalidateQueries({ queryKey: ['todos'] })
68
- },
69
- })
70
-
71
- return (
72
- <div>
73
- <form onSubmit={(e) => {
74
- e.preventDefault()
75
- addTodoMutation.mutate(text)
76
- setText('')
77
- }}>
78
- <input value={text} onChange={(e) => setText(e.target.value)} />
79
- <button type="submit" disabled={addTodoMutation.isPending}>
80
- {addTodoMutation.isPending ? 'Adding...' : 'Add Todo'}
81
- </button>
82
- </form>
83
- {addTodoMutation.isError && <div>Error: {addTodoMutation.error.message}</div>}
84
- </div>
85
- )
86
- }
87
- ```
88
-
89
- ## 단일 항목 Optimistic Update
5
+ ## 패턴
90
6
 
91
7
  ```tsx
92
8
  useMutation({
93
- mutationFn: updateTodo,
9
+ mutationFn: addTodo,
94
10
  onMutate: async (newTodo) => {
95
- // 진행 중인 refetch 취소
96
- await queryClient.cancelQueries({ queryKey: ['todos', newTodo.id] })
11
+ // 1. 진행 중인 refetch 취소
12
+ await queryClient.cancelQueries({ queryKey: ['todos'] })
97
13
 
98
- // 이전 값 스냅샷
99
- const previousTodo = queryClient.getQueryData(['todos', newTodo.id])
14
+ // 2. 이전 값 스냅샷
15
+ const previousTodos = queryClient.getQueryData(['todos'])
100
16
 
101
- // Optimistic 업데이트
102
- queryClient.setQueryData(['todos', newTodo.id], newTodo)
17
+ // 3. 낙관적 업데이트
18
+ queryClient.setQueryData(['todos'], (old) => [...old, newTodo])
103
19
 
104
- return { previousTodo, newTodo }
20
+ // 4. context로 이전 값 반환
21
+ return { previousTodos }
105
22
  },
106
23
  onError: (err, newTodo, context) => {
107
- // 에러 시 롤백
108
- queryClient.setQueryData(
109
- ['todos', context.newTodo.id],
110
- context.previousTodo,
111
- )
24
+ // 5. 에러 시 롤백
25
+ queryClient.setQueryData(['todos'], context.previousTodos)
112
26
  },
113
- onSettled: (newTodo) => {
114
- queryClient.invalidateQueries({ queryKey: ['todos', newTodo.id] })
27
+ onSettled: () => {
28
+ // 6. 서버와 동기화
29
+ queryClient.invalidateQueries({ queryKey: ['todos'] })
115
30
  },
116
31
  })
117
32
  ```
118
33
 
119
- ## 삭제 Optimistic Update
34
+ ## 삭제
120
35
 
121
36
  ```tsx
122
- const deleteMutation = useMutation({
123
- mutationFn: (todoId: string) => deleteTodo(todoId),
37
+ useMutation({
38
+ mutationFn: deleteTodo,
124
39
  onMutate: async (todoId) => {
125
40
  await queryClient.cancelQueries({ queryKey: ['todos'] })
126
-
127
- const previousTodos = queryClient.getQueryData<Todo[]>(['todos'])
128
-
129
- // 삭제된 항목 제외
130
- queryClient.setQueryData<Todo[]>(['todos'], (old) =>
131
- old?.filter((todo) => todo.id !== todoId)
41
+ const previousTodos = queryClient.getQueryData(['todos'])
42
+ queryClient.setQueryData(['todos'], (old) =>
43
+ old.filter((todo) => todo.id !== todoId)
132
44
  )
133
-
134
45
  return { previousTodos }
135
46
  },
136
47
  onError: (err, todoId, context) => {
137
- queryClient.setQueryData(['todos'], context?.previousTodos)
138
- },
139
- onSettled: () => {
140
- queryClient.invalidateQueries({ queryKey: ['todos'] })
48
+ queryClient.setQueryData(['todos'], context.previousTodos)
141
49
  },
50
+ onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
142
51
  })
143
52
  ```
144
53
 
145
- ## 토글 Optimistic Update
54
+ ## 토글
146
55
 
147
56
  ```tsx
148
- const toggleMutation = useMutation({
149
- mutationFn: (todoId: string) => toggleTodo(todoId),
57
+ useMutation({
58
+ mutationFn: toggleTodo,
150
59
  onMutate: async (todoId) => {
151
60
  await queryClient.cancelQueries({ queryKey: ['todos'] })
152
-
153
- const previousTodos = queryClient.getQueryData<Todo[]>(['todos'])
154
-
155
- // 토글 상태 변경
156
- queryClient.setQueryData<Todo[]>(['todos'], (old) =>
157
- old?.map((todo) =>
158
- todo.id === todoId
159
- ? { ...todo, completed: !todo.completed }
160
- : todo
61
+ const previousTodos = queryClient.getQueryData(['todos'])
62
+ queryClient.setQueryData(['todos'], (old) =>
63
+ old.map((todo) =>
64
+ todo.id === todoId ? { ...todo, completed: !todo.completed } : todo
161
65
  )
162
66
  )
163
-
164
67
  return { previousTodos }
165
68
  },
166
69
  onError: (err, todoId, context) => {
167
- queryClient.setQueryData(['todos'], context?.previousTodos)
168
- },
169
- onSettled: () => {
170
- queryClient.invalidateQueries({ queryKey: ['todos'] })
70
+ queryClient.setQueryData(['todos'], context.previousTodos)
171
71
  },
72
+ onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
172
73
  })
173
74
  ```
174
75
 
175
- ## Persist (영속성)
176
-
177
- ### Optimistic Update 즉시 저장
76
+ ## 단일 항목 업데이트
178
77
 
179
78
  ```tsx
180
- const persister = experimental_createQueryPersister({
181
- storage: AsyncStorage,
182
- maxAge: 1000 * 60 * 60 * 12, // 12시간
183
- })
184
-
185
- const queryClient = useQueryClient()
186
-
187
79
  useMutation({
188
80
  mutationFn: updateTodo,
189
81
  onMutate: async (newTodo) => {
190
- // Optimistic 업데이트
191
- queryClient.setQueryData(['todos'], (old) => [...old, newTodo])
192
- // 즉시 스토리지에 저장
193
- persister.persistQueryByKey(['todos'], queryClient)
82
+ await queryClient.cancelQueries({ queryKey: ['todos', newTodo.id] })
83
+ const previousTodo = queryClient.getQueryData(['todos', newTodo.id])
84
+ queryClient.setQueryData(['todos', newTodo.id], newTodo)
85
+ return { previousTodo, newTodo }
86
+ },
87
+ onError: (err, newTodo, context) => {
88
+ queryClient.setQueryData(['todos', context.newTodo.id], context.previousTodo)
89
+ },
90
+ onSettled: (newTodo) => {
91
+ queryClient.invalidateQueries({ queryKey: ['todos', newTodo.id] })
194
92
  },
195
93
  })
196
94
  ```
@@ -1,103 +1,48 @@
1
1
  # TanStack Query - useMutation
2
2
 
3
- > **상위 문서**: [TanStack Query](./index.md)
4
-
5
3
  ## 기본 사용법
6
4
 
7
5
  ```tsx
8
- import { useMutation, useQueryClient } from '@tanstack/react-query'
9
-
10
- function AddTodo() {
11
- const queryClient = useQueryClient()
12
-
13
- const mutation = useMutation({
14
- mutationFn: postTodo,
15
- onSuccess: () => {
16
- // 성공 시 todos 쿼리 무효화하여 재조회
17
- queryClient.invalidateQueries({ queryKey: ['todos'] })
18
- },
19
- })
6
+ const queryClient = useQueryClient()
20
7
 
21
- return (
22
- <button
23
- onClick={() => {
24
- mutation.mutate({
25
- id: Date.now(),
26
- title: 'Do Laundry',
27
- })
28
- }}
29
- >
30
- Add Todo
31
- </button>
32
- )
33
- }
34
- ```
35
-
36
- ## useMutation 반환 값
8
+ const mutation = useMutation({
9
+ mutationFn: postTodo,
10
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
11
+ })
37
12
 
38
- ```typescript
39
- const {
40
- data, // mutation 결과 데이터
41
- error, // 에러 객체
42
- isError, // 에러 상태
43
- isIdle, // 아직 실행 안 됨
44
- isPending, // 실행 중
45
- isPaused, // 일시 정지됨
46
- isSuccess, // 성공 상태
47
- failureCount, // 실패 횟수
48
- failureReason, // 실패 이유
49
- mutate, // mutation 실행 (비동기)
50
- mutateAsync, // mutation 실행 (Promise 반환)
51
- reset, // 상태 초기화
52
- status, // 'idle' | 'pending' | 'success' | 'error'
53
- submittedAt, // 제출 시간
54
- variables, // 전달된 변수
55
- } = useMutation({ mutationFn })
13
+ mutation.mutate({ title: 'New Todo' })
56
14
  ```
57
15
 
58
- ## useMutation 옵션
16
+ ## 반환값
59
17
 
60
- ```tsx
61
- const mutation = useMutation({
62
- mutationFn,
63
- gcTime, // 가비지 컬렉션 시간
64
- meta, // 메타데이터
65
- mutationKey, // mutation
66
- networkMode, // 네트워크 모드
67
- onError, // 에러 콜백
68
- onMutate, // mutation 시작 콜백
69
- onSettled, // 완료 콜백 (성공/실패 무관)
70
- onSuccess, // 성공 콜백
71
- retry, // 재시도 횟수
72
- retryDelay, // 재시도 딜레이
73
- scope, // 범위
74
- throwOnError, // 에러 throw 여부
75
- })
76
- ```
18
+ | 속성 | 설명 |
19
+ |------|------|
20
+ | data | mutation 결과 |
21
+ | error | 에러 객체 |
22
+ | isPending | 실행 중 |
23
+ | isSuccess/isError | 상태 |
24
+ | mutate | 실행 (비동기) |
25
+ | mutateAsync | 실행 (Promise) |
26
+ | reset | 상태 초기화 |
27
+ | variables | 전달된 변수 |
77
28
 
78
- ## 콜백 함수들
29
+ ## 콜백
79
30
 
80
31
  ```tsx
81
- const mutation = useMutation({
32
+ useMutation({
82
33
  mutationFn: updateTodo,
83
34
  onMutate: async (newTodo) => {
84
- // mutation 시작 전 실행
85
- console.log('Starting mutation with:', newTodo)
86
- return { previousData: 'something' } // context로 전달
35
+ // mutation 시작 전 (optimistic update용)
36
+ return { previousData } // context로 전달
87
37
  },
88
38
  onSuccess: (data, variables, context) => {
89
- // 성공 시 실행
90
- console.log('Success:', data)
91
- console.log('Variables:', variables)
92
- console.log('Context:', context)
39
+ // 성공 시
93
40
  },
94
41
  onError: (error, variables, context) => {
95
- // 에러 시 실행
96
- console.log('Error:', error)
97
- // context로 롤백 가능
42
+ // 에러 시 (context로 롤백)
98
43
  },
99
44
  onSettled: (data, error, variables, context) => {
100
- // 성공/실패 무관하게 완료 시 실행
45
+ // 완료 시 (성공/실패 무관)
101
46
  queryClient.invalidateQueries({ queryKey: ['todos'] })
102
47
  },
103
48
  })
@@ -106,65 +51,26 @@ const mutation = useMutation({
106
51
  ## mutate vs mutateAsync
107
52
 
108
53
  ```tsx
109
- // mutate - 콜백 기반
54
+ // 콜백 기반
110
55
  mutation.mutate(data, {
111
- onSuccess: (result) => {
112
- console.log('Success:', result)
113
- },
114
- onError: (error) => {
115
- console.log('Error:', error)
116
- },
56
+ onSuccess: (result) => console.log(result),
57
+ onError: (error) => console.log(error),
117
58
  })
118
59
 
119
- // mutateAsync - Promise 기반
60
+ // Promise 기반
120
61
  try {
121
62
  const result = await mutation.mutateAsync(data)
122
- console.log('Success:', result)
123
- } catch (error) {
124
- console.log('Error:', error)
125
- }
63
+ } catch (error) { ... }
126
64
  ```
127
65
 
128
- ## Error 상태 Reset
66
+ ## 캐시 업데이트
129
67
 
130
68
  ```tsx
131
- const CreateTodo = () => {
132
- const [title, setTitle] = useState('')
133
- const mutation = useMutation({ mutationFn: createTodo })
134
-
135
- const onCreateTodo = (e) => {
136
- e.preventDefault()
137
- mutation.mutate({ title })
138
- }
139
-
140
- return (
141
- <form onSubmit={onCreateTodo}>
142
- {mutation.error && (
143
- <h5 onClick={() => mutation.reset()}>{mutation.error}</h5>
144
- )}
145
- <input
146
- type="text"
147
- value={title}
148
- onChange={(e) => setTitle(e.target.value)}
149
- />
150
- <br />
151
- <button type="submit">Create Todo</button>
152
- </form>
153
- )
154
- }
155
- ```
156
-
157
- ## 캐시 직접 업데이트
158
-
159
- ```tsx
160
- const saveMutation = useMutation({
69
+ useMutation({
161
70
  mutationFn: patchTodo,
162
71
  onSuccess: (data) => {
163
- // 목록 쿼리 무효화
164
- queryClient.invalidateQueries({ queryKey: ['todos'] })
165
-
166
- // 개별 항목 캐시 직접 업데이트
167
- queryClient.setQueryData(['todo', { id: data.id }], data)
72
+ queryClient.invalidateQueries({ queryKey: ['todos'] }) // 목록 무효화
73
+ queryClient.setQueryData(['todo', { id: data.id }], data) // 개별 캐시 업데이트
168
74
  },
169
75
  })
170
76
  ```
@@ -1,173 +1,73 @@
1
1
  # TanStack Query - useQuery
2
2
 
3
- > **상위 문서**: [TanStack Query](./index.md)
4
-
5
3
  ## 기본 사용법
6
4
 
7
5
  ```tsx
8
- import { useQuery } from '@tanstack/react-query'
9
-
10
- function Todos() {
11
- const { data, isLoading, error } = useQuery({
12
- queryKey: ['todos'],
13
- queryFn: getTodos,
14
- })
15
-
16
- if (isLoading) return <div>Loading...</div>
17
- if (error) return <div>Error: {error.message}</div>
6
+ const { data, isLoading, error } = useQuery({
7
+ queryKey: ['todos'],
8
+ queryFn: getTodos,
9
+ })
18
10
 
19
- return (
20
- <ul>
21
- {data?.map((todo) => (
22
- <li key={todo.id}>{todo.title}</li>
23
- ))}
24
- </ul>
25
- )
26
- }
11
+ if (isLoading) return <div>Loading...</div>
12
+ if (error) return <div>Error: {error.message}</div>
27
13
  ```
28
14
 
29
- ## useQuery 반환 값
15
+ ## 반환값
30
16
 
31
- ```typescript
32
- const {
33
- data, // 쿼리 결과 데이터
34
- error, // 에러 객체
35
- isLoading, // 첫 로딩 중
36
- isFetching, // 백그라운드 페칭 중
37
- isError, // 에러 상태
38
- isSuccess, // 성공 상태
39
- isPending, // 데이터 없고 로딩
40
- isStale, // 데이터가 stale 상태
41
- refetch, // 수동 리페치 함수
42
- status, // 'pending' | 'error' | 'success'
43
- fetchStatus, // 'fetching' | 'paused' | 'idle'
44
- } = useQuery({ queryKey, queryFn })
45
- ```
17
+ | 속성 | 설명 |
18
+ |------|------|
19
+ | data | 쿼리 결과 |
20
+ | error | 에러 객체 |
21
+ | isLoading | 첫 로딩 중 |
22
+ | isFetching | 백그라운드 페칭 중 |
23
+ | isError/isSuccess | 상태 |
24
+ | refetch | 수동 리페치 |
25
+ | status | 'pending' \| 'error' \| 'success' |
46
26
 
47
- ## useQuery 옵션
27
+ ## 주요 옵션
48
28
 
49
29
  ```tsx
50
- const { data } = useQuery({
30
+ useQuery({
51
31
  queryKey: ['todos'],
52
32
  queryFn: fetchTodos,
53
-
54
- // 캐시 관련
55
- staleTime: 1000 * 60 * 5, // 5분 동안 fresh
56
- gcTime: 1000 * 60 * 30, // 30분 가비지 컬렉션
57
-
58
- // 리페치 관련
59
- refetchOnWindowFocus: true, // 윈도우 포커스 시 리페치
60
- refetchOnReconnect: true, // 네트워크 재연결 시 리페치
61
- refetchInterval: 1000 * 60, // 1분마다 리페치
62
-
63
- // 재시도 관련
64
- retry: 3, // 실패 시 재시도 횟수
65
- retryDelay: 1000, // 재시도 딜레이
66
-
67
- // 조건부 실행
68
- enabled: !!userId, // false면 쿼리 실행 안 함
69
-
70
- // 초기 데이터
71
- initialData: [], // 초기 데이터
72
-
73
- // 플레이스홀더
74
- placeholderData: [], // 로딩 중 임시 데이터
33
+ staleTime: 1000 * 60 * 5, // fresh 유지 시간
34
+ gcTime: 1000 * 60 * 30, // 가비지 컬렉션 시간
35
+ refetchOnWindowFocus: true, // 포커스 리페치
36
+ refetchInterval: 1000 * 60, // 자동 리페치 간격
37
+ retry: 3, // 재시도 횟수
38
+ enabled: !!userId, // 조건부 실행
39
+ initialData: [], // 초기 데이터
40
+ select: (data) => data.filter(t => t.done), // 데이터 변환
75
41
  })
76
42
  ```
77
43
 
78
- ## 파라미터가 있는 쿼리
44
+ ## 패턴
79
45
 
80
46
  ```tsx
81
- function Todo({ todoId }: { todoId: number }) {
82
- const { data } = useQuery({
83
- queryKey: ['todo', todoId],
84
- queryFn: () => fetchTodoById(todoId),
85
- enabled: !!todoId,
86
- })
87
-
88
- return <div>{data?.title}</div>
89
- }
90
- ```
91
-
92
- ## 의존적 쿼리
93
-
94
- ```tsx
95
- function UserPosts({ userId }: { userId: number }) {
96
- // 먼저 유저 정보를 가져옴
97
- const { data: user } = useQuery({
98
- queryKey: ['user', userId],
99
- queryFn: () => fetchUserById(userId),
100
- })
101
-
102
- // 유저 정보가 있어야 포스트를 가져옴
103
- const { data: posts } = useQuery({
104
- queryKey: ['posts', user?.id],
105
- queryFn: () => fetchPostsByUserId(user!.id),
106
- enabled: !!user?.id,
107
- })
108
-
109
- return (
110
- <div>
111
- <h1>{user?.name}</h1>
112
- <ul>
113
- {posts?.map((post) => (
114
- <li key={post.id}>{post.title}</li>
115
- ))}
116
- </ul>
117
- </div>
118
- )
119
- }
120
- ```
121
-
122
- ## 병렬 쿼리
123
-
124
- ```tsx
125
- function Dashboard() {
126
- const usersQuery = useQuery({ queryKey: ['users'], queryFn: fetchUsers })
127
- const postsQuery = useQuery({ queryKey: ['posts'], queryFn: fetchPosts })
128
-
129
- if (usersQuery.isLoading || postsQuery.isLoading) {
130
- return <div>Loading...</div>
131
- }
132
-
133
- return (
134
- <div>
135
- <UserList users={usersQuery.data} />
136
- <PostList posts={postsQuery.data} />
137
- </div>
138
- )
139
- }
140
- ```
141
-
142
- ## useQueries (동적 병렬 쿼리)
143
-
144
- ```tsx
145
- function UsersList({ userIds }: { userIds: number[] }) {
146
- const userQueries = useQueries({
147
- queries: userIds.map((id) => ({
148
- queryKey: ['user', id],
149
- queryFn: () => fetchUserById(id),
150
- })),
151
- })
47
+ // 파라미터 쿼리
48
+ useQuery({
49
+ queryKey: ['todo', todoId],
50
+ queryFn: () => fetchTodoById(todoId),
51
+ enabled: !!todoId,
52
+ })
152
53
 
153
- return (
154
- <ul>
155
- {userQueries.map((query, index) => (
156
- <li key={userIds[index]}>
157
- {query.isLoading ? 'Loading...' : query.data?.name}
158
- </li>
159
- ))}
160
- </ul>
161
- )
162
- }
163
- ```
54
+ // 의존적 쿼리
55
+ const { data: user } = useQuery({ queryKey: ['user', userId], queryFn: ... })
56
+ const { data: posts } = useQuery({
57
+ queryKey: ['posts', user?.id],
58
+ queryFn: () => fetchPostsByUserId(user!.id),
59
+ enabled: !!user?.id, // user 로드 후 실행
60
+ })
164
61
 
165
- ## Select로 데이터 변환
62
+ // 병렬 쿼리
63
+ const usersQuery = useQuery({ queryKey: ['users'], queryFn: fetchUsers })
64
+ const postsQuery = useQuery({ queryKey: ['posts'], queryFn: fetchPosts })
166
65
 
167
- ```tsx
168
- const { data } = useQuery({
169
- queryKey: ['todos'],
170
- queryFn: fetchTodos,
171
- select: (todos) => todos.filter((todo) => todo.completed),
66
+ // 동적 병렬 쿼리
67
+ const userQueries = useQueries({
68
+ queries: userIds.map((id) => ({
69
+ queryKey: ['user', id],
70
+ queryFn: () => fetchUserById(id),
71
+ })),
172
72
  })
173
73
  ```