@kood/claude-code 0.2.4 → 0.3.0

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 (130) hide show
  1. package/dist/index.js +13 -8
  2. package/package.json +1 -1
  3. package/templates/.claude/agents/code-reviewer.md +371 -19
  4. package/templates/.claude/agents/dependency-manager.md +197 -0
  5. package/templates/.claude/agents/deployment-validator.md +136 -0
  6. package/templates/.claude/agents/git-operator.md +147 -0
  7. package/templates/.claude/agents/implementation-executor.md +202 -0
  8. package/templates/.claude/agents/lint-fixer.md +155 -0
  9. package/templates/.claude/agents/refactor-advisor.md +339 -29
  10. package/templates/.claude/commands/agent-creator.md +355 -0
  11. package/templates/.claude/commands/docs-creator.md +404 -163
  12. package/templates/.claude/commands/docs-refactor.md +400 -113
  13. package/templates/.claude/commands/execute.md +357 -185
  14. package/templates/.claude/commands/git-all.md +16 -70
  15. package/templates/.claude/commands/git-session.md +36 -68
  16. package/templates/.claude/commands/git.md +20 -69
  17. package/templates/.claude/commands/lint-fix.md +164 -107
  18. package/templates/.claude/commands/lint-init.md +142 -168
  19. package/templates/.claude/commands/plan.md +300 -84
  20. package/templates/.claude/commands/prd.md +613 -0
  21. package/templates/.claude/commands/pre-deploy.md +242 -0
  22. package/templates/.claude/commands/subagent-creator.md +118 -0
  23. package/templates/.claude/commands/version-update.md +45 -57
  24. package/templates/hono/CLAUDE.md +99 -54
  25. package/templates/hono/docs/guides/conventions.md +352 -0
  26. package/templates/hono/docs/guides/env-setup.md +347 -0
  27. package/templates/hono/docs/guides/getting-started.md +239 -0
  28. package/templates/hono/docs/library/hono/error-handling.md +20 -29
  29. package/templates/hono/docs/library/hono/index.md +25 -52
  30. package/templates/hono/docs/library/hono/middleware.md +16 -75
  31. package/templates/hono/docs/library/hono/rpc.md +7 -35
  32. package/templates/hono/docs/library/hono/validation.md +25 -45
  33. package/templates/hono/docs/library/t3-env/index.md +374 -0
  34. package/templates/npx/CLAUDE.md +165 -65
  35. package/templates/npx/docs/library/commander/index.md +10 -73
  36. package/templates/npx/docs/library/fs-extra/index.md +21 -113
  37. package/templates/npx/docs/library/prompts/index.md +30 -176
  38. package/templates/npx/docs/references/patterns.md +75 -48
  39. package/templates/tanstack-start/CLAUDE.md +101 -77
  40. package/templates/tanstack-start/docs/architecture.md +427 -0
  41. package/templates/tanstack-start/docs/design.md +558 -0
  42. package/templates/tanstack-start/docs/guides/conventions.md +132 -32
  43. package/templates/tanstack-start/docs/guides/env-setup.md +127 -62
  44. package/templates/tanstack-start/docs/guides/getting-started.md +81 -20
  45. package/templates/tanstack-start/docs/guides/hooks.md +241 -36
  46. package/templates/tanstack-start/docs/guides/routes.md +213 -61
  47. package/templates/tanstack-start/docs/guides/services.md +260 -24
  48. package/templates/tanstack-start/docs/library/better-auth/index.md +469 -16
  49. package/templates/tanstack-start/docs/library/t3-env/index.md +307 -0
  50. package/templates/tanstack-start/docs/library/tanstack-query/index.md +12 -21
  51. package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +22 -35
  52. package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +7 -24
  53. package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +26 -39
  54. package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +23 -26
  55. package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +32 -147
  56. package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +25 -167
  57. package/templates/tanstack-start/docs/library/tanstack-router/index.md +39 -74
  58. package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +46 -116
  59. package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +35 -154
  60. package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +32 -171
  61. package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +7 -15
  62. package/templates/tanstack-start/docs/library/tanstack-start/routing.md +16 -23
  63. package/templates/tanstack-start/docs/library/zod/complex-types.md +12 -31
  64. package/templates/tanstack-start/docs/library/zod/index.md +18 -35
  65. package/templates/tanstack-start/docs/library/zod/transforms.md +11 -25
  66. package/templates/tanstack-start/docs/library/zod/validation.md +12 -34
  67. package/templates/.claude/agents/debug-detective.md +0 -37
  68. package/templates/.claude/agents/test-writer.md +0 -41
  69. package/templates/.claude/commands/feedback.md +0 -199
  70. package/templates/.claude/commands/ts-fix.md +0 -176
  71. package/templates/.claude/skills/command-creator/LICENSE.txt +0 -202
  72. package/templates/.claude/skills/command-creator/SKILL.md +0 -245
  73. package/templates/.claude/skills/command-creator/scripts/init_command.py +0 -244
  74. package/templates/.claude/skills/command-creator/scripts/package_command.py +0 -125
  75. package/templates/.claude/skills/command-creator/scripts/quick_validate.py +0 -143
  76. package/templates/.claude/skills/frontend-design/SKILL.md +0 -310
  77. package/templates/.claude/skills/frontend-design/references/animation-patterns.md +0 -446
  78. package/templates/.claude/skills/frontend-design/references/colors-2026.md +0 -244
  79. package/templates/.claude/skills/frontend-design/references/typography-2026.md +0 -302
  80. package/templates/.claude/skills/gemini-review/SKILL.md +0 -118
  81. package/templates/.claude/skills/gemini-review/references/checklists.md +0 -129
  82. package/templates/.claude/skills/gemini-review/references/prompt-templates.md +0 -274
  83. package/templates/.claude/skills/skill-creator/LICENSE.txt +0 -202
  84. package/templates/.claude/skills/skill-creator/SKILL.md +0 -184
  85. package/templates/.claude/skills/skill-creator/scripts/init_skill.py +0 -303
  86. package/templates/.claude/skills/skill-creator/scripts/package_skill.py +0 -110
  87. package/templates/.claude/skills/skill-creator/scripts/quick_validate.py +0 -65
  88. package/templates/hono/docs/library/ai-sdk/index.md +0 -190
  89. package/templates/hono/docs/library/ai-sdk/openrouter.md +0 -111
  90. package/templates/hono/docs/library/ai-sdk/providers.md +0 -102
  91. package/templates/hono/docs/library/ai-sdk/streaming.md +0 -146
  92. package/templates/hono/docs/library/ai-sdk/structured-output.md +0 -161
  93. package/templates/hono/docs/library/ai-sdk/tools.md +0 -144
  94. package/templates/hono/docs/library/drizzle/cloudflare-d1.md +0 -247
  95. package/templates/hono/docs/library/drizzle/config.md +0 -167
  96. package/templates/hono/docs/library/drizzle/index.md +0 -259
  97. package/templates/hono/docs/library/hono/env-setup.md +0 -169
  98. package/templates/hono/docs/library/pino/index.md +0 -146
  99. package/templates/tanstack-start/docs/architecture/architecture.md +0 -243
  100. package/templates/tanstack-start/docs/deployment/cloudflare.md +0 -132
  101. package/templates/tanstack-start/docs/deployment/index.md +0 -163
  102. package/templates/tanstack-start/docs/deployment/nitro.md +0 -110
  103. package/templates/tanstack-start/docs/deployment/railway.md +0 -147
  104. package/templates/tanstack-start/docs/deployment/vercel.md +0 -135
  105. package/templates/tanstack-start/docs/design/components.md +0 -175
  106. package/templates/tanstack-start/docs/design/index.md +0 -151
  107. package/templates/tanstack-start/docs/design/safe-area.md +0 -118
  108. package/templates/tanstack-start/docs/design/tailwind-setup.md +0 -156
  109. package/templates/tanstack-start/docs/library/ai-sdk/hooks.md +0 -472
  110. package/templates/tanstack-start/docs/library/ai-sdk/index.md +0 -264
  111. package/templates/tanstack-start/docs/library/ai-sdk/openrouter.md +0 -371
  112. package/templates/tanstack-start/docs/library/ai-sdk/providers.md +0 -403
  113. package/templates/tanstack-start/docs/library/ai-sdk/streaming.md +0 -320
  114. package/templates/tanstack-start/docs/library/ai-sdk/structured-output.md +0 -454
  115. package/templates/tanstack-start/docs/library/ai-sdk/tools.md +0 -473
  116. package/templates/tanstack-start/docs/library/better-auth/2fa.md +0 -48
  117. package/templates/tanstack-start/docs/library/better-auth/advanced.md +0 -55
  118. package/templates/tanstack-start/docs/library/better-auth/plugins.md +0 -34
  119. package/templates/tanstack-start/docs/library/better-auth/session.md +0 -47
  120. package/templates/tanstack-start/docs/library/better-auth/setup.md +0 -41
  121. package/templates/tanstack-start/docs/library/drizzle/cloudflare-d1.md +0 -147
  122. package/templates/tanstack-start/docs/library/drizzle/config.md +0 -118
  123. package/templates/tanstack-start/docs/library/drizzle/crud.md +0 -205
  124. package/templates/tanstack-start/docs/library/drizzle/index.md +0 -79
  125. package/templates/tanstack-start/docs/library/drizzle/relations.md +0 -202
  126. package/templates/tanstack-start/docs/library/drizzle/schema.md +0 -154
  127. package/templates/tanstack-start/docs/library/drizzle/setup.md +0 -96
  128. package/templates/tanstack-start/docs/library/drizzle/transactions.md +0 -127
  129. package/templates/tanstack-start/docs/library/pino/index.md +0 -320
  130. /package/templates/hono/docs/{architecture/architecture.md → architecture.md} +0 -0
@@ -1,202 +0,0 @@
1
- # Drizzle - 관계 정의
2
-
3
- ## 관계 유형
4
-
5
- ### 1:N (One-to-Many)
6
-
7
- ```typescript
8
- import { pgTable, serial, text, integer } from 'drizzle-orm/pg-core'
9
- import { relations } from 'drizzle-orm'
10
-
11
- // 사용자
12
- export const users = pgTable('users', {
13
- id: serial('id').primaryKey(),
14
- name: text('name').notNull(),
15
- })
16
-
17
- // 사용자 관계
18
- export const usersRelations = relations(users, ({ many }) => ({
19
- posts: many(posts),
20
- }))
21
-
22
- // 게시글
23
- export const posts = pgTable('posts', {
24
- id: serial('id').primaryKey(),
25
- title: text('title').notNull(),
26
- authorId: integer('author_id').references(() => users.id),
27
- })
28
-
29
- // 게시글 관계
30
- export const postsRelations = relations(posts, ({ one }) => ({
31
- author: one(users, {
32
- fields: [posts.authorId],
33
- references: [users.id],
34
- }),
35
- }))
36
- ```
37
-
38
- ### 1:1 (One-to-One)
39
-
40
- ```typescript
41
- // 사용자
42
- export const users = pgTable('users', {
43
- id: serial('id').primaryKey(),
44
- name: text('name').notNull(),
45
- })
46
-
47
- export const usersRelations = relations(users, ({ one }) => ({
48
- profile: one(profiles),
49
- }))
50
-
51
- // 프로필
52
- export const profiles = pgTable('profiles', {
53
- id: serial('id').primaryKey(),
54
- bio: text('bio'),
55
- userId: integer('user_id').unique().references(() => users.id),
56
- })
57
-
58
- export const profilesRelations = relations(profiles, ({ one }) => ({
59
- user: one(users, {
60
- fields: [profiles.userId],
61
- references: [users.id],
62
- }),
63
- }))
64
- ```
65
-
66
- ### M:N (Many-to-Many)
67
-
68
- ```typescript
69
- // 사용자
70
- export const users = pgTable('users', {
71
- id: serial('id').primaryKey(),
72
- name: text('name').notNull(),
73
- })
74
-
75
- export const usersRelations = relations(users, ({ many }) => ({
76
- usersToGroups: many(usersToGroups),
77
- }))
78
-
79
- // 그룹
80
- export const groups = pgTable('groups', {
81
- id: serial('id').primaryKey(),
82
- name: text('name').notNull(),
83
- })
84
-
85
- export const groupsRelations = relations(groups, ({ many }) => ({
86
- usersToGroups: many(usersToGroups),
87
- }))
88
-
89
- // 조인 테이블
90
- export const usersToGroups = pgTable('users_to_groups', {
91
- userId: integer('user_id').notNull().references(() => users.id),
92
- groupId: integer('group_id').notNull().references(() => groups.id),
93
- }, (table) => [
94
- primaryKey({ columns: [table.userId, table.groupId] }),
95
- ])
96
-
97
- export const usersToGroupsRelations = relations(usersToGroups, ({ one }) => ({
98
- user: one(users, {
99
- fields: [usersToGroups.userId],
100
- references: [users.id],
101
- }),
102
- group: one(groups, {
103
- fields: [usersToGroups.groupId],
104
- references: [groups.id],
105
- }),
106
- }))
107
- ```
108
-
109
- ## 관계 포함 조회
110
-
111
- ```typescript
112
- // 1:N 관계 포함
113
- const userWithPosts = await db.query.users.findFirst({
114
- where: eq(users.id, 1),
115
- with: {
116
- posts: true,
117
- },
118
- })
119
-
120
- // 중첩 관계
121
- const userWithAll = await db.query.users.findFirst({
122
- where: eq(users.id, 1),
123
- with: {
124
- posts: {
125
- with: {
126
- comments: true,
127
- },
128
- },
129
- },
130
- })
131
-
132
- // 관계 필터링 + 정렬
133
- const result = await db.query.users.findFirst({
134
- with: {
135
- posts: {
136
- where: eq(posts.published, true),
137
- orderBy: desc(posts.createdAt),
138
- limit: 5,
139
- },
140
- },
141
- })
142
-
143
- // M:N 관계
144
- const userWithGroups = await db.query.users.findFirst({
145
- where: eq(users.id, 1),
146
- with: {
147
- usersToGroups: {
148
- with: {
149
- group: true,
150
- },
151
- },
152
- },
153
- })
154
- ```
155
-
156
- ## SQL Join으로 조회
157
-
158
- ```typescript
159
- // Inner join
160
- const result = await db.select({
161
- userId: users.id,
162
- userName: users.name,
163
- postTitle: posts.title,
164
- })
165
- .from(users)
166
- .innerJoin(posts, eq(users.id, posts.authorId))
167
- .where(eq(users.id, 1))
168
-
169
- // Left join + count
170
- const result = await db.select({
171
- userId: users.id,
172
- userName: users.name,
173
- postCount: count(posts.id),
174
- })
175
- .from(users)
176
- .leftJoin(posts, eq(users.id, posts.authorId))
177
- .groupBy(users.id)
178
- ```
179
-
180
- ## 자기 참조 관계
181
-
182
- ```typescript
183
- import { type AnyPgColumn, pgTable, serial, text, integer } from 'drizzle-orm/pg-core'
184
-
185
- // 사용자 (초대 관계)
186
- export const users = pgTable('users', {
187
- id: serial('id').primaryKey(),
188
- name: text('name').notNull(),
189
- invitedBy: integer('invited_by').references((): AnyPgColumn => users.id),
190
- })
191
-
192
- export const usersRelations = relations(users, ({ one, many }) => ({
193
- inviter: one(users, {
194
- fields: [users.invitedBy],
195
- references: [users.id],
196
- relationName: 'inviter',
197
- }),
198
- invitees: many(users, {
199
- relationName: 'inviter',
200
- }),
201
- }))
202
- ```
@@ -1,154 +0,0 @@
1
- # Drizzle - 스키마 정의
2
-
3
- ## 필수 규칙
4
-
5
- 1. **파일별 분리** 구조 사용
6
- 2. **모든 요소에 한글 주석** (파일, 테이블, 컬럼)
7
-
8
- ## 구조
9
-
10
- ```
11
- src/db/schema/
12
- ├── index.ts # export all
13
- ├── user.ts # User 테이블
14
- ├── post.ts # Post 테이블
15
- └── enums.ts # Enum 정의
16
- ```
17
-
18
- ## 예시
19
-
20
- ### index.ts
21
-
22
- ```typescript
23
- // 모든 스키마 export
24
- export * from './user'
25
- export * from './post'
26
- export * from './enums'
27
- ```
28
-
29
- ### enums.ts
30
-
31
- ```typescript
32
- import { pgEnum } from 'drizzle-orm/pg-core'
33
-
34
- // 사용자 역할
35
- export const roleEnum = pgEnum('role', ['USER', 'ADMIN'])
36
- ```
37
-
38
- ### user.ts
39
-
40
- ```typescript
41
- import { pgTable, serial, text, boolean, timestamp } from 'drizzle-orm/pg-core'
42
- import { relations } from 'drizzle-orm'
43
- import { roleEnum } from './enums'
44
- import { posts } from './post'
45
-
46
- // 사용자 테이블
47
- export const users = pgTable('users', {
48
- id: serial('id').primaryKey(),
49
- email: text('email').notNull().unique(), // 로그인 이메일
50
- name: text('name'), // 표시 이름
51
- role: roleEnum('role').default('USER'), // 사용자 역할
52
- verified: boolean('verified').notNull().default(false),
53
- createdAt: timestamp('created_at').notNull().defaultNow(),
54
- updatedAt: timestamp('updated_at').notNull().defaultNow(),
55
- })
56
-
57
- // 사용자 관계
58
- export const usersRelations = relations(users, ({ many }) => ({
59
- posts: many(posts), // 작성 게시글 (1:N)
60
- }))
61
-
62
- // 타입 추론
63
- export type User = typeof users.$inferSelect
64
- export type NewUser = typeof users.$inferInsert
65
- ```
66
-
67
- ### post.ts
68
-
69
- ```typescript
70
- import { pgTable, serial, text, integer, boolean, timestamp } from 'drizzle-orm/pg-core'
71
- import { relations } from 'drizzle-orm'
72
- import { users } from './user'
73
-
74
- // 게시글 테이블
75
- export const posts = pgTable('posts', {
76
- id: serial('id').primaryKey(),
77
- title: text('title').notNull(), // 제목
78
- content: text('content'), // 본문
79
- published: boolean('published').notNull().default(false),
80
- authorId: integer('author_id').references(() => users.id), // 작성자
81
- createdAt: timestamp('created_at').notNull().defaultNow(),
82
- updatedAt: timestamp('updated_at').notNull().defaultNow(),
83
- })
84
-
85
- // 게시글 관계
86
- export const postsRelations = relations(posts, ({ one }) => ({
87
- author: one(users, {
88
- fields: [posts.authorId],
89
- references: [users.id],
90
- }),
91
- }))
92
-
93
- export type Post = typeof posts.$inferSelect
94
- export type NewPost = typeof posts.$inferInsert
95
- ```
96
-
97
- ## 컬럼 타입 (PostgreSQL)
98
-
99
- ```typescript
100
- import {
101
- serial, // auto-increment
102
- integer, // 정수
103
- bigint, // 큰 정수
104
- text, // 문자열
105
- varchar, // 길이 제한 문자열
106
- boolean, // 불린
107
- timestamp, // 날짜/시간
108
- date, // 날짜
109
- numeric, // 소수점
110
- json, // JSON
111
- jsonb, // JSONB
112
- uuid, // UUID
113
- } from 'drizzle-orm/pg-core'
114
- ```
115
-
116
- ## 인덱스
117
-
118
- ```typescript
119
- import { pgTable, serial, text, index, uniqueIndex } from 'drizzle-orm/pg-core'
120
-
121
- export const users = pgTable('users', {
122
- id: serial('id').primaryKey(),
123
- name: text('name'),
124
- email: text('email').notNull(),
125
- }, (table) => [
126
- index('name_idx').on(table.name),
127
- uniqueIndex('email_idx').on(table.email),
128
- ])
129
- ```
130
-
131
- ## 복합 키
132
-
133
- ```typescript
134
- import { pgTable, integer, primaryKey } from 'drizzle-orm/pg-core'
135
-
136
- export const usersToGroups = pgTable('users_to_groups', {
137
- userId: integer('user_id').notNull(),
138
- groupId: integer('group_id').notNull(),
139
- }, (table) => [
140
- primaryKey({ columns: [table.userId, table.groupId] }),
141
- ])
142
- ```
143
-
144
- ## 테이블 매핑
145
-
146
- ```typescript
147
- // snake_case 컬럼명 사용
148
- export const users = pgTable('users', {
149
- id: serial('id').primaryKey(),
150
- firstName: text('first_name'), // DB: first_name, JS: firstName
151
- lastName: text('last_name'),
152
- createdAt: timestamp('created_at').defaultNow(),
153
- })
154
- ```
@@ -1,96 +0,0 @@
1
- # Drizzle - 설치 및 설정
2
-
3
- ## 설치
4
-
5
- ### PostgreSQL
6
-
7
- ```bash
8
- npm install drizzle-orm pg
9
- npm install -D drizzle-kit @types/pg
10
- ```
11
-
12
- ### MySQL
13
-
14
- ```bash
15
- npm install drizzle-orm mysql2
16
- npm install -D drizzle-kit
17
- ```
18
-
19
- ### SQLite
20
-
21
- ```bash
22
- npm install drizzle-orm better-sqlite3
23
- npm install -D drizzle-kit @types/better-sqlite3
24
- ```
25
-
26
- ---
27
-
28
- ## 폴더 구조
29
-
30
- ```
31
- 프로젝트/
32
- ├── drizzle.config.ts
33
- ├── drizzle/
34
- │ └── migrations/
35
- ├── src/
36
- │ ├── db/
37
- │ │ └── schema/
38
- │ │ ├── index.ts # export all
39
- │ │ ├── user.ts
40
- │ │ └── post.ts
41
- │ └── lib/
42
- │ └── db.ts # Drizzle client
43
- ```
44
-
45
- ---
46
-
47
- ## Drizzle Client
48
-
49
- ```typescript
50
- // src/lib/db.ts
51
- import { drizzle } from 'drizzle-orm/node-postgres'
52
- import * as schema from '@/db/schema'
53
-
54
- export const db = drizzle(process.env.DATABASE_URL!, { schema })
55
- ```
56
-
57
- ---
58
-
59
- ## TanStack Start 연동
60
-
61
- ```typescript
62
- // app/services/user.ts
63
- import { createServerFn } from '@tanstack/react-start'
64
- import { db } from '@/lib/db'
65
- import { users } from '@/db/schema'
66
- import { eq } from 'drizzle-orm'
67
- import { z } from 'zod'
68
-
69
- export const getUsers = createServerFn({ method: 'GET' })
70
- .handler(async () => {
71
- return db.select().from(users)
72
- })
73
-
74
- export const getUserById = createServerFn({ method: 'GET' })
75
- .inputValidator(z.coerce.number())
76
- .handler(async ({ data: id }) => {
77
- const [user] = await db.select().from(users).where(eq(users.id, id))
78
- return user
79
- })
80
-
81
- export const createUser = createServerFn({ method: 'POST' })
82
- .inputValidator(z.object({ email: z.email(), name: z.string() }))
83
- .handler(async ({ data }) => {
84
- const [user] = await db.insert(users).values(data).returning()
85
- return user
86
- })
87
- ```
88
-
89
- ---
90
-
91
- ## 환경 변수
92
-
93
- ```bash
94
- # .env
95
- DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
96
- ```
@@ -1,127 +0,0 @@
1
- # Drizzle - 트랜잭션
2
-
3
- ## 기본 트랜잭션
4
-
5
- 하나라도 실패하면 모두 롤백.
6
-
7
- ```typescript
8
- import { db } from '@/lib/db'
9
- import { users, accounts } from '@/db/schema'
10
- import { eq, sql } from 'drizzle-orm'
11
-
12
- await db.transaction(async (tx) => {
13
- await tx.update(accounts)
14
- .set({ balance: sql`${accounts.balance} - 100` })
15
- .where(eq(accounts.userId, senderId))
16
-
17
- await tx.update(accounts)
18
- .set({ balance: sql`${accounts.balance} + 100` })
19
- .where(eq(accounts.userId, recipientId))
20
- })
21
- ```
22
-
23
- ## 반환값 사용
24
-
25
- ```typescript
26
- const newBalance = await db.transaction(async (tx) => {
27
- await tx.update(accounts)
28
- .set({ balance: sql`${accounts.balance} - 100` })
29
- .where(eq(accounts.userId, senderId))
30
-
31
- await tx.update(accounts)
32
- .set({ balance: sql`${accounts.balance} + 100` })
33
- .where(eq(accounts.userId, recipientId))
34
-
35
- const [account] = await tx.select({ balance: accounts.balance })
36
- .from(accounts)
37
- .where(eq(accounts.userId, senderId))
38
-
39
- return account.balance
40
- })
41
- ```
42
-
43
- ## 조건부 롤백
44
-
45
- ```typescript
46
- await db.transaction(async (tx) => {
47
- const [account] = await tx.select({ balance: accounts.balance })
48
- .from(accounts)
49
- .where(eq(accounts.userId, senderId))
50
-
51
- if (account.balance < 100) {
52
- tx.rollback() // 예외 발생, 트랜잭션 롤백
53
- }
54
-
55
- await tx.update(accounts)
56
- .set({ balance: sql`${accounts.balance} - 100` })
57
- .where(eq(accounts.userId, senderId))
58
-
59
- await tx.update(accounts)
60
- .set({ balance: sql`${accounts.balance} + 100` })
61
- .where(eq(accounts.userId, recipientId))
62
- })
63
- ```
64
-
65
- ## 에러 처리
66
-
67
- ```typescript
68
- try {
69
- await db.transaction(async (tx) => {
70
- const [user] = await tx.insert(users)
71
- .values({ email: 'user@example.com' })
72
- .returning()
73
-
74
- if (someCondition) {
75
- throw new Error('Rollback')
76
- }
77
-
78
- await tx.insert(posts)
79
- .values({ title: 'Post', authorId: user.id })
80
- })
81
- } catch (error) {
82
- // 전체 롤백됨
83
- console.error('Transaction failed:', error)
84
- }
85
- ```
86
-
87
- ## 중첩 트랜잭션 (PostgreSQL)
88
-
89
- ```typescript
90
- await db.transaction(async (tx) => {
91
- await tx.insert(users).values({ email: 'user1@example.com' })
92
-
93
- await tx.transaction(async (tx2) => {
94
- await tx2.insert(users).values({ email: 'user2@example.com' })
95
- // 여기서 에러 발생해도 외부 트랜잭션은 영향 없음 (savepoint)
96
- })
97
- })
98
- ```
99
-
100
- ## 복잡한 예시
101
-
102
- ```typescript
103
- import { db } from '@/lib/db'
104
- import { users, posts, comments } from '@/db/schema'
105
- import { eq } from 'drizzle-orm'
106
-
107
- const createUserWithPost = async (userData: NewUser, postData: NewPost) => {
108
- return db.transaction(async (tx) => {
109
- // 사용자 생성
110
- const [user] = await tx.insert(users).values(userData).returning()
111
-
112
- // 게시글 생성
113
- const [post] = await tx.insert(posts)
114
- .values({ ...postData, authorId: user.id })
115
- .returning()
116
-
117
- // 사용자 + 게시글 반환
118
- return { user, post }
119
- })
120
- }
121
-
122
- // 사용
123
- const result = await createUserWithPost(
124
- { email: 'user@example.com', name: 'John' },
125
- { title: 'First Post', content: 'Hello World' }
126
- )
127
- ```