@kood/claude-code 0.2.0 → 0.2.2

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 (28) hide show
  1. package/dist/index.js +62 -18
  2. package/package.json +1 -1
  3. package/templates/.claude/agents/code-reviewer.md +31 -0
  4. package/templates/.claude/agents/debug-detective.md +37 -0
  5. package/templates/.claude/agents/refactor-advisor.md +44 -0
  6. package/templates/.claude/agents/test-writer.md +41 -0
  7. package/templates/.claude/skills/frontend-design/SKILL.md +310 -0
  8. package/templates/.claude/skills/frontend-design/references/animation-patterns.md +446 -0
  9. package/templates/.claude/skills/frontend-design/references/colors-2026.md +244 -0
  10. package/templates/.claude/skills/frontend-design/references/typography-2026.md +302 -0
  11. package/templates/.claude/skills/gemini-review/SKILL.md +1 -1
  12. package/templates/hono/docs/library/drizzle/cloudflare-d1.md +247 -0
  13. package/templates/hono/docs/library/drizzle/config.md +167 -0
  14. package/templates/hono/docs/library/drizzle/index.md +259 -0
  15. package/templates/tanstack-start/docs/library/drizzle/cloudflare-d1.md +146 -0
  16. package/templates/tanstack-start/docs/library/drizzle/config.md +118 -0
  17. package/templates/tanstack-start/docs/library/drizzle/crud.md +205 -0
  18. package/templates/tanstack-start/docs/library/drizzle/index.md +79 -0
  19. package/templates/tanstack-start/docs/library/drizzle/relations.md +202 -0
  20. package/templates/tanstack-start/docs/library/drizzle/schema.md +154 -0
  21. package/templates/tanstack-start/docs/library/drizzle/setup.md +95 -0
  22. package/templates/tanstack-start/docs/library/drizzle/transactions.md +127 -0
  23. package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +204 -0
  24. package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +195 -0
  25. package/templates/tanstack-start/docs/library/tanstack-router/index.md +150 -0
  26. package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +150 -0
  27. package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +203 -0
  28. package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +213 -0
@@ -0,0 +1,79 @@
1
+ # Drizzle ORM
2
+
3
+ > **Version**: 0.38.x | Node.js/TypeScript ORM
4
+
5
+ @setup.md
6
+ @config.md
7
+ @schema.md
8
+ @crud.md
9
+ @relations.md
10
+ @transactions.md
11
+ @cloudflare-d1.md
12
+
13
+ ---
14
+
15
+ ## Quick Reference
16
+
17
+ ```typescript
18
+ import { db } from '@/lib/db'
19
+ import { users } from '@/db/schema'
20
+ import { eq } from 'drizzle-orm'
21
+
22
+ // CRUD
23
+ const allUsers = await db.select().from(users)
24
+ const [user] = await db.insert(users).values({ email, name }).returning()
25
+ const [updated] = await db.update(users).set({ name }).where(eq(users.id, id)).returning()
26
+ await db.delete(users).where(eq(users.id, id))
27
+
28
+ // 관계 포함 (Relational Query)
29
+ const userWithPosts = await db.query.users.findFirst({
30
+ where: eq(users.id, id),
31
+ with: { posts: true },
32
+ })
33
+ ```
34
+
35
+ ### drizzle.config.ts
36
+
37
+ ```typescript
38
+ import { defineConfig } from 'drizzle-kit'
39
+
40
+ export default defineConfig({
41
+ dialect: 'postgresql',
42
+ schema: './src/db/schema/index.ts',
43
+ out: './drizzle/migrations',
44
+ dbCredentials: {
45
+ url: process.env.DATABASE_URL!,
46
+ },
47
+ })
48
+ ```
49
+
50
+ ### Claude Code 금지
51
+
52
+ | 금지 사항 |
53
+ |----------|
54
+ | drizzle-kit push 자동 실행 |
55
+ | drizzle-kit migrate 자동 실행 |
56
+ | drizzle-kit generate 자동 실행 |
57
+ | 스키마 임의 변경 |
58
+
59
+ ---
60
+
61
+ ## Drizzle Client 설정
62
+
63
+ ```typescript
64
+ // lib/db.ts
65
+ import { drizzle } from 'drizzle-orm/node-postgres'
66
+ import * as schema from '@/db/schema'
67
+
68
+ export const db = drizzle(process.env.DATABASE_URL!, { schema })
69
+ ```
70
+
71
+ ## CLI 명령어
72
+
73
+ ```bash
74
+ npx drizzle-kit generate # 마이그레이션 생성
75
+ npx drizzle-kit migrate # 마이그레이션 실행
76
+ npx drizzle-kit push # 스키마 동기화 (개발용)
77
+ npx drizzle-kit pull # DB에서 스키마 가져오기
78
+ npx drizzle-kit studio # GUI
79
+ ```
@@ -0,0 +1,202 @@
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
+ ```
@@ -0,0 +1,154 @@
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
+ ```
@@ -0,0 +1,95 @@
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
+
68
+ export const getUsers = createServerFn({ method: 'GET' })
69
+ .handler(async () => {
70
+ return db.select().from(users)
71
+ })
72
+
73
+ export const getUserById = createServerFn({ method: 'GET' })
74
+ .validator((id: number) => id)
75
+ .handler(async ({ data: id }) => {
76
+ const [user] = await db.select().from(users).where(eq(users.id, id))
77
+ return user
78
+ })
79
+
80
+ export const createUser = createServerFn({ method: 'POST' })
81
+ .validator((data: { email: string; name: string }) => data)
82
+ .handler(async ({ data }) => {
83
+ const [user] = await db.insert(users).values(data).returning()
84
+ return user
85
+ })
86
+ ```
87
+
88
+ ---
89
+
90
+ ## 환경 변수
91
+
92
+ ```bash
93
+ # .env
94
+ DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
95
+ ```
@@ -0,0 +1,127 @@
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
+ ```