@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.
- package/dist/index.js +109 -216
- package/package.json +8 -2
- package/templates/hono/CLAUDE.md +59 -328
- package/templates/hono/docs/architecture/architecture.md +93 -747
- package/templates/hono/docs/deployment/cloudflare.md +59 -513
- package/templates/hono/docs/deployment/docker.md +41 -356
- package/templates/hono/docs/deployment/index.md +54 -190
- package/templates/hono/docs/deployment/railway.md +36 -306
- package/templates/hono/docs/deployment/vercel.md +49 -434
- package/templates/hono/docs/library/ai-sdk/index.md +53 -290
- package/templates/hono/docs/library/ai-sdk/openrouter.md +19 -387
- package/templates/hono/docs/library/ai-sdk/providers.md +28 -394
- package/templates/hono/docs/library/ai-sdk/streaming.md +52 -353
- package/templates/hono/docs/library/ai-sdk/structured-output.md +63 -395
- package/templates/hono/docs/library/ai-sdk/tools.md +62 -431
- package/templates/hono/docs/library/hono/env-setup.md +24 -313
- package/templates/hono/docs/library/hono/error-handling.md +34 -295
- package/templates/hono/docs/library/hono/index.md +29 -121
- package/templates/hono/docs/library/hono/middleware.md +21 -188
- package/templates/hono/docs/library/hono/rpc.md +40 -341
- package/templates/hono/docs/library/hono/validation.md +35 -195
- package/templates/hono/docs/library/pino/index.md +42 -333
- package/templates/hono/docs/library/prisma/cloudflare-d1.md +64 -367
- package/templates/hono/docs/library/prisma/config.md +19 -260
- package/templates/hono/docs/library/prisma/index.md +67 -320
- package/templates/hono/docs/library/zod/index.md +53 -257
- package/templates/npx/CLAUDE.md +62 -274
- package/templates/npx/docs/references/patterns.md +160 -0
- package/templates/tanstack-start/CLAUDE.md +100 -256
- package/templates/tanstack-start/docs/architecture/architecture.md +44 -589
- package/templates/tanstack-start/docs/deployment/cloudflare.md +37 -424
- package/templates/tanstack-start/docs/deployment/index.md +57 -286
- package/templates/tanstack-start/docs/deployment/nitro.md +36 -318
- package/templates/tanstack-start/docs/deployment/railway.md +40 -409
- package/templates/tanstack-start/docs/deployment/vercel.md +43 -465
- package/templates/tanstack-start/docs/design/components.md +77 -311
- package/templates/tanstack-start/docs/design/index.md +113 -69
- package/templates/tanstack-start/docs/design/safe-area.md +51 -250
- package/templates/tanstack-start/docs/design/tailwind-setup.md +45 -359
- package/templates/tanstack-start/docs/guides/conventions.md +103 -0
- package/templates/tanstack-start/docs/guides/env-setup.md +34 -340
- package/templates/tanstack-start/docs/guides/getting-started.md +22 -209
- package/templates/tanstack-start/docs/guides/hooks.md +166 -0
- package/templates/tanstack-start/docs/guides/routes.md +166 -0
- package/templates/tanstack-start/docs/guides/services.md +143 -0
- package/templates/tanstack-start/docs/library/better-auth/2fa.md +27 -115
- package/templates/tanstack-start/docs/library/better-auth/advanced.md +22 -105
- package/templates/tanstack-start/docs/library/better-auth/index.md +17 -66
- package/templates/tanstack-start/docs/library/better-auth/plugins.md +11 -88
- package/templates/tanstack-start/docs/library/better-auth/session.md +12 -92
- package/templates/tanstack-start/docs/library/better-auth/setup.md +9 -91
- package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +30 -358
- package/templates/tanstack-start/docs/library/prisma/config.md +27 -327
- package/templates/tanstack-start/docs/library/prisma/crud.md +46 -174
- package/templates/tanstack-start/docs/library/prisma/index.md +23 -113
- package/templates/tanstack-start/docs/library/prisma/relations.md +31 -153
- package/templates/tanstack-start/docs/library/prisma/schema.md +40 -217
- package/templates/tanstack-start/docs/library/prisma/setup.md +12 -112
- package/templates/tanstack-start/docs/library/prisma/transactions.md +20 -110
- package/templates/tanstack-start/docs/library/tanstack-query/index.md +26 -97
- package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +28 -107
- package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +44 -146
- package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +33 -127
- package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +49 -149
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +19 -112
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +33 -80
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +28 -106
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +21 -118
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +34 -246
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +6 -39
- package/templates/tanstack-start/docs/library/zod/complex-types.md +32 -156
- package/templates/tanstack-start/docs/library/zod/index.md +31 -144
- package/templates/tanstack-start/docs/library/zod/transforms.md +20 -129
- package/templates/tanstack-start/docs/library/zod/validation.md +39 -155
- package/templates/hono/docs/commands/git.md +0 -145
- package/templates/hono/docs/mcp/context7.md +0 -106
- package/templates/hono/docs/mcp/index.md +0 -176
- package/templates/hono/docs/mcp/sequential-thinking.md +0 -101
- package/templates/hono/docs/mcp/serena.md +0 -269
- package/templates/hono/docs/mcp/sgrep.md +0 -105
- package/templates/hono/docs/skills/gemini-review/SKILL.md +0 -220
- package/templates/hono/docs/skills/gemini-review/references/checklists.md +0 -136
- package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +0 -303
- package/templates/npx/docs/commands/git.md +0 -145
- package/templates/npx/docs/mcp/index.md +0 -60
- package/templates/npx/docs/skills/gemini-review/SKILL.md +0 -220
- package/templates/npx/docs/skills/gemini-review/references/checklists.md +0 -134
- package/templates/npx/docs/skills/gemini-review/references/prompt-templates.md +0 -301
- package/templates/tanstack-start/docs/commands/git.md +0 -145
- package/templates/tanstack-start/docs/design/accessibility.md +0 -433
- package/templates/tanstack-start/docs/design/color.md +0 -235
- package/templates/tanstack-start/docs/design/spacing.md +0 -341
- package/templates/tanstack-start/docs/design/typography.md +0 -324
- package/templates/tanstack-start/docs/guides/best-practices.md +0 -950
- package/templates/tanstack-start/docs/guides/husky-lint-staged.md +0 -303
- package/templates/tanstack-start/docs/guides/prettier.md +0 -189
- package/templates/tanstack-start/docs/guides/project-templates.md +0 -710
- package/templates/tanstack-start/docs/library/tanstack-query/setup.md +0 -107
- package/templates/tanstack-start/docs/library/zod/basic-types.md +0 -186
- package/templates/tanstack-start/docs/mcp/context7.md +0 -204
- package/templates/tanstack-start/docs/mcp/index.md +0 -177
- package/templates/tanstack-start/docs/mcp/sequential-thinking.md +0 -180
- package/templates/tanstack-start/docs/mcp/serena.md +0 -269
- package/templates/tanstack-start/docs/mcp/sgrep.md +0 -174
- package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +0 -220
- package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +0 -144
- package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +0 -292
|
@@ -1,133 +1,41 @@
|
|
|
1
1
|
# Architecture
|
|
2
2
|
|
|
3
|
-
TanStack Start
|
|
3
|
+
TanStack Start 애플리케이션 아키텍처 가이드.
|
|
4
4
|
|
|
5
5
|
## System Overview
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
┌─────────────────────────────────────────────────────────────────┐
|
|
9
9
|
│ Client (Browser) │
|
|
10
|
-
│ │
|
|
11
10
|
│ ┌────────────────┐ ┌────────────────┐ ┌───────────────┐ │
|
|
12
11
|
│ │ React Router │───▶│ TanStack Query │───▶│ React UI │ │
|
|
13
12
|
│ │ (Navigation) │◀───│ (Caching) │◀───│ (Components) │ │
|
|
14
13
|
│ └────────────────┘ └───────┬────────┘ └───────────────┘ │
|
|
15
|
-
│ │ │
|
|
16
14
|
└────────────────────────────────┼─────────────────────────────────┘
|
|
17
|
-
│
|
|
18
15
|
▼
|
|
19
16
|
┌─────────────────────────────────────────────────────────────────┐
|
|
20
17
|
│ TanStack Start Server │
|
|
21
|
-
│ │
|
|
22
18
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
|
23
19
|
│ │ Server Functions │ │
|
|
24
|
-
│ │
|
|
25
|
-
│ │ - routes/-functions/ → 페이지 전용 서버 함수 │ │
|
|
26
|
-
│ │ - middlewares/ → 공통 미들웨어 │ │
|
|
20
|
+
│ │ routes/-functions/ → 페이지 전용 | functions/ → 글로벌 │ │
|
|
27
21
|
│ └────────────────────────────┬───────────────────────────────┘ │
|
|
28
|
-
│ │ │
|
|
29
22
|
│ ┌────────────────────────────▼───────────────────────────────┐ │
|
|
30
23
|
│ │ Services Layer │ │
|
|
31
|
-
│ │
|
|
32
|
-
│ │ - Business Logic │ │
|
|
33
|
-
│ │ - Data Transformation │ │
|
|
24
|
+
│ │ Zod Validation | Business Logic | Data Transformation │ │
|
|
34
25
|
│ └────────────────────────────┬───────────────────────────────┘ │
|
|
35
|
-
│ │ │
|
|
36
26
|
└───────────────────────────────┼──────────────────────────────────┘
|
|
37
|
-
│
|
|
38
27
|
▼
|
|
39
28
|
┌─────────────────────────────────────────────────────────────────┐
|
|
40
29
|
│ Database Layer │
|
|
41
|
-
│ │
|
|
42
30
|
│ ┌────────────────┐ ┌────────────────┐ ┌───────────────┐ │
|
|
43
31
|
│ │ Prisma Client │───▶│ PostgreSQL │ │ Redis │ │
|
|
44
|
-
│ │ (ORM) │ │ (Primary) │ │ (Cache) │ │
|
|
45
32
|
│ └────────────────┘ └────────────────┘ └───────────────┘ │
|
|
46
|
-
│ │
|
|
47
33
|
└─────────────────────────────────────────────────────────────────┘
|
|
48
34
|
```
|
|
49
35
|
|
|
50
|
-
## Project Structure
|
|
51
|
-
|
|
52
|
-
```
|
|
53
|
-
my-app/
|
|
54
|
-
├── src/
|
|
55
|
-
│ ├── routes/ # 파일 기반 라우팅
|
|
56
|
-
│ │ ├── __root.tsx # Root layout
|
|
57
|
-
│ │ ├── index.tsx # Home (/)
|
|
58
|
-
│ │ ├── -functions/ # 글로벌 라우트 레벨 서버 함수
|
|
59
|
-
│ │ │ ├── index.ts # re-export
|
|
60
|
-
│ │ │ └── get-session.ts # 세션 조회 함수
|
|
61
|
-
│ │ └── users/
|
|
62
|
-
│ │ ├── index.tsx # /users
|
|
63
|
-
│ │ ├── $id.tsx # /users/:id
|
|
64
|
-
│ │ ├── route.tsx # route 설정 (선택)
|
|
65
|
-
│ │ ├── -functions/ # 페이지 전용 서버 함수
|
|
66
|
-
│ │ │ ├── index.ts # re-export
|
|
67
|
-
│ │ │ ├── get-users.ts # 사용자 목록 조회
|
|
68
|
-
│ │ │ └── create-user.ts # 사용자 생성
|
|
69
|
-
│ │ ├── -components/ # 페이지 전용 컴포넌트
|
|
70
|
-
│ │ │ └── user-card.tsx
|
|
71
|
-
│ │ ├── -sections/ # 섹션 분리
|
|
72
|
-
│ │ │ ├── user-list-section.tsx
|
|
73
|
-
│ │ │ └── user-filter-section.tsx
|
|
74
|
-
│ │ └── -hooks/ # 페이지 전용 훅
|
|
75
|
-
│ │ └── use-users.ts
|
|
76
|
-
│ ├── functions/ # 글로벌 서버 함수
|
|
77
|
-
│ │ ├── index.ts # re-export
|
|
78
|
-
│ │ ├── get-current-user.ts # 현재 사용자 조회
|
|
79
|
-
│ │ ├── validate-session.ts # 세션 검증
|
|
80
|
-
│ │ └── middlewares/ # 서버 함수 미들웨어
|
|
81
|
-
│ │ ├── index.ts # re-export
|
|
82
|
-
│ │ ├── auth.ts # 인증 미들웨어
|
|
83
|
-
│ │ └── rate-limit.ts # 레이트 리밋 미들웨어
|
|
84
|
-
│ ├── components/ # 공통 컴포넌트
|
|
85
|
-
│ │ └── ui/
|
|
86
|
-
│ │ ├── button.tsx
|
|
87
|
-
│ │ ├── input.tsx
|
|
88
|
-
│ │ └── modal.tsx
|
|
89
|
-
│ ├── database/ # 데이터베이스 관련
|
|
90
|
-
│ │ ├── prisma.ts # Prisma Client 싱글톤
|
|
91
|
-
│ │ └── seed.ts # 시드 데이터 (선택)
|
|
92
|
-
│ ├── services/ # 도메인별 서비스 레이어
|
|
93
|
-
│ │ ├── user/
|
|
94
|
-
│ │ │ ├── index.ts # 진입점 (re-export)
|
|
95
|
-
│ │ │ ├── schemas.ts # Zod 스키마
|
|
96
|
-
│ │ │ ├── queries.ts # GET 요청
|
|
97
|
-
│ │ │ └── mutations.ts # POST 요청
|
|
98
|
-
│ │ ├── auth/
|
|
99
|
-
│ │ │ ├── index.ts
|
|
100
|
-
│ │ │ ├── schemas.ts
|
|
101
|
-
│ │ │ ├── queries.ts
|
|
102
|
-
│ │ │ └── mutations.ts
|
|
103
|
-
│ │ └── post/
|
|
104
|
-
│ │ ├── index.ts
|
|
105
|
-
│ │ ├── schemas.ts
|
|
106
|
-
│ │ ├── queries.ts
|
|
107
|
-
│ │ └── mutations.ts
|
|
108
|
-
│ ├── lib/ # 공통 유틸리티
|
|
109
|
-
│ │ ├── query-client.ts
|
|
110
|
-
│ │ └── utils.ts
|
|
111
|
-
│ ├── hooks/ # 공통 훅
|
|
112
|
-
│ │ └── use-auth.ts
|
|
113
|
-
│ ├── types/ # 타입 정의
|
|
114
|
-
│ │ └── index.ts
|
|
115
|
-
│ └── styles/
|
|
116
|
-
│ └── app.css
|
|
117
|
-
├── generated/
|
|
118
|
-
│ └── prisma/ # Prisma Client 출력
|
|
119
|
-
├── prisma/
|
|
120
|
-
│ └── schema.prisma
|
|
121
|
-
├── app.config.ts
|
|
122
|
-
├── package.json
|
|
123
|
-
└── tsconfig.json
|
|
124
|
-
```
|
|
125
|
-
|
|
126
36
|
## Layer Architecture
|
|
127
37
|
|
|
128
|
-
### 1. Routes Layer
|
|
129
|
-
|
|
130
|
-
파일 기반 라우팅으로 페이지를 구성합니다.
|
|
38
|
+
### 1. Routes Layer
|
|
131
39
|
|
|
132
40
|
```
|
|
133
41
|
routes/<route-name>/
|
|
@@ -135,208 +43,36 @@ routes/<route-name>/
|
|
|
135
43
|
├── route.tsx # route 설정 (loader, beforeLoad)
|
|
136
44
|
├── -functions/ # 페이지 전용 서버 함수
|
|
137
45
|
├── -components/ # 페이지 전용 컴포넌트
|
|
138
|
-
├── -sections/ # 섹션 분리
|
|
46
|
+
├── -sections/ # 섹션 분리
|
|
139
47
|
└── -hooks/ # 페이지 전용 훅
|
|
140
48
|
```
|
|
141
49
|
|
|
142
|
-
|
|
143
|
-
- `-` 접두사 폴더는 라우트에서 제외
|
|
144
|
-
- 페이지별로 서버 함수, 컴포넌트, 섹션, 훅을 분리
|
|
145
|
-
- Section은 UI 영역 단위, Component는 재사용 단위
|
|
146
|
-
- `-functions/`는 해당 페이지에서만 사용하는 서버 함수
|
|
147
|
-
|
|
148
|
-
```tsx
|
|
149
|
-
// routes/users/index.tsx
|
|
150
|
-
import { createFileRoute } from '@tanstack/react-router'
|
|
151
|
-
import { UserListSection } from './-sections/user-list-section'
|
|
152
|
-
import { UserFilterSection } from './-sections/user-filter-section'
|
|
153
|
-
|
|
154
|
-
export const Route = createFileRoute('/users/')({
|
|
155
|
-
component: UsersPage,
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
const UsersPage = (): JSX.Element => {
|
|
159
|
-
return (
|
|
160
|
-
<div className="container mx-auto p-4">
|
|
161
|
-
<h1 className="text-2xl font-bold mb-4">Users</h1>
|
|
162
|
-
<UserFilterSection />
|
|
163
|
-
<UserListSection />
|
|
164
|
-
</div>
|
|
165
|
-
)
|
|
166
|
-
}
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### 2. Services Layer (Business Logic)
|
|
170
|
-
|
|
171
|
-
도메인별로 서비스를 구성합니다.
|
|
50
|
+
### 2. Services Layer
|
|
172
51
|
|
|
173
52
|
```
|
|
174
53
|
services/<domain>/
|
|
175
54
|
├── index.ts # 진입점 (re-export)
|
|
176
55
|
├── schemas.ts # Zod 스키마
|
|
177
|
-
├── queries.ts # GET 요청
|
|
178
|
-
└── mutations.ts # POST 요청
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
**Schemas** - 입력 검증:
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
// services/user/schemas.ts
|
|
185
|
-
import { z } from 'zod'
|
|
186
|
-
|
|
187
|
-
export const createUserSchema = z.object({
|
|
188
|
-
email: z.email(),
|
|
189
|
-
name: z.string().min(1).max(100).trim(),
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
export type CreateUserInput = z.infer<typeof createUserSchema>
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
**Queries** - 읽기 작업:
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
// services/user/queries.ts
|
|
199
|
-
import { createServerFn } from '@tanstack/react-start'
|
|
200
|
-
import { prisma } from '@/database/prisma'
|
|
201
|
-
|
|
202
|
-
export const getUsers = createServerFn({ method: 'GET' })
|
|
203
|
-
.handler(async () => {
|
|
204
|
-
return prisma.user.findMany({
|
|
205
|
-
orderBy: { createdAt: 'desc' },
|
|
206
|
-
})
|
|
207
|
-
})
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
**Mutations** - 쓰기 작업:
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
// services/user/mutations.ts
|
|
214
|
-
import { createServerFn } from '@tanstack/react-start'
|
|
215
|
-
import { prisma } from '@/database/prisma'
|
|
216
|
-
import { createUserSchema } from './schemas'
|
|
217
|
-
|
|
218
|
-
export const createUser = createServerFn({ method: 'POST' })
|
|
219
|
-
.inputValidator(createUserSchema)
|
|
220
|
-
.handler(async ({ data }) => {
|
|
221
|
-
return prisma.user.create({ data })
|
|
222
|
-
})
|
|
56
|
+
├── queries.ts # GET 요청
|
|
57
|
+
└── mutations.ts # POST 요청
|
|
223
58
|
```
|
|
224
59
|
|
|
225
60
|
### 3. Server Functions Layer
|
|
226
61
|
|
|
227
|
-
서버 함수를 체계적으로 구성합니다. 파일당 하나의 서버 함수만 정의합니다.
|
|
228
|
-
|
|
229
62
|
```
|
|
230
|
-
|
|
231
|
-
├── index.ts
|
|
232
|
-
├── <function-name>.ts
|
|
233
|
-
└── middlewares/
|
|
234
|
-
|
|
235
|
-
└── <middleware-name>.ts # 개별 미들웨어
|
|
63
|
+
functions/ # 글로벌 서버 함수
|
|
64
|
+
├── index.ts
|
|
65
|
+
├── <function-name>.ts # 파일당 하나의 함수
|
|
66
|
+
└── middlewares/
|
|
67
|
+
└── <middleware-name>.ts
|
|
236
68
|
|
|
237
|
-
routes/<route
|
|
238
|
-
|
|
239
|
-
└── <function-name>.ts # 개별 서버 함수
|
|
69
|
+
routes/<route>/-functions/ # 페이지 전용
|
|
70
|
+
└── <function-name>.ts
|
|
240
71
|
```
|
|
241
72
|
|
|
242
|
-
**규칙**:
|
|
243
|
-
- **파일당 하나의 함수**: 각 파일에는 하나의 서버 함수만 정의
|
|
244
|
-
- **명확한 네이밍**: 파일명이 곧 함수의 역할 (`get-users.ts`, `create-user.ts`)
|
|
245
|
-
- **글로벌 vs 로컬**: 여러 페이지에서 사용 → `src/functions/`, 특정 페이지 전용 → `-functions/`
|
|
73
|
+
**규칙**: 파일당 하나의 함수, 글로벌 vs 로컬 분리
|
|
246
74
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
// src/functions/get-current-user.ts
|
|
251
|
-
import { createServerFn } from '@tanstack/react-start'
|
|
252
|
-
import { prisma } from '@/database/prisma'
|
|
253
|
-
import { authMiddleware } from './middlewares'
|
|
254
|
-
|
|
255
|
-
export const getCurrentUser = createServerFn({ method: 'GET' })
|
|
256
|
-
.middleware([authMiddleware])
|
|
257
|
-
.handler(async ({ context }) => {
|
|
258
|
-
return prisma.user.findUnique({
|
|
259
|
-
where: { id: context.userId },
|
|
260
|
-
})
|
|
261
|
-
})
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
```typescript
|
|
265
|
-
// src/functions/index.ts
|
|
266
|
-
export { getCurrentUser } from './get-current-user'
|
|
267
|
-
export { validateSession } from './validate-session'
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
**페이지 전용 서버 함수** - 특정 페이지에서만 사용:
|
|
271
|
-
|
|
272
|
-
```typescript
|
|
273
|
-
// routes/users/-functions/get-users.ts
|
|
274
|
-
import { createServerFn } from '@tanstack/react-start'
|
|
275
|
-
import { prisma } from '@/database/prisma'
|
|
276
|
-
|
|
277
|
-
export const getUsers = createServerFn({ method: 'GET' })
|
|
278
|
-
.handler(async () => {
|
|
279
|
-
return prisma.user.findMany({
|
|
280
|
-
orderBy: { createdAt: 'desc' },
|
|
281
|
-
})
|
|
282
|
-
})
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
```typescript
|
|
286
|
-
// routes/users/-functions/create-user.ts
|
|
287
|
-
import { createServerFn } from '@tanstack/react-start'
|
|
288
|
-
import { prisma } from '@/database/prisma'
|
|
289
|
-
import { createUserSchema } from '@/services/user/schemas'
|
|
290
|
-
|
|
291
|
-
export const createUser = createServerFn({ method: 'POST' })
|
|
292
|
-
.inputValidator(createUserSchema)
|
|
293
|
-
.handler(async ({ data }) => {
|
|
294
|
-
return prisma.user.create({ data })
|
|
295
|
-
})
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
```typescript
|
|
299
|
-
// routes/users/-functions/index.ts
|
|
300
|
-
export { getUsers } from './get-users'
|
|
301
|
-
export { createUser } from './create-user'
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
**미들웨어** - 공통 로직 재사용:
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
307
|
-
// src/functions/middlewares/auth.ts
|
|
308
|
-
import { createMiddleware } from '@tanstack/react-start'
|
|
309
|
-
|
|
310
|
-
export const authMiddleware = createMiddleware()
|
|
311
|
-
.server(async ({ next }) => {
|
|
312
|
-
const session = await getSession()
|
|
313
|
-
if (!session) {
|
|
314
|
-
throw new Error('Unauthorized')
|
|
315
|
-
}
|
|
316
|
-
return next({ context: { userId: session.userId } })
|
|
317
|
-
})
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
```typescript
|
|
321
|
-
// src/functions/middlewares/rate-limit.ts
|
|
322
|
-
import { createMiddleware } from '@tanstack/react-start'
|
|
323
|
-
|
|
324
|
-
export const rateLimitMiddleware = createMiddleware()
|
|
325
|
-
.server(async ({ next }) => {
|
|
326
|
-
// 레이트 리밋 로직
|
|
327
|
-
return next()
|
|
328
|
-
})
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
```typescript
|
|
332
|
-
// src/functions/middlewares/index.ts
|
|
333
|
-
export { authMiddleware } from './auth'
|
|
334
|
-
export { rateLimitMiddleware } from './rate-limit'
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
### 4. Database Layer (Data Access)
|
|
338
|
-
|
|
339
|
-
Prisma를 통한 데이터 액세스를 담당합니다.
|
|
75
|
+
### 4. Database Layer
|
|
340
76
|
|
|
341
77
|
```typescript
|
|
342
78
|
// database/prisma.ts
|
|
@@ -347,10 +83,7 @@ const globalForPrisma = globalThis as unknown as {
|
|
|
347
83
|
}
|
|
348
84
|
|
|
349
85
|
export const prisma =
|
|
350
|
-
globalForPrisma.prisma ??
|
|
351
|
-
new PrismaClient({
|
|
352
|
-
log: process.env.NODE_ENV === 'development' ? ['query'] : [],
|
|
353
|
-
})
|
|
86
|
+
globalForPrisma.prisma ?? new PrismaClient()
|
|
354
87
|
|
|
355
88
|
if (process.env.NODE_ENV !== 'production') {
|
|
356
89
|
globalForPrisma.prisma = prisma
|
|
@@ -362,316 +95,38 @@ if (process.env.NODE_ENV !== 'production') {
|
|
|
362
95
|
### Query Flow (읽기)
|
|
363
96
|
|
|
364
97
|
```
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
│
|
|
369
|
-
▼
|
|
370
|
-
┌─────────────────┐
|
|
371
|
-
│ TanStack Query │ ◀── Cache check
|
|
372
|
-
└────────┬────────┘
|
|
373
|
-
│ Cache miss
|
|
374
|
-
▼
|
|
375
|
-
┌─────────────────┐
|
|
376
|
-
│ Server Function│ ← getUsers()
|
|
377
|
-
│ (method: GET) │
|
|
378
|
-
└────────┬────────┘
|
|
379
|
-
│
|
|
380
|
-
▼
|
|
381
|
-
┌─────────────────┐
|
|
382
|
-
│ Prisma Query │
|
|
383
|
-
└────────┬────────┘
|
|
384
|
-
│
|
|
385
|
-
▼
|
|
386
|
-
┌─────────────────┐
|
|
387
|
-
│ Database │
|
|
388
|
-
└─────────────────┘
|
|
98
|
+
Page Hook → useQuery → Server Function (GET) → Prisma → Database
|
|
99
|
+
↑
|
|
100
|
+
TanStack Query (Cache)
|
|
389
101
|
```
|
|
390
102
|
|
|
391
103
|
### Mutation Flow (쓰기)
|
|
392
104
|
|
|
393
105
|
```
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
└────────┬────────┘
|
|
402
|
-
│
|
|
403
|
-
▼
|
|
404
|
-
┌─────────────────┐
|
|
405
|
-
│ Server Function│ ← createUser()
|
|
406
|
-
│ (method: POST) │
|
|
407
|
-
└────────┬────────┘
|
|
408
|
-
│
|
|
409
|
-
▼
|
|
410
|
-
┌─────────────────┐
|
|
411
|
-
│ Zod Validation │ ← Schema check
|
|
412
|
-
└────────┬────────┘
|
|
413
|
-
│ Valid
|
|
414
|
-
▼
|
|
415
|
-
┌─────────────────┐
|
|
416
|
-
│ Prisma Mutation│
|
|
417
|
-
└────────┬────────┘
|
|
418
|
-
│
|
|
419
|
-
▼
|
|
420
|
-
┌─────────────────┐
|
|
421
|
-
│ Database │
|
|
422
|
-
└────────┬────────┘
|
|
423
|
-
│
|
|
424
|
-
▼
|
|
425
|
-
┌─────────────────┐
|
|
426
|
-
│ Cache Invalidate│ ← invalidateQueries
|
|
427
|
-
└─────────────────┘
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
## Component Patterns
|
|
431
|
-
|
|
432
|
-
### Page Hook Pattern
|
|
433
|
-
|
|
434
|
-
```typescript
|
|
435
|
-
// routes/users/-hooks/use-users.ts
|
|
436
|
-
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|
437
|
-
import { getUsers, createUser, deleteUser } from '@/services/user'
|
|
438
|
-
|
|
439
|
-
export const useUsers = () => {
|
|
440
|
-
const queryClient = useQueryClient()
|
|
441
|
-
|
|
442
|
-
const { data: users, isLoading, error } = useQuery({
|
|
443
|
-
queryKey: ['users'],
|
|
444
|
-
queryFn: () => getUsers(),
|
|
445
|
-
})
|
|
446
|
-
|
|
447
|
-
const createMutation = useMutation({
|
|
448
|
-
mutationFn: createUser,
|
|
449
|
-
onSuccess: () => {
|
|
450
|
-
queryClient.invalidateQueries({ queryKey: ['users'] })
|
|
451
|
-
},
|
|
452
|
-
})
|
|
453
|
-
|
|
454
|
-
return {
|
|
455
|
-
users,
|
|
456
|
-
isLoading,
|
|
457
|
-
error,
|
|
458
|
-
createUser: createMutation.mutate,
|
|
459
|
-
isCreating: createMutation.isPending,
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
### Section Pattern
|
|
465
|
-
|
|
466
|
-
```tsx
|
|
467
|
-
// routes/users/-sections/user-list-section.tsx
|
|
468
|
-
import { useUsers } from '../-hooks/use-users'
|
|
469
|
-
import { UserCard } from '../-components/user-card'
|
|
470
|
-
|
|
471
|
-
export const UserListSection = (): JSX.Element => {
|
|
472
|
-
const { users, isLoading, error } = useUsers()
|
|
473
|
-
|
|
474
|
-
if (isLoading) return <div>Loading...</div>
|
|
475
|
-
if (error) return <div>Error: {error.message}</div>
|
|
476
|
-
|
|
477
|
-
return (
|
|
478
|
-
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
479
|
-
{users?.map((user) => (
|
|
480
|
-
<UserCard key={user.id} user={user} />
|
|
481
|
-
))}
|
|
482
|
-
</div>
|
|
483
|
-
)
|
|
484
|
-
}
|
|
106
|
+
Form Submit → useMutation → Server Function (POST)
|
|
107
|
+
↓
|
|
108
|
+
Zod Validation
|
|
109
|
+
↓
|
|
110
|
+
Prisma Mutation → Database
|
|
111
|
+
↓
|
|
112
|
+
Cache Invalidate
|
|
485
113
|
```
|
|
486
114
|
|
|
487
115
|
## Technology Stack
|
|
488
116
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
|
492
|
-
|
|
493
|
-
|
|
|
494
|
-
|
|
|
495
|
-
|
|
|
496
|
-
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
### Database
|
|
507
|
-
|
|
508
|
-
| Technology | Purpose |
|
|
509
|
-
|------------|---------|
|
|
510
|
-
| PostgreSQL | Primary Database |
|
|
511
|
-
| Redis | Caching (선택) |
|
|
512
|
-
|
|
513
|
-
## TypeScript Configuration
|
|
514
|
-
|
|
515
|
-
```json
|
|
516
|
-
{
|
|
517
|
-
"compilerOptions": {
|
|
518
|
-
"target": "ES2022",
|
|
519
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
520
|
-
"module": "ESNext",
|
|
521
|
-
"moduleResolution": "bundler",
|
|
522
|
-
"jsx": "react-jsx",
|
|
523
|
-
"strict": true,
|
|
524
|
-
"noUnusedLocals": true,
|
|
525
|
-
"noUnusedParameters": true,
|
|
526
|
-
"paths": {
|
|
527
|
-
"@/*": ["./src/*"]
|
|
528
|
-
}
|
|
529
|
-
},
|
|
530
|
-
"include": ["src/**/*", "*.config.ts"]
|
|
531
|
-
}
|
|
532
|
-
```
|
|
533
|
-
|
|
534
|
-
## Deployment Architecture
|
|
535
|
-
|
|
536
|
-
### Vercel
|
|
537
|
-
|
|
538
|
-
```
|
|
539
|
-
┌─────────────────────────────────────┐
|
|
540
|
-
│ Vercel Platform │
|
|
541
|
-
│ │
|
|
542
|
-
│ ┌─────────────┐ ┌─────────────┐ │
|
|
543
|
-
│ │ Edge │ │ Serverless │ │
|
|
544
|
-
│ │ Functions │ │ Functions │ │
|
|
545
|
-
│ └─────────────┘ └─────────────┘ │
|
|
546
|
-
│ │
|
|
547
|
-
└───────────────────┬─────────────────┘
|
|
548
|
-
│
|
|
549
|
-
▼
|
|
550
|
-
┌───────────────┐
|
|
551
|
-
│ Database │
|
|
552
|
-
│ (Neon/Supabase) │
|
|
553
|
-
└───────────────┘
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
### Railway / Cloudflare
|
|
557
|
-
|
|
558
|
-
```
|
|
559
|
-
┌─────────────────────────────────────┐
|
|
560
|
-
│ Platform (Railway/CF) │
|
|
561
|
-
│ │
|
|
562
|
-
│ ┌─────────────────────────────┐ │
|
|
563
|
-
│ │ Node.js / Workers │ │
|
|
564
|
-
│ │ (Full-stack Server) │ │
|
|
565
|
-
│ └─────────────────────────────┘ │
|
|
566
|
-
│ │
|
|
567
|
-
└───────────────────┬─────────────────┘
|
|
568
|
-
│
|
|
569
|
-
▼
|
|
570
|
-
┌───────────────┐
|
|
571
|
-
│ Database │
|
|
572
|
-
│ (PostgreSQL) │
|
|
573
|
-
└───────────────┘
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
## Documentation Structure
|
|
577
|
-
|
|
578
|
-
```
|
|
579
|
-
docs/
|
|
580
|
-
├── guides/ # 개발 가이드
|
|
581
|
-
│ ├── getting-started.md # 시작 가이드
|
|
582
|
-
│ ├── best-practices.md # 모범 사례
|
|
583
|
-
│ └── project-templates.md # 템플릿
|
|
584
|
-
├── library/ # 라이브러리 레퍼런스
|
|
585
|
-
│ ├── tanstack-start/ # TanStack Start
|
|
586
|
-
│ ├── tanstack-query/ # TanStack Query
|
|
587
|
-
│ ├── prisma/ # Prisma
|
|
588
|
-
│ ├── zod/ # Zod
|
|
589
|
-
│ └── better-auth/ # Better Auth
|
|
590
|
-
├── deployment/ # 배포 가이드
|
|
591
|
-
│ ├── nitro.md # Nitro 설정
|
|
592
|
-
│ ├── vercel.md # Vercel 배포
|
|
593
|
-
│ ├── cloudflare.md # Cloudflare 배포
|
|
594
|
-
│ └── railway.md # Railway 배포
|
|
595
|
-
└── architecture/ # 아키텍처
|
|
596
|
-
└── architecture.md # 시스템 아키텍처
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
## Security Considerations
|
|
600
|
-
|
|
601
|
-
### Input Validation
|
|
602
|
-
|
|
603
|
-
모든 서버 함수에서 Zod를 통한 입력 검증:
|
|
604
|
-
|
|
605
|
-
```typescript
|
|
606
|
-
export const createUser = createServerFn({ method: 'POST' })
|
|
607
|
-
.inputValidator(createUserSchema) // 자동 검증
|
|
608
|
-
.handler(async ({ data }) => {
|
|
609
|
-
// data는 이미 검증됨
|
|
610
|
-
})
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
### Environment Variables
|
|
614
|
-
|
|
615
|
-
```typescript
|
|
616
|
-
// lib/env.ts
|
|
617
|
-
import { z } from 'zod'
|
|
618
|
-
|
|
619
|
-
const envSchema = z.object({
|
|
620
|
-
NODE_ENV: z.enum(['development', 'production', 'test']),
|
|
621
|
-
DATABASE_URL: z.url(),
|
|
622
|
-
API_SECRET: z.string().min(32),
|
|
623
|
-
})
|
|
624
|
-
|
|
625
|
-
export const env = envSchema.parse(process.env)
|
|
626
|
-
```
|
|
627
|
-
|
|
628
|
-
### Authentication
|
|
629
|
-
|
|
630
|
-
Better Auth를 통한 인증 관리:
|
|
631
|
-
|
|
632
|
-
```typescript
|
|
633
|
-
// services/auth/index.ts
|
|
634
|
-
import { betterAuth } from 'better-auth'
|
|
635
|
-
import { prismaAdapter } from 'better-auth/adapters/prisma'
|
|
636
|
-
|
|
637
|
-
export const auth = betterAuth({
|
|
638
|
-
database: prismaAdapter(prisma),
|
|
639
|
-
emailAndPassword: { enabled: true },
|
|
640
|
-
})
|
|
641
|
-
```
|
|
642
|
-
|
|
643
|
-
## Performance Considerations
|
|
644
|
-
|
|
645
|
-
### Caching Strategy
|
|
646
|
-
|
|
647
|
-
```typescript
|
|
648
|
-
// TanStack Query 캐싱
|
|
649
|
-
const { data } = useQuery({
|
|
650
|
-
queryKey: ['users'],
|
|
651
|
-
queryFn: () => getUsers(),
|
|
652
|
-
staleTime: 60 * 1000, // 1분간 fresh
|
|
653
|
-
gcTime: 5 * 60 * 1000, // 5분간 캐시 유지
|
|
654
|
-
})
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
### Database Optimization
|
|
658
|
-
|
|
659
|
-
```typescript
|
|
660
|
-
// Prisma 쿼리 최적화
|
|
661
|
-
const users = await prisma.user.findMany({
|
|
662
|
-
select: { // 필요한 필드만 선택
|
|
663
|
-
id: true,
|
|
664
|
-
name: true,
|
|
665
|
-
email: true,
|
|
666
|
-
},
|
|
667
|
-
take: 20, // 페이지네이션
|
|
668
|
-
skip: (page - 1) * 20,
|
|
669
|
-
})
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
## Related Documentation
|
|
673
|
-
|
|
674
|
-
- [Getting Started](../guides/getting-started.md) - 프로젝트 시작 가이드
|
|
675
|
-
- [Best Practices](../guides/best-practices.md) - 개발 모범 사례
|
|
676
|
-
- [Project Templates](../guides/project-templates.md) - 프로젝트 템플릿
|
|
677
|
-
- [Deployment](../deployment/index.md) - 배포 가이드
|
|
117
|
+
| Layer | Technology | Version |
|
|
118
|
+
|-------|------------|---------|
|
|
119
|
+
| Framework | TanStack Start | latest |
|
|
120
|
+
| Router | TanStack Router | latest |
|
|
121
|
+
| Data | TanStack Query | latest |
|
|
122
|
+
| ORM | Prisma | 7.x |
|
|
123
|
+
| Validation | Zod | 4.x |
|
|
124
|
+
| Database | PostgreSQL | - |
|
|
125
|
+
| UI | React 18+ | - |
|
|
126
|
+
|
|
127
|
+
## 관련 문서
|
|
128
|
+
|
|
129
|
+
- [conventions.md](../guides/conventions.md) - 코드 컨벤션
|
|
130
|
+
- [routes.md](../guides/routes.md) - 라우트 구조
|
|
131
|
+
- [hooks.md](../guides/hooks.md) - Custom Hook 패턴
|
|
132
|
+
- [services.md](../guides/services.md) - Service Layer
|