@kood/claude-code 0.1.5 → 0.1.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 (77) hide show
  1. package/dist/index.js +21 -243
  2. package/package.json +1 -1
  3. package/templates/hono/CLAUDE.md +10 -6
  4. package/templates/hono/docs/deployment/index.md +5 -0
  5. package/templates/hono/docs/library/hono/index.md +6 -0
  6. package/templates/hono/docs/library/prisma/index.md +3 -0
  7. package/templates/npx/CLAUDE.md +8 -2
  8. package/templates/tanstack-start/CLAUDE.md +105 -259
  9. package/templates/tanstack-start/docs/deployment/cloudflare.md +37 -424
  10. package/templates/tanstack-start/docs/deployment/index.md +57 -286
  11. package/templates/tanstack-start/docs/deployment/nitro.md +36 -318
  12. package/templates/tanstack-start/docs/deployment/railway.md +40 -409
  13. package/templates/tanstack-start/docs/deployment/vercel.md +43 -465
  14. package/templates/tanstack-start/docs/design/accessibility.md +56 -326
  15. package/templates/tanstack-start/docs/design/color.md +37 -179
  16. package/templates/tanstack-start/docs/design/components.md +77 -311
  17. package/templates/tanstack-start/docs/design/index.md +24 -87
  18. package/templates/tanstack-start/docs/design/safe-area.md +51 -250
  19. package/templates/tanstack-start/docs/design/spacing.md +57 -276
  20. package/templates/tanstack-start/docs/design/tailwind-setup.md +45 -359
  21. package/templates/tanstack-start/docs/design/typography.md +40 -284
  22. package/templates/tanstack-start/docs/guides/best-practices.md +3 -8
  23. package/templates/tanstack-start/docs/guides/env-setup.md +3 -3
  24. package/templates/tanstack-start/docs/library/better-auth/2fa.md +27 -115
  25. package/templates/tanstack-start/docs/library/better-auth/advanced.md +22 -105
  26. package/templates/tanstack-start/docs/library/better-auth/index.md +17 -66
  27. package/templates/tanstack-start/docs/library/better-auth/plugins.md +11 -88
  28. package/templates/tanstack-start/docs/library/better-auth/session.md +12 -92
  29. package/templates/tanstack-start/docs/library/better-auth/setup.md +9 -91
  30. package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +30 -358
  31. package/templates/tanstack-start/docs/library/prisma/config.md +27 -327
  32. package/templates/tanstack-start/docs/library/prisma/crud.md +46 -174
  33. package/templates/tanstack-start/docs/library/prisma/index.md +23 -113
  34. package/templates/tanstack-start/docs/library/prisma/relations.md +31 -153
  35. package/templates/tanstack-start/docs/library/prisma/schema.md +40 -217
  36. package/templates/tanstack-start/docs/library/prisma/setup.md +13 -113
  37. package/templates/tanstack-start/docs/library/prisma/transactions.md +20 -110
  38. package/templates/tanstack-start/docs/library/tanstack-query/index.md +12 -99
  39. package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +28 -107
  40. package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +44 -146
  41. package/templates/tanstack-start/docs/library/tanstack-query/setup.md +11 -73
  42. package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +33 -127
  43. package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +49 -149
  44. package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +19 -112
  45. package/templates/tanstack-start/docs/library/tanstack-start/index.md +33 -80
  46. package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +28 -106
  47. package/templates/tanstack-start/docs/library/tanstack-start/routing.md +21 -118
  48. package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +41 -172
  49. package/templates/tanstack-start/docs/library/tanstack-start/setup.md +6 -39
  50. package/templates/tanstack-start/docs/library/zod/basic-types.md +33 -145
  51. package/templates/tanstack-start/docs/library/zod/complex-types.md +32 -156
  52. package/templates/tanstack-start/docs/library/zod/index.md +22 -150
  53. package/templates/tanstack-start/docs/library/zod/transforms.md +20 -129
  54. package/templates/tanstack-start/docs/library/zod/validation.md +39 -155
  55. package/templates/hono/docs/commands/git.md +0 -145
  56. package/templates/hono/docs/mcp/context7.md +0 -106
  57. package/templates/hono/docs/mcp/index.md +0 -176
  58. package/templates/hono/docs/mcp/sequential-thinking.md +0 -101
  59. package/templates/hono/docs/mcp/serena.md +0 -269
  60. package/templates/hono/docs/mcp/sgrep.md +0 -105
  61. package/templates/hono/docs/skills/gemini-review/SKILL.md +0 -220
  62. package/templates/hono/docs/skills/gemini-review/references/checklists.md +0 -136
  63. package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +0 -303
  64. package/templates/npx/docs/commands/git.md +0 -145
  65. package/templates/npx/docs/mcp/index.md +0 -60
  66. package/templates/npx/docs/skills/gemini-review/SKILL.md +0 -220
  67. package/templates/npx/docs/skills/gemini-review/references/checklists.md +0 -134
  68. package/templates/npx/docs/skills/gemini-review/references/prompt-templates.md +0 -301
  69. package/templates/tanstack-start/docs/commands/git.md +0 -145
  70. package/templates/tanstack-start/docs/mcp/context7.md +0 -204
  71. package/templates/tanstack-start/docs/mcp/index.md +0 -177
  72. package/templates/tanstack-start/docs/mcp/sequential-thinking.md +0 -180
  73. package/templates/tanstack-start/docs/mcp/serena.md +0 -269
  74. package/templates/tanstack-start/docs/mcp/sgrep.md +0 -174
  75. package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +0 -220
  76. package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +0 -144
  77. package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +0 -292
@@ -1,142 +1,64 @@
1
1
  # TanStack Start - Middleware
2
2
 
3
- > **상위 문서**: [TanStack Start](./index.md)
3
+ Server Function 라우트에 공통 로직 적용.
4
4
 
5
- 미들웨어는 Server Function 및 라우트 핸들러에 공통 로직을 적용합니다.
6
-
7
- ## Server Function Middleware
5
+ ## 기본 패턴
8
6
 
9
7
  ```typescript
10
- import { createMiddleware, createServerFn } from '@tanstack/react-start'
11
-
12
8
  // 미들웨어 정의
13
9
  const loggingMiddleware = createMiddleware({ type: 'function' })
14
- .client(() => {
15
- console.log('Client: Server function called')
16
- })
17
10
  .server(({ next }) => {
18
- console.log('Server: Processing request')
11
+ console.log('Processing request')
19
12
  return next()
20
13
  })
21
14
 
22
- // 미들웨어 적용
15
+ // 적용
23
16
  const fn = createServerFn()
24
17
  .middleware([loggingMiddleware])
25
- .handler(async () => {
26
- return { message: 'Hello' }
27
- })
18
+ .handler(async () => ({ message: 'Hello' }))
28
19
  ```
29
20
 
30
- ## Zod Validation Middleware
21
+ ## 인증 미들웨어
31
22
 
32
23
  ```typescript
33
- import { createMiddleware } from '@tanstack/react-start'
34
- import { zodValidator } from '@tanstack/react-start/validators'
35
- import { z } from 'zod'
36
-
37
- const mySchema = z.object({
38
- workspaceId: z.string(),
39
- })
40
-
41
- const workspaceMiddleware = createMiddleware({ type: 'function' })
42
- .inputValidator(zodValidator(mySchema))
43
- .server(({ next, data }) => {
44
- console.log('Workspace ID:', data.workspaceId)
45
- return next()
24
+ const authMiddleware = createMiddleware({ type: 'function' })
25
+ .server(async ({ next }) => {
26
+ const session = await getSession()
27
+ if (!session) throw redirect({ to: '/login' })
28
+ return next({ context: { user: session.user } })
46
29
  })
47
- ```
48
30
 
49
- ## Global Request Middleware
50
-
51
- ```typescript
52
- // src/start.ts
53
- import { createStart, createMiddleware } from '@tanstack/react-start'
54
-
55
- const myGlobalMiddleware = createMiddleware().server(({ next }) => {
56
- console.log('Global middleware running')
57
- return next()
58
- })
59
-
60
- export const startInstance = createStart(() => {
61
- return {
62
- requestMiddleware: [myGlobalMiddleware],
63
- }
64
- })
31
+ // 사용
32
+ export const protectedFn = createServerFn({ method: 'GET' })
33
+ .middleware([authMiddleware])
34
+ .handler(async ({ context }) => ({ user: context.user }))
65
35
  ```
66
36
 
67
- ## Global Server Function Middleware
37
+ ## Zod Validation Middleware
68
38
 
69
39
  ```typescript
70
- // src/start.ts
71
- import { createStart } from '@tanstack/react-start'
72
- import { loggingMiddleware } from './middleware'
73
-
74
- export const startInstance = createStart(() => {
75
- return {
76
- functionMiddleware: [loggingMiddleware],
77
- }
78
- })
40
+ const workspaceMiddleware = createMiddleware({ type: 'function' })
41
+ .inputValidator(zodValidator(z.object({ workspaceId: z.string() })))
42
+ .server(({ next, data }) => next())
79
43
  ```
80
44
 
81
- ## Route-Level Middleware
45
+ ## Global Middleware
82
46
 
83
47
  ```typescript
84
- export const Route = createFileRoute('/hello')({
85
- server: {
86
- middleware: [authMiddleware, loggerMiddleware], // 모든 핸들러에 적용
87
- handlers: {
88
- GET: async ({ request }) => {
89
- return new Response('Hello, World! from ' + request.url)
90
- },
91
- POST: async ({ request }) => {
92
- const body = await request.json()
93
- return new Response(`Hello, ${body.name}!`)
94
- },
95
- },
96
- },
97
- })
48
+ // src/start.ts
49
+ export const startInstance = createStart(() => ({
50
+ requestMiddleware: [globalMiddleware], // 모든 요청
51
+ functionMiddleware: [loggingMiddleware], // 모든 Server Function
52
+ }))
98
53
  ```
99
54
 
100
- ## Handler-Specific Middleware
55
+ ## Route-Level
101
56
 
102
57
  ```typescript
103
58
  export const Route = createFileRoute('/hello')({
104
59
  server: {
105
- handlers: ({ createHandlers }) =>
106
- createHandlers({
107
- GET: {
108
- middleware: [loggerMiddleware], // GET에만 적용
109
- handler: async ({ request }) => {
110
- return new Response('Hello, World! from ' + request.url)
111
- },
112
- },
113
- }),
60
+ middleware: [authMiddleware], // 모든 핸들러
61
+ handlers: { GET: async ({ request }) => new Response('Hello') },
114
62
  },
115
63
  })
116
64
  ```
117
-
118
- ## 인증 미들웨어 예시
119
-
120
- ```typescript
121
- import { createMiddleware } from '@tanstack/react-start'
122
- import { redirect } from '@tanstack/react-router'
123
-
124
- const authMiddleware = createMiddleware({ type: 'function' })
125
- .server(async ({ next }) => {
126
- const session = await getSession()
127
-
128
- if (!session) {
129
- throw redirect({ to: '/login' })
130
- }
131
-
132
- return next({ context: { user: session.user } })
133
- })
134
-
135
- // 사용
136
- export const protectedFn = createServerFn({ method: 'GET' })
137
- .middleware([authMiddleware])
138
- .handler(async ({ context }) => {
139
- // context.user 사용 가능
140
- return { user: context.user }
141
- })
142
- ```
@@ -1,163 +1,66 @@
1
1
  # TanStack Start - Routing
2
2
 
3
- > **상위 문서**: [TanStack Start](./index.md)
3
+ 파일 기반 라우팅.
4
4
 
5
- TanStack Start는 파일 기반 라우팅을 지원합니다.
6
-
7
- ## 기본 라우트
5
+ ## 기본 패턴
8
6
 
9
7
  ```tsx
10
8
  // routes/about.tsx
11
- import { createFileRoute } from '@tanstack/react-router'
12
-
13
9
  export const Route = createFileRoute('/about')({
14
10
  component: AboutPage,
15
11
  })
16
12
 
17
- function AboutPage() {
18
- return <h1>About</h1>
19
- }
20
- ```
21
-
22
- ## Loader를 사용한 데이터 로딩
23
-
24
- ```tsx
25
- // routes/index.tsx
26
- import { createFileRoute } from '@tanstack/react-router'
27
-
13
+ // Loader
28
14
  export const Route = createFileRoute('/')({
29
15
  component: Page,
30
- loader: async () => {
31
- const res = await fetch('https://api.example.com/posts')
32
- return res.json()
33
- },
16
+ loader: async () => fetch('/api/posts').then(r => r.json()),
34
17
  })
35
18
 
36
19
  function Page() {
37
20
  const posts = Route.useLoaderData()
38
- return (
39
- <ul>
40
- {posts.map((post) => (
41
- <li key={post.id}>{post.title}</li>
42
- ))}
43
- </ul>
44
- )
21
+ return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>
45
22
  }
46
- ```
47
-
48
- ## 동적 라우트
49
-
50
- ```tsx
51
- // routes/users/$id.tsx
52
- import { createFileRoute } from '@tanstack/react-router'
53
23
 
24
+ // 동적 라우트
54
25
  export const Route = createFileRoute('/users/$id')({
55
- loader: async ({ params }) => {
56
- const user = await getUserById(params.id)
57
- return { user }
58
- },
59
- component: UserDetailPage,
60
- })
61
-
62
- function UserDetailPage() {
63
- const { user } = Route.useLoaderData()
64
- return <h1>{user.name}</h1>
65
- }
66
- ```
67
-
68
- ## SSR 설정
69
-
70
- ```tsx
71
- // routes/posts/$postId.tsx
72
- export const Route = createFileRoute('/posts/$postId')({
73
- ssr: true, // SSR 활성화
74
- beforeLoad: () => {
75
- console.log('서버에서 초기 요청 시 실행')
76
- },
77
- loader: () => {
78
- console.log('서버에서 초기 요청 시 실행')
26
+ loader: async ({ params }) => ({ user: await getUserById(params.id) }),
27
+ component: () => {
28
+ const { user } = Route.useLoaderData()
29
+ return <h1>{user.name}</h1>
79
30
  },
80
- component: () => <div>서버에서 렌더링됨</div>,
81
31
  })
82
32
  ```
83
33
 
84
- ### SSR 옵션
34
+ ## SSR 옵션
85
35
 
86
36
  ```typescript
87
- // ssr: true - 전체 SSR (기본값)
88
- // ssr: false - 클라이언트 사이드만
89
- // ssr: 'data-only' - 데이터만 서버에서 로드, 렌더링은 클라이언트
37
+ ssr: true // 전체 SSR (기본값)
38
+ ssr: false // 클라이언트만
39
+ ssr: 'data-only' // 데이터만 서버, 렌더링은 클라이언트
90
40
  ```
91
41
 
92
42
  ## Server Routes (API)
93
43
 
94
- ### 기본 API 라우트
95
-
96
44
  ```typescript
97
- // routes/api/hello.ts
98
- import { createFileRoute } from '@tanstack/react-router'
99
-
100
45
  export const Route = createFileRoute('/api/hello')({
101
46
  server: {
102
47
  handlers: {
103
- GET: async ({ request }) => {
104
- return new Response('Hello, World!')
105
- },
48
+ GET: async () => new Response('Hello'),
106
49
  POST: async ({ request }) => {
107
50
  const body = await request.json()
108
- return new Response(`Hello, ${body.name}!`)
51
+ return json({ name: body.name })
109
52
  },
110
53
  },
111
54
  },
112
55
  })
113
56
  ```
114
57
 
115
- ### JSON 응답
116
-
117
- ```typescript
118
- import { createFileRoute } from '@tanstack/react-router'
119
- import { json } from '@tanstack/react-start'
120
-
121
- export const Route = createFileRoute('/api/users')({
122
- server: {
123
- handlers: {
124
- GET: async ({ request }) => {
125
- const users = await getUsers()
126
- return json({ users })
127
- },
128
- },
129
- },
130
- })
131
- ```
132
-
133
- ## 라우트 파일 구조
58
+ ## 구조
134
59
 
135
60
  ```
136
61
  routes/
137
- ├── __root.tsx → Root layout
138
- ├── index.tsx → /
139
- ├── about.tsx → /about
140
- ├── users/
141
- │ ├── index.tsx → /users
142
- │ └── $id.tsx → /users/:id
143
- ├── posts/
144
- │ ├── index.tsx → /posts
145
- │ ├── $postId.tsx → /posts/:postId
146
- │ └── new.tsx → /posts/new
147
- └── api/
148
- ├── hello.ts → /api/hello
149
- └── users.ts → /api/users
150
- ```
151
-
152
- ## Catch-All 라우트
153
-
154
- ```tsx
155
- // routes/$.tsx - 모든 매칭되지 않는 경로 처리
156
- export const Route = createFileRoute('/$')({
157
- component: NotFoundPage,
158
- })
159
-
160
- function NotFoundPage() {
161
- return <h1>404 - Page Not Found</h1>
162
- }
62
+ ├── __root.tsx → Root layout
63
+ ├── index.tsx → /
64
+ ├── users/$id.tsx → /users/:id
65
+ ├── $.tsx → Catch-all (404)
163
66
  ```
@@ -1,206 +1,75 @@
1
1
  # TanStack Start - Server Functions
2
2
 
3
- > **상위 문서**: [TanStack Start](./index.md)
4
-
5
- Server Functions는 서버에서만 실행되는 타입 안전한 함수입니다.
3
+ 서버에서만 실행되는 타입 안전한 함수.
6
4
 
7
5
  ## ⚠️ 필수: TanStack Query 사용
8
6
 
9
- **Server Function을 클라이언트에서 호출할 때는 반드시 TanStack Query를 사용해야 합니다.**
10
-
11
- ```
12
- ❌ 금지: Server Function 직접 호출
13
- ✅ 필수: useQuery/useMutation과 함께 사용
14
- ```
7
+ 클라이언트 호출 반드시 useQuery/useMutation 사용.
8
+ - 자동 캐싱, 중복 요청 제거, 로딩/에러 상태 관리, invalidateQueries 동기화
15
9
 
16
- **이유**:
17
- - 자동 캐싱 및 중복 요청 제거
18
- - 로딩/에러 상태 관리
19
- - 자동 재시도 및 백그라운드 갱신
20
- - invalidateQueries로 일관된 데이터 동기화
21
-
22
- ## 기본 Server Function
10
+ ## 기본 패턴
23
11
 
24
12
  ```typescript
25
- import { createServerFn } from '@tanstack/react-start'
26
-
27
- // GET 요청 (데이터 조회)
13
+ // GET
28
14
  export const getUsers = createServerFn({ method: 'GET' })
29
- .handler(async () => {
30
- return prisma.user.findMany()
31
- })
32
-
33
- // POST 요청 (데이터 생성/수정)
34
- export const createUser = createServerFn({ method: 'POST' })
35
- .handler(async () => {
36
- return { success: true }
37
- })
38
- ```
39
-
40
- ## Input Validation
41
-
42
- ### 기본 Validator
43
-
44
- ```typescript
45
- import { createServerFn } from '@tanstack/react-start'
46
-
47
- export const createUser = createServerFn({ method: 'POST' })
48
- .inputValidator((data: { email: string; name: string }) => data)
49
- .handler(async ({ data }) => {
50
- // data는 타입 안전함
51
- return prisma.user.create({ data })
52
- })
53
- ```
54
-
55
- ### Zod Validation 사용
56
-
57
- ```typescript
58
- import { createServerFn } from '@tanstack/react-start'
59
- import { zodValidator } from '@tanstack/react-start/validators'
60
- import { z } from 'zod'
15
+ .handler(async () => prisma.user.findMany())
61
16
 
17
+ // POST + Zod Validation
62
18
  const createUserSchema = z.object({
63
- email: z.string().email(),
19
+ email: z.email(),
64
20
  name: z.string().min(1).max(100),
65
- bio: z.string().max(500).optional(),
66
21
  })
67
22
 
68
23
  export const createUser = createServerFn({ method: 'POST' })
69
24
  .inputValidator(zodValidator(createUserSchema))
70
- .handler(async ({ data }) => {
71
- return prisma.user.create({ data })
72
- })
25
+ .handler(async ({ data }) => prisma.user.create({ data }))
73
26
  ```
74
27
 
75
- ## FormData 처리
76
-
77
- ```typescript
78
- import { createServerFn } from '@tanstack/react-start'
79
-
80
- export const submitForm = createServerFn({ method: 'POST' })
81
- .inputValidator((formData: FormData) => {
82
- const name = formData.get('name') as string
83
- const email = formData.get('email') as string
84
- return { name, email }
85
- })
86
- .handler(async ({ data }) => {
87
- return prisma.user.create({ data })
88
- })
89
- ```
90
-
91
- ## 컴포넌트에서 호출 (TanStack Query 필수)
92
-
93
- ### ✅ 올바른 패턴: useQuery 사용 (데이터 조회)
28
+ ## 컴포넌트에서 호출
94
29
 
95
30
  ```tsx
96
- import { useServerFn } from '@tanstack/react-start'
97
- import { useQuery } from '@tanstack/react-query'
98
- import { getServerPosts } from '~/lib/server-functions'
99
-
100
- function PostList() {
101
- const getPosts = useServerFn(getServerPosts)
31
+ // useQuery (조회)
32
+ const { data, isLoading } = useQuery({
33
+ queryKey: ['posts'],
34
+ queryFn: () => getServerPosts(),
35
+ })
102
36
 
103
- const { data, isLoading, error } = useQuery({
104
- queryKey: ['posts'],
105
- queryFn: () => getPosts(),
106
- })
37
+ // useMutation (변경)
38
+ const mutation = useMutation({
39
+ mutationFn: createPost,
40
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts'] }),
41
+ })
107
42
 
108
- if (isLoading) return <div>Loading...</div>
109
- if (error) return <div>Error: {error.message}</div>
110
-
111
- return (
112
- <ul>
113
- {data?.map((post) => (
114
- <li key={post.id}>{post.title}</li>
115
- ))}
116
- </ul>
117
- )
118
- }
43
+ // ❌ 직접 호출 금지 (캐싱 없음, 동기화 안됨)
119
44
  ```
120
45
 
121
- ### 올바른 패턴: useMutation 사용 (데이터 변경)
46
+ ## 함수 분리 규칙
122
47
 
123
- ```tsx
124
- import { useServerFn } from '@tanstack/react-start'
125
- import { useMutation, useQueryClient } from '@tanstack/react-query'
126
- import { createPost, deletePost } from '~/lib/server-functions'
127
-
128
- function PostForm() {
129
- const queryClient = useQueryClient()
130
- const createPostFn = useServerFn(createPost)
131
-
132
- const mutation = useMutation({
133
- mutationFn: (data: { title: string; content: string }) => createPostFn({ data }),
134
- onSuccess: () => {
135
- // 관련 쿼리 무효화로 데이터 동기화
136
- queryClient.invalidateQueries({ queryKey: ['posts'] })
137
- },
138
- })
139
-
140
- const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
141
- e.preventDefault()
142
- const formData = new FormData(e.currentTarget)
143
- mutation.mutate({
144
- title: formData.get('title') as string,
145
- content: formData.get('content') as string,
146
- })
147
- }
148
-
149
- return (
150
- <form onSubmit={handleSubmit}>
151
- <input name="title" required />
152
- <textarea name="content" required />
153
- <button type="submit" disabled={mutation.isPending}>
154
- {mutation.isPending ? '저장 중...' : '저장'}
155
- </button>
156
- </form>
157
- )
158
- }
159
- ```
48
+ ```typescript
49
+ // 내부 헬퍼 (export 금지!)
50
+ const validateUserData = async (email: string) => { ... }
160
51
 
161
- ### ❌ 금지: Server Function 직접 호출
52
+ // Server Function (export 가능)
53
+ export const createUser = createServerFn({ method: 'POST' })
54
+ .inputValidator(createUserSchema)
55
+ .handler(async ({ data }) => {
56
+ await validateUserData(data.email)
57
+ return prisma.user.create({ data })
58
+ })
162
59
 
163
- ```tsx
164
- // 이렇게 하지 마세요!
165
- function BadExample() {
166
- const [posts, setPosts] = useState([])
167
- const [loading, setLoading] = useState(false)
168
-
169
- useEffect(() => {
170
- setLoading(true)
171
- getPosts()
172
- .then(setPosts)
173
- .finally(() => setLoading(false))
174
- }, [])
175
-
176
- // 문제점:
177
- // - 중복 요청 발생 가능
178
- // - 캐싱 없음
179
- // - 에러 처리 수동
180
- // - 다른 컴포넌트와 데이터 동기화 안됨
181
- }
60
+ // index.ts: Server Function만 export
61
+ export { createUser } from './mutations'
62
+ // export { validateUserData } 금지
182
63
  ```
183
64
 
184
- ## 보안 패턴
185
-
186
- ### 서버 전용 데이터 보호
65
+ ## 보안
187
66
 
188
67
  ```tsx
189
- // ❌ 잘못된 방법 - 클라이언트에 노출됨
190
- export const Route = createFileRoute('/users')({
191
- loader: () => {
192
- const secret = process.env.SECRET // 클라이언트에 노출!
193
- return fetch(`/api/users?key=${secret}`)
194
- },
195
- })
196
-
197
- // ✅ 올바른 방법 - 서버 함수 사용
198
- const getUsersSecurely = createServerFn().handler(() => {
199
- const secret = process.env.SECRET // 서버에서만 접근
200
- return fetch(`/api/users?key=${secret}`)
201
- })
68
+ // ❌ loader에서 환경변수 직접 사용 (노출됨)
69
+ loader: () => { const secret = process.env.SECRET }
202
70
 
203
- export const Route = createFileRoute('/users')({
204
- loader: () => getUsersSecurely(),
71
+ // Server Function 사용
72
+ const fn = createServerFn().handler(() => {
73
+ const secret = process.env.SECRET // 서버에서만
205
74
  })
206
75
  ```
@@ -1,15 +1,13 @@
1
1
  # TanStack Start - 설치 및 설정
2
2
 
3
- > **상위 문서**: [TanStack Start](./index.md)
4
-
5
- ## 패키지 설치
3
+ ## 설치
6
4
 
7
5
  ```bash
8
6
  yarn add @tanstack/react-start @tanstack/react-router vinxi
9
7
  yarn add -D vite @vitejs/plugin-react vite-tsconfig-paths
10
8
  ```
11
9
 
12
- ## Vite 설정
10
+ ## 설정
13
11
 
14
12
  ```typescript
15
13
  // vite.config.ts
@@ -19,19 +17,11 @@ import { tanstackStart } from '@tanstack/react-start/plugin/vite'
19
17
  import viteReact from '@vitejs/plugin-react'
20
18
 
21
19
  export default defineConfig({
22
- server: {
23
- port: 3000,
24
- },
25
- plugins: [
26
- tsConfigPaths(),
27
- tanstackStart(),
28
- viteReact(),
29
- ],
20
+ server: { port: 3000 },
21
+ plugins: [tsConfigPaths(), tanstackStart(), viteReact()],
30
22
  })
31
23
  ```
32
24
 
33
- ## TypeScript 설정
34
-
35
25
  ```json
36
26
  // tsconfig.json
37
27
  {
@@ -40,35 +30,12 @@ export default defineConfig({
40
30
  "module": "ESNext",
41
31
  "moduleResolution": "bundler",
42
32
  "strict": true,
43
- "esModuleInterop": true,
44
- "skipLibCheck": true,
45
33
  "jsx": "react-jsx",
46
- "baseUrl": ".",
47
- "paths": {
48
- "~/*": ["./src/*"]
49
- }
50
- },
51
- "include": ["src/**/*"]
34
+ "paths": { "@/*": ["./src/*"] }
35
+ }
52
36
  }
53
37
  ```
54
38
 
55
- ## 프로젝트 구조
56
-
57
- ```
58
- project/
59
- ├── src/
60
- │ ├── routes/
61
- │ │ ├── __root.tsx
62
- │ │ ├── index.tsx
63
- │ │ └── about.tsx
64
- │ ├── lib/
65
- │ │ └── server-functions.ts
66
- │ └── start.ts
67
- ├── vite.config.ts
68
- ├── tsconfig.json
69
- └── package.json
70
- ```
71
-
72
39
  ## 환경 변수 검증
73
40
 
74
41
  ```typescript