@kood/claude-code 0.1.5 → 0.1.6

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kood/claude-code",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Claude Code documentation installer for projects",
5
5
  "type": "module",
6
6
  "bin": "./dist/index.js",
@@ -277,21 +277,19 @@ export const Route = createFileRoute('/users')({
277
277
  ### TanStack Query (서버 연동 시 필수)
278
278
  ```tsx
279
279
  // ✅ 데이터 조회: useQuery 필수
280
- const getPosts = useServerFn(getServerPosts)
281
280
  const { data } = useQuery({
282
- queryKey: ['users'],
283
- queryFn: () => getPosts(),
281
+ queryKey: ['posts'],
282
+ queryFn: () => getServerPosts(),
284
283
  })
285
284
 
286
285
  // ✅ 데이터 변경: useMutation 필수
287
- const createPostFn = useServerFn(createPost)
288
286
  const mutation = useMutation({
289
- mutationFn: createPostFn,
290
- onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
287
+ mutationFn: createPost,
288
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts'] }),
291
289
  })
292
290
 
293
- // ❌ 금지: Server Function 직접 호출
294
- // useEffect(() => { getPosts().then(setData) }, [])
291
+ // ❌ 금지: Server Function TanStack Query 없이 직접 호출
292
+ // useEffect(() => { getServerPosts().then(setData) }, [])
295
293
  ```
296
294
 
297
295
  ---
@@ -304,7 +304,6 @@ const UsersPage = (): JSX.Element => {
304
304
  import { useState, useMemo, useEffect, useCallback } from 'react'
305
305
  import { useParams, useNavigate } from '@tanstack/react-router'
306
306
  import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
307
- import { useServerFn } from '@tanstack/react-start'
308
307
  import { useAuthStore } from '@/stores/auth'
309
308
  import { getUsers, createUser, deleteUser } from '@/services/user'
310
309
  import type { User } from '@/types'
@@ -339,24 +338,20 @@ export const useUsers = (): UseUsersReturn => {
339
338
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
340
339
  // 3. React Query (useQuery → useMutation)
341
340
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
342
- const getUsersFn = useServerFn(getUsers)
343
- const createUserFn = useServerFn(createUser)
344
- const deleteUserFn = useServerFn(deleteUser)
345
-
346
341
  const { data: users, isLoading, error } = useQuery({
347
342
  queryKey: ['users'],
348
- queryFn: () => getUsersFn(),
343
+ queryFn: () => getUsers(),
349
344
  })
350
345
 
351
346
  const createMutation = useMutation({
352
- mutationFn: createUserFn,
347
+ mutationFn: createUser,
353
348
  onSuccess: () => {
354
349
  queryClient.invalidateQueries({ queryKey: ['users'] })
355
350
  },
356
351
  })
357
352
 
358
353
  const deleteMutation = useMutation({
359
- mutationFn: deleteUserFn,
354
+ mutationFn: deleteUser,
360
355
  onSuccess: () => {
361
356
  queryClient.invalidateQueries({ queryKey: ['users'] })
362
357
  },
@@ -226,7 +226,7 @@ export const clientEnv = parseClientEnv()
226
226
  ```typescript
227
227
  // app/server-functions/users.ts
228
228
  import { createServerFn } from '@tanstack/react-start'
229
- import { getServerEnv } from '~/config/env'
229
+ import { getServerEnv } from '@/config/env'
230
230
 
231
231
  export const getUsers = createServerFn({ method: 'GET' })
232
232
  .handler(async () => {
@@ -248,7 +248,7 @@ export const getUsers = createServerFn({ method: 'GET' })
248
248
 
249
249
  ```tsx
250
250
  // app/components/AppHeader.tsx
251
- import { clientEnv } from '~/config/env'
251
+ import { clientEnv } from '@/config/env'
252
252
 
253
253
  export const AppHeader = () => {
254
254
  return (
@@ -282,7 +282,7 @@ const appName = import.meta.env.VITE_APP_NAME // ✅ VITE_ 접두사만
282
282
 
283
283
  ```typescript
284
284
  // app/lib/auth.ts
285
- import { getServerEnv } from '~/config/env'
285
+ import { getServerEnv } from '@/config/env'
286
286
 
287
287
  export const getAuthConfig = () => {
288
288
  const env = getServerEnv()
@@ -103,7 +103,7 @@ await authClient.twoFactor.disable({
103
103
  ```tsx
104
104
  // pages/two-factor.tsx
105
105
  import { useState } from 'react'
106
- import { authClient } from '~/lib/auth-client'
106
+ import { authClient } from '@/lib/auth-client'
107
107
 
108
108
  export default function TwoFactorPage() {
109
109
  const [code, setCode] = useState('')
@@ -111,7 +111,7 @@ export const auth = betterAuth({
111
111
 
112
112
  // Server Function에서 사용
113
113
  import { createServerFn } from '@tanstack/react-start'
114
- import { auth } from '~/lib/auth'
114
+ import { auth } from '@/lib/auth'
115
115
 
116
116
  export const getSession = createServerFn({ method: 'GET' })
117
117
  .handler(async ({ request }) => {
@@ -113,7 +113,7 @@ if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
113
113
 
114
114
  // Server Function에서 사용
115
115
  import { createServerFn } from '@tanstack/react-start'
116
- import { prisma } from '~/lib/prisma'
116
+ import { prisma } from '@/lib/prisma'
117
117
 
118
118
  export const getUsers = createServerFn({ method: 'GET' })
119
119
  .handler(async () => {
@@ -52,15 +52,12 @@ const queryClient = new QueryClient({
52
52
 
53
53
  ```tsx
54
54
  import { useQuery } from '@tanstack/react-query'
55
- import { useServerFn } from '@tanstack/react-start'
56
- import { getServerPosts } from '~/lib/server-functions'
55
+ import { getServerPosts } from '@/lib/server-functions'
57
56
 
58
57
  function PostList() {
59
- const getPosts = useServerFn(getServerPosts)
60
-
61
58
  const { data, isLoading, error } = useQuery({
62
59
  queryKey: ['posts'],
63
- queryFn: () => getPosts(),
60
+ queryFn: () => getServerPosts(),
64
61
  })
65
62
 
66
63
  if (isLoading) return <div>Loading...</div>
@@ -159,7 +159,7 @@ export const auth = betterAuth({
159
159
 
160
160
  // Server Function에서 사용
161
161
  import { createServerFn } from '@tanstack/react-start'
162
- import { auth } from '~/lib/auth'
162
+ import { auth } from '@/lib/auth'
163
163
 
164
164
  export const getSession = createServerFn({ method: 'GET' })
165
165
  .handler(async ({ request }) => {
@@ -93,16 +93,13 @@ export const submitForm = createServerFn({ method: 'POST' })
93
93
  ### ✅ 올바른 패턴: useQuery 사용 (데이터 조회)
94
94
 
95
95
  ```tsx
96
- import { useServerFn } from '@tanstack/react-start'
97
96
  import { useQuery } from '@tanstack/react-query'
98
- import { getServerPosts } from '~/lib/server-functions'
97
+ import { getServerPosts } from '@/lib/server-functions'
99
98
 
100
99
  function PostList() {
101
- const getPosts = useServerFn(getServerPosts)
102
-
103
100
  const { data, isLoading, error } = useQuery({
104
101
  queryKey: ['posts'],
105
- queryFn: () => getPosts(),
102
+ queryFn: () => getServerPosts(),
106
103
  })
107
104
 
108
105
  if (isLoading) return <div>Loading...</div>
@@ -121,16 +118,14 @@ function PostList() {
121
118
  ### ✅ 올바른 패턴: useMutation 사용 (데이터 변경)
122
119
 
123
120
  ```tsx
124
- import { useServerFn } from '@tanstack/react-start'
125
121
  import { useMutation, useQueryClient } from '@tanstack/react-query'
126
- import { createPost, deletePost } from '~/lib/server-functions'
122
+ import { createPost, deletePost } from '@/lib/server-functions'
127
123
 
128
124
  function PostForm() {
129
125
  const queryClient = useQueryClient()
130
- const createPostFn = useServerFn(createPost)
131
126
 
132
127
  const mutation = useMutation({
133
- mutationFn: (data: { title: string; content: string }) => createPostFn({ data }),
128
+ mutationFn: (data: { title: string; content: string }) => createPost({ data }),
134
129
  onSuccess: () => {
135
130
  // 관련 쿼리 무효화로 데이터 동기화
136
131
  queryClient.invalidateQueries({ queryKey: ['posts'] })
@@ -181,6 +176,92 @@ function BadExample() {
181
176
  }
182
177
  ```
183
178
 
179
+ ## ⚠️ 함수 분리 시 유의사항
180
+
181
+ Server Function 내부 로직을 별도 함수로 분리할 때 반드시 아래 규칙을 따르세요.
182
+
183
+ ### 규칙
184
+
185
+ ```
186
+ 1. 분리한 함수는 createServerFn 내부에서만 호출
187
+ 2. 분리한 함수는 createServerFn으로 감싸지 않음
188
+ 3. 분리한 함수는 index.ts에서 export 금지 (프론트엔드 import 방지)
189
+ ```
190
+
191
+ ### ✅ 올바른 패턴
192
+
193
+ ```typescript
194
+ // services/user/mutations.ts
195
+
196
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
197
+ // 내부 헬퍼 함수 (export 금지!)
198
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
199
+ const validateUserData = async (email: string) => {
200
+ const existing = await prisma.user.findUnique({ where: { email } })
201
+ if (existing) throw new Error('Email already exists')
202
+ return true
203
+ }
204
+
205
+ const sendWelcomeEmail = async (userId: string, email: string) => {
206
+ await emailService.send({
207
+ to: email,
208
+ template: 'welcome',
209
+ data: { userId },
210
+ })
211
+ }
212
+
213
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
214
+ // Server Function (export 가능)
215
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
216
+ export const createUser = createServerFn({ method: 'POST' })
217
+ .inputValidator(createUserSchema)
218
+ .handler(async ({ data }) => {
219
+ // 내부 헬퍼 함수 호출
220
+ await validateUserData(data.email)
221
+
222
+ const user = await prisma.user.create({ data })
223
+
224
+ // 내부 헬퍼 함수 호출
225
+ await sendWelcomeEmail(user.id, user.email)
226
+
227
+ return user
228
+ })
229
+ ```
230
+
231
+ ```typescript
232
+ // services/user/index.ts
233
+
234
+ // ✅ Server Function만 export
235
+ export { createUser, updateUser, deleteUser } from './mutations'
236
+ export { getUsers, getUserById } from './queries'
237
+
238
+ // ❌ 내부 헬퍼 함수는 export 금지!
239
+ // export { validateUserData, sendWelcomeEmail } from './mutations'
240
+ ```
241
+
242
+ ### ❌ 잘못된 패턴
243
+
244
+ ```typescript
245
+ // ❌ 분리한 함수를 createServerFn으로 감싸지 마세요
246
+ export const validateUserData = createServerFn({ method: 'POST' })
247
+ .handler(async ({ data }) => {
248
+ // ...
249
+ })
250
+
251
+ // ❌ 내부 헬퍼 함수를 export 하지 마세요
252
+ export const sendWelcomeEmail = async (userId: string) => {
253
+ // 프론트엔드에서 import 가능해져 보안 위험!
254
+ }
255
+ ```
256
+
257
+ ### 이유
258
+
259
+ - **보안**: 내부 로직이 프론트엔드 번들에 포함되지 않음
260
+ - **명확한 API**: Server Function만 외부에 노출되어 API 경계가 명확함
261
+ - **트리 쉐이킹**: export하지 않은 함수는 번들에서 제거됨
262
+
263
+ ---
264
+
184
265
  ## 보안 패턴
185
266
 
186
267
  ### 서버 전용 데이터 보호
@@ -45,7 +45,7 @@ export default defineConfig({
45
45
  "jsx": "react-jsx",
46
46
  "baseUrl": ".",
47
47
  "paths": {
48
- "~/*": ["./src/*"]
48
+ "@/*": ["./src/*"]
49
49
  }
50
50
  },
51
51
  "include": ["src/**/*"]