jun-claude-code 0.6.2 → 0.6.4

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.
@@ -0,0 +1,329 @@
1
+ ---
2
+ name: Planning
3
+ description: 개발 계획 문서(plan.md, context.md, checklist.md) 작성 템플릿. DB/API/FE 설계와 의사결정 근거 포함.
4
+ keywords: [plan, 계획, DB설계, API구성, FE플로우, 의사결정, 차선책, planning, 마이그레이션]
5
+ user-invocable: true
6
+ context: fork
7
+ agent: task-planner
8
+ ---
9
+
10
+ # 개발 계획 문서 작성 스킬
11
+
12
+ > `.claude/plan/` 폴더에 작성하는 3개 문서의 템플릿과 작성 규칙을 정의한다.
13
+
14
+ ## Plan 문서 개요
15
+
16
+ | 문서 | 역할 | 핵심 내용 |
17
+ |------|------|----------|
18
+ | `plan.md` | 전체 설계 문서 | 목적, DB/API/FE 설계, 설계 결정, TaskList |
19
+ | `context.md` | 맥락 기록 | 사용자 요청 원문, 비즈니스/기술 배경, 탐색한 코드 |
20
+ | `checklist.md` | 실행 추적 | Phase별 체크리스트, Task별 세부 작업 |
21
+
22
+ ### 문서 생명주기
23
+
24
+ ```
25
+ draft → confirmed → in-progress → done
26
+ ```
27
+
28
+ | 상태 | 의미 | 전환 시점 |
29
+ |------|------|----------|
30
+ | `draft` | 초안 작성 중 | 문서 생성 시 |
31
+ | `confirmed` | 사용자 승인 완료 | 사용자 Confirm 후 |
32
+ | `in-progress` | 구현 진행 중 | Phase 3 시작 시 |
33
+ | `done` | 작업 완료 | 모든 Task 완료 시 |
34
+
35
+ > Stop Hook에서 `plan.md`와 `checklist.md`를 참조하여 진행 상황을 추적한다.
36
+
37
+ ---
38
+
39
+ <instructions>
40
+
41
+ ## plan.md 템플릿
42
+
43
+ > 전체 설계를 담는 핵심 문서. 목적, 설계 결정, TaskList를 한 곳에 정리한다.
44
+
45
+ ### Frontmatter
46
+
47
+ ```yaml
48
+ ---
49
+ name: [작업명]-plan
50
+ description: [작업명] 개발 계획
51
+ created: YYYY-MM-DD
52
+ status: draft # draft | confirmed | in-progress | done
53
+ ---
54
+ ```
55
+
56
+ ### 전체 구조
57
+
58
+ ```markdown
59
+ # [작업명] Plan
60
+
61
+ ## 목적 (Why)
62
+ - 목적: [이 작업을 왜 하는가]
63
+ - 문제: [어떤 문제를 해결하는가]
64
+ - 방법: [어떻게 해결할 것인가]
65
+
66
+ ## DB 수정 계획
67
+ ### 테이블 변경
68
+ | 테이블 | 변경 유형 | 컬럼 | 타입 | 설명 |
69
+ |--------|----------|------|------|------|
70
+ | users | ADD | role | varchar(20) | 사용자 역할 |
71
+
72
+ ### 마이그레이션 순서
73
+ 1. [첫 번째 마이그레이션]
74
+ 2. [두 번째 마이그레이션]
75
+
76
+ ### 설계 결정
77
+ - **선택:** [채택한 방안]
78
+ - **이유:** [왜 이 방안을 선택했는지]
79
+ - **차선책:** [고려했지만 선택하지 않은 방안]
80
+ - **차선책 미채택 이유:** [왜 차선책을 선택하지 않았는지]
81
+
82
+ ## API 구성
83
+ ### 엔드포인트 목록
84
+ | Method | Path | 설명 | Request | Response |
85
+ |--------|------|------|---------|----------|
86
+ | POST | /api/users | 사용자 생성 | CreateUserDto | UserResponse |
87
+
88
+ ### 각 API 상세
89
+ #### POST /api/users
90
+ - Request Body: ...
91
+ - Response: ...
92
+ - 에러 케이스: ...
93
+
94
+ ### 설계 결정
95
+ - **선택:** [채택한 방안]
96
+ - **이유:** [왜 이 방안을 선택했는지]
97
+ - **차선책:** [고려했지만 선택하지 않은 방안]
98
+ - **차선책 미채택 이유:** [왜 차선책을 선택하지 않았는지]
99
+
100
+ ## FE 페이지 Flow
101
+ ### 화면 목록
102
+ | 페이지 | 경로 | 설명 |
103
+ |--------|------|------|
104
+ | 사용자 목록 | /users | 사용자 리스트 조회 |
105
+
106
+ ### 페이지 흐름도
107
+ [목록] -> [상세] -> [수정]
108
+
109
+ [삭제 확인 모달]
110
+
111
+ ### 컴포넌트 구조
112
+ UserListPage
113
+ ├── UserTable
114
+ │ └── UserRow
115
+ ├── UserSearchBar
116
+ └── Pagination
117
+
118
+ ### 설계 결정
119
+ - **선택:** [채택한 방안]
120
+ - **이유:** [왜 이 방안을 선택했는지]
121
+ - **차선책:** [고려했지만 선택하지 않은 방안]
122
+ - **차선책 미채택 이유:** [왜 차선책을 선택하지 않았는지]
123
+
124
+ ## 설계 결정 요약
125
+ | 영역 | 결정 | 이유 | 차선책 |
126
+ |------|------|------|--------|
127
+
128
+ ## TaskList 요약
129
+ | Task | 설명 | 의존성 |
130
+ |------|------|--------|
131
+ ```
132
+
133
+ </instructions>
134
+
135
+ ---
136
+
137
+ <instructions>
138
+
139
+ ## context.md 템플릿
140
+
141
+ > 작업의 맥락을 기록하여 구현 중 목적 희석을 방지한다.
142
+
143
+ ### Frontmatter
144
+
145
+ ```yaml
146
+ ---
147
+ name: [작업명]-context
148
+ description: [작업명] 작업 맥락
149
+ created: YYYY-MM-DD
150
+ ---
151
+ ```
152
+
153
+ ### 전체 구조
154
+
155
+ ```markdown
156
+ # [작업명] Context
157
+
158
+ ## 사용자 요청 원문
159
+ > (원문 그대로 인용)
160
+
161
+ ## 비즈니스 배경
162
+ - 왜 이 작업이 필요한가
163
+ - 비즈니스 제약 조건
164
+
165
+ ## 기술적 배경
166
+ - 현재 아키텍처 상태
167
+ - 관련 기존 코드/모듈
168
+
169
+ ## 탐색한 코드
170
+ | 파일 | 관련 내용 |
171
+ |------|----------|
172
+
173
+ ## 결정 사항
174
+ | 결정 | 근거 | 일시 |
175
+ |------|------|------|
176
+ ```
177
+
178
+ </instructions>
179
+
180
+ ---
181
+
182
+ <instructions>
183
+
184
+ ## checklist.md 템플릿
185
+
186
+ > Phase별 진행 상황을 추적하고, Task별 세부 작업을 관리한다.
187
+
188
+ ### Frontmatter
189
+
190
+ ```yaml
191
+ ---
192
+ name: [작업명]-checklist
193
+ description: [작업명] 실행 체크리스트
194
+ created: YYYY-MM-DD
195
+ ---
196
+ ```
197
+
198
+ ### 전체 구조
199
+
200
+ ```markdown
201
+ # [작업명] Checklist
202
+
203
+ ## Phase 1: 계획
204
+ - [x] 목적 확인
205
+ - [x] Context 수집
206
+ - [x] Plan 문서 작성
207
+
208
+ ## Phase 2: 검증
209
+ - [ ] Code Flow 분석
210
+ - [ ] UserFlow 분석
211
+ - [ ] Breaking Change 확인
212
+
213
+ ## Phase 3: 구현
214
+ ### Task 1: [제목]
215
+ - [ ] 세부 작업 1
216
+ - [ ] 세부 작업 2
217
+ - [ ] 커밋
218
+
219
+ ### Task 2: [제목]
220
+ - [ ] 세부 작업 1
221
+ - [ ] 세부 작업 2
222
+ - [ ] 커밋
223
+
224
+ ## Phase 4: 리뷰
225
+ - [ ] 전체 요구사항 충족 확인
226
+ - [ ] 엣지케이스 점검
227
+ ```
228
+
229
+ </instructions>
230
+
231
+ ---
232
+
233
+ <rules>
234
+
235
+ ## 설계 결정(Decision) 작성 규칙
236
+
237
+ > 모든 설계 결정에는 "왜 이것을 선택했고, 왜 다른 것을 선택하지 않았는가"를 명시한다.
238
+
239
+ ### 필수 항목
240
+
241
+ | 항목 | 설명 | 필수 |
242
+ |------|------|------|
243
+ | 선택 (What) | 무엇을 채택했는가 | 필수 |
244
+ | 이유 (Why) | 왜 이것을 선택했는가 | 필수 |
245
+ | 차선책 (Alternative) | 어떤 대안을 검토했는가 | 필수 |
246
+ | 차선책 미채택 이유 (Why Not) | 왜 차선책을 선택하지 않았는가 | 필수 |
247
+ | 트레이드오프 (Tradeoff) | 현재 선택의 단점은 무엇인가 | 선택 |
248
+
249
+ ### 작성 형식
250
+
251
+ ```markdown
252
+ - **선택:** JSON 컬럼 대신 별도 테이블로 분리
253
+ - **이유:** 검색/인덱싱 성능과 스키마 변경 유연성 확보
254
+ - **차선책:** JSON 컬럼으로 저장
255
+ - **차선책 미채택 이유:** 복잡한 쿼리 시 성능 저하, 타입 안정성 부족
256
+ - **트레이드오프:** JOIN 비용 증가 (캐싱으로 완화 가능)
257
+ ```
258
+
259
+ ### 적용 범위
260
+
261
+ > DB, API, FE 모든 영역의 설계 결정에 이 형식을 동일하게 적용한다.
262
+
263
+ | 영역 | 설계 결정 예시 |
264
+ |------|--------------|
265
+ | DB | 테이블 구조, 인덱스 전략, 정규화 수준 |
266
+ | API | 엔드포인트 설계, 인증 방식, 에러 처리 전략 |
267
+ | FE | 상태 관리 방식, 컴포넌트 구조, 라우팅 전략 |
268
+
269
+ </rules>
270
+
271
+ ---
272
+
273
+ <rules>
274
+
275
+ ## 선택적 섹션 규칙
276
+
277
+ > 모든 프로젝트에 DB/API/FE가 모두 존재하는 것은 아니다. 해당 영역이 있는 섹션만 선택하여 작성한다.
278
+
279
+ ### 최소 필수 섹션
280
+
281
+ | 섹션 | 필수 여부 | 설명 |
282
+ |------|----------|------|
283
+ | 목적 (Why) | 필수 | 항상 포함 |
284
+ | 설계 결정 요약 | 필수 | 항상 포함 |
285
+ | TaskList 요약 | 필수 | 항상 포함 |
286
+ | DB 수정 계획 | 선택 | DB 변경이 있을 때만 |
287
+ | API 구성 | 선택 | API 변경이 있을 때만 |
288
+ | FE 페이지 Flow | 선택 | FE 변경이 있을 때만 |
289
+
290
+ ### 영역별 선택 기준
291
+
292
+ ```
293
+ DB 변경 있음 → DB 수정 계획 섹션 포함
294
+ API 변경 있음 → API 구성 섹션 포함
295
+ FE 변경 있음 → FE 페이지 Flow 섹션 포함
296
+ 해당 없음 → 섹션 생략
297
+ ```
298
+
299
+ </rules>
300
+
301
+ ---
302
+
303
+ <checklist>
304
+
305
+ ## Plan 문서 작성 시 검증
306
+
307
+ - [ ] plan.md에 목적(Why) 섹션이 있는가?
308
+ - [ ] 각 설계 결정에 이유와 차선책이 명시되어 있는가?
309
+ - [ ] context.md에 사용자 요청 원문이 인용되어 있는가?
310
+ - [ ] checklist.md에 Task별 세부 작업이 있는가?
311
+ - [ ] status 필드가 올바르게 설정되어 있는가?
312
+ - [ ] 선택적 섹션이 프로젝트에 맞게 포함/생략되어 있는가?
313
+ - [ ] 설계 결정에 트레이드오프가 기록되어 있는가? (선택 사항이지만 권장)
314
+
315
+ </checklist>
316
+
317
+ ---
318
+
319
+ <reference>
320
+
321
+ ## 관련 문서
322
+
323
+ | 문서 | 설명 |
324
+ |------|------|
325
+ | `CLAUDE.md` 워크플로우 | Plan 문서 생성 시점 (Step 1.5.5) |
326
+ | `.claude/skills/Documentation/SKILL.md` | 문서 작성 공통 규칙 |
327
+ | `.claude/skills/PromptStructuring/SKILL.md` | XML 태그 구조화 가이드 |
328
+
329
+ </reference>
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: PromptStructuring
3
- description: "Claude Code 프롬프트의 XML 태그 구조화, 긍정 표현 전환, 출력 최적화 가이드"
4
- keywords: [prompt, xml-tags, positive-phrasing, output-optimization, 프롬프트, 구조화, 긍정표현, XML, 출력최적화]
3
+ description: ".claude/skills/ 또는 .claude/agents/ 하위 파일을 생성·수정할 때 반드시 이 Skill을 읽고 규칙을 적용한다. XML 태그 구조화, 긍정 표현, 출력 최적화, Skills 2.0 frontmatter(context:fork, agent, hooks, skills preload) 스펙 포함."
4
+ keywords: [prompt, xml-tags, positive-phrasing, output-optimization, 프롬프트, 구조화, 긍정표현, XML, 출력최적화, frontmatter, SKILL.md, agent.md, skills-2.0, hooks, context-fork, skills-preload]
5
5
  user-invocable: false
6
6
  ---
7
7
 
8
8
  # 프롬프트 구조화 스킬
9
9
 
10
- 프롬프트의 형식과 표현 방식은 LLM 출력 품질에 직접적인 영향을 준다.
10
+ > Skill, Agent, Hook 파일 작성/수정 스킬의 규칙을 적용한다.
11
11
 
12
12
  ## 핵심 원칙
13
13
 
@@ -17,4 +17,14 @@ user-invocable: false
17
17
  | 2. 긍정 표현 | "~금지" 대신 "~한다"로 행동 직접 유도 | `positive-phrasing.md` |
18
18
  | 3. 흐름 기호 | 단계/변환/위임에 `->` 사용: 계획 -> 구현 -> 검증 | - |
19
19
  | 4. 출력 최적화 | 반복 제거, 테이블 > 산문, Hook 출력 150줄 이내 | `output-optimization.md` |
20
- | 5. Frontmatter | Skill/Agent YAML frontmatter 필드 선택과 작성 기준 | `skills-frontmatter.md` |
20
+ | 5. Skills 2.0 Frontmatter | Skill/Agent YAML frontmatter 필드, context:fork + agent 조합, hooks, skills preload | `skills-frontmatter.md` |
21
+
22
+ ## 적용 시점
23
+
24
+ | 상황 | 참조할 문서 |
25
+ |------|-----------|
26
+ | Skill SKILL.md 새로 작성 | `skills-frontmatter.md` (frontmatter) + `xml-tags.md` (구조) |
27
+ | Agent .md 새로 작성 | `skills-frontmatter.md` (frontmatter) + `xml-tags.md` (구조) |
28
+ | Hook 스크립트 출력 작성 | `output-optimization.md` (줄 수 제한) |
29
+ | 기존 프롬프트 개선 | `positive-phrasing.md` + `xml-tags.md` |
30
+ | Skill ↔ Agent 연결 설계 | `skills-frontmatter.md` (양방향 연결 패턴) |
@@ -17,6 +17,7 @@ SKILL.md와 Agent .md 파일의 YAML frontmatter 필드 가이드.
17
17
  |------|------|--------|------|
18
18
  | `name` | string | 디렉토리명 | 스킬 이름. `/slash-command`가 됨. 소문자+하이픈, 최대 64자 |
19
19
  | `description` | string | 첫 단락 | 용도 및 트리거 조건. Claude가 자동 호출 판단에 사용 |
20
+ | `keywords` | array | - | 검색/매칭용 키워드. 영문+한글 혼용 권장. 예: `[plan, 계획, DB설계]` |
20
21
  | `argument-hint` | string | - | 자동완성 시 인자 힌트. 예: `[PR-number]` |
21
22
  | `disable-model-invocation` | boolean | `false` | `true` -> Claude 자동 호출 차단. description이 컨텍스트에 로딩되지 않아 토큰 절약 |
22
23
  | `user-invocable` | boolean | `true` | `false` -> `/` 메뉴에서 숨김. Claude만 자동 호출 가능 |
@@ -71,6 +72,7 @@ argument-hint: "[PR-number]"
71
72
  |------|------|--------|------|
72
73
  | `name` | string | **필수** | Agent 이름. 소문자+하이픈 |
73
74
  | `description` | string | **필수** | 위임 판단 기준. 언제 이 Agent를 사용하는지 명시 |
75
+ | `keywords` | array | - | 검색/매칭용 키워드. 영문+한글 혼용 권장 |
74
76
  | `disallowedTools` | array | - | 명시적 거부 도구 목록 |
75
77
  | `model` | string | `inherit` | `sonnet`, `opus`, `haiku`, 모델 ID |
76
78
  | `permissionMode` | string | `default` | 권한 모드 |
@@ -108,6 +110,68 @@ skills: [Git]
108
110
 
109
111
  -> hook으로 "Skill을 먼저 읽으세요"라고 안내하는 간접 방식 대신, frontmatter로 자동 주입.
110
112
 
113
+ ## Skill ↔ Agent 연결 패턴
114
+
115
+ > Skill(무엇을 할지) + Agent(어떻게/누가 할지) + Context 격리(어디서 할지)를 조합한다.
116
+
117
+ ### 패턴 1: 참조 Skill (인라인 주입)
118
+
119
+ Agent가 Skill을 배경 지식으로 preload. 메인 컨텍스트에서 실행.
120
+
121
+ ```yaml
122
+ # Agent frontmatter
123
+ skills: [Coding, Backend] # Agent 시작 시 Skill 전체 내용 주입
124
+
125
+ # Skill frontmatter
126
+ user-invocable: false # 직접 호출 불가, Agent에 의해서만 사용
127
+ ```
128
+
129
+ 적용 예: Coding, Backend, React, Reporting
130
+
131
+ ### 패턴 2: 작업 Skill (격리 실행)
132
+
133
+ Skill 호출 시 fork된 Agent에서 독립 실행. 메인 컨텍스트와 격리.
134
+
135
+ ```yaml
136
+ # Skill frontmatter
137
+ context: fork # 격리된 subagent에서 실행
138
+ agent: task-planner # 사용할 agent 타입 지정
139
+ user-invocable: true # /slash-command로 호출 가능
140
+ ```
141
+
142
+ 적용 예: Planning, pr-review, deploy
143
+
144
+ ### 패턴 3: 양방향 연결
145
+
146
+ Skill과 Agent가 서로를 참조. 사용자 직접 호출과 Agent preload 모두 지원.
147
+
148
+ ```yaml
149
+ # Planning Skill
150
+ context: fork
151
+ agent: task-planner # /planning → fork된 task-planner에서 실행
152
+ user-invocable: true
153
+
154
+ # task-planner Agent
155
+ skills: [Planning, Reporting] # Agent 시작 시 Planning Skill preload
156
+ ```
157
+
158
+ ```
159
+ 사용자 /planning → fork(task-planner) → Planning 템플릿에 따라 plan 문서 생성
160
+ Main Agent → task-planner 호출 → Planning Skill 자동 preload → 동일 품질 보장
161
+ ```
162
+
163
+ ### 패턴 선택 기준
164
+
165
+ | 기준 | 참조 Skill | 작업 Skill | 양방향 |
166
+ |------|:-:|:-:|:-:|
167
+ | 규칙/가이드 제공 | O | - | O |
168
+ | 독립 실행 필요 | - | O | O |
169
+ | 사용자 직접 호출 | - | O | O |
170
+ | Agent preload | O | - | O |
171
+ | 컨텍스트 격리 | - | O | O |
172
+
173
+ ---
174
+
111
175
  ## Hooks in Skills/Agents
112
176
 
113
177
  Skill과 Agent의 frontmatter에 `hooks` 필드를 정의하면 해당 컴포넌트가 활성화된 동안에만 실행된다.
@@ -0,0 +1,189 @@
1
+ ---
2
+ name: TypeORM
3
+ description: TypeORM 사용 시 참조. find > queryBuilder > rawQuery 우선순위, 가독성 기반 선택 기준, Migration 생성 규칙 제공.
4
+ keywords: [TypeORM, find, queryBuilder, rawQuery, Repository, 쿼리, ORM, migration]
5
+ user-invocable: false
6
+ ---
7
+
8
+ # TypeORM 사용 규칙
9
+
10
+ <rules>
11
+
12
+ ## 쿼리 작성 우선순위
13
+
14
+ > **가독성을 최우선으로, find > QueryBuilder > Raw Query 순서로 사용한다.**
15
+
16
+ | 우선순위 | 방식 | 사용 조건 |
17
+ |---------|------|----------|
18
+ | 1 | **find 메서드** | 기본 CRUD, 단순 조건, relations |
19
+ | 2 | **QueryBuilder** | groupBy, 집계, 커스텀 JOIN — 가독성이 유지되는 경우 |
20
+ | 3 | **Raw Query** | QueryBuilder의 서브쿼리 등으로 가독성이 떨어지는 경우 |
21
+
22
+ ### 핵심 판단 기준: 가독성
23
+
24
+ QueryBuilder가 허용되는 케이스라도, 서브쿼리 중첩 등으로 **코드 가독성이 떨어지면 Raw Query를 사용한다.**
25
+
26
+ ## find 메서드 (우선순위 1)
27
+
28
+ ```typescript
29
+ // ✅ 기본 조회
30
+ const user = await this.userRepository.findOneBy({ id });
31
+ const users = await this.userRepository.find({
32
+ where: { status: 'active' },
33
+ relations: ['orders'],
34
+ order: { createdAt: 'DESC' },
35
+ take: 10,
36
+ });
37
+
38
+ // ✅ 조건 조합
39
+ const users = await this.userRepository.find({
40
+ where: [
41
+ { status: 'active', role: 'admin' },
42
+ { status: 'active', role: 'manager' },
43
+ ],
44
+ });
45
+ ```
46
+
47
+ ## QueryBuilder (우선순위 2)
48
+
49
+ **다음 경우에 QueryBuilder 사용 (가독성이 유지될 때):**
50
+
51
+ | 케이스 | 예시 |
52
+ |--------|------|
53
+ | **groupBy** | 집계 쿼리 |
54
+ | **getRawMany/getRawOne** | 원시 데이터 필요 |
55
+ | **커스텀 JOIN 조건** | ON 절 커스텀 |
56
+ | **단순 서브쿼리** | 가독성이 유지되는 서브쿼리 |
57
+
58
+ ## Raw Query (우선순위 3)
59
+
60
+ **QueryBuilder로 작성 시 가독성이 떨어지는 경우 Raw Query 사용:**
61
+
62
+ | 케이스 | 이유 |
63
+ |--------|------|
64
+ | **복잡한 서브쿼리 중첩** | QueryBuilder 체이닝이 읽기 어려움 |
65
+ | **복잡한 CTE (WITH 절)** | QueryBuilder로 표현 시 과도한 중첩 |
66
+ | **DB 특화 문법** | QueryBuilder가 지원하지 않는 문법 |
67
+
68
+ </rules>
69
+
70
+ <examples>
71
+
72
+ ### find vs QueryBuilder
73
+
74
+ <example type="bad">
75
+ ```typescript
76
+ // ❌ 불필요한 QueryBuilder 사용
77
+ const user = await this.userRepository
78
+ .createQueryBuilder('user')
79
+ .where('user.id = :id', { id })
80
+ .getOne();
81
+ ```
82
+ </example>
83
+ <example type="good">
84
+ ```typescript
85
+ // ✅ find로 대체
86
+ const user = await this.userRepository.findOneBy({ id });
87
+ ```
88
+ </example>
89
+
90
+ ### QueryBuilder 허용
91
+
92
+ <example type="good">
93
+ ```typescript
94
+ // ✅ QueryBuilder 허용: groupBy + getRawMany (가독성 유지)
95
+ const stats = await this.orderRepository
96
+ .createQueryBuilder('order')
97
+ .select('order.status', 'status')
98
+ .addSelect('COUNT(*)', 'count')
99
+ .addSelect('SUM(order.amount)', 'total')
100
+ .groupBy('order.status')
101
+ .getRawMany();
102
+ ```
103
+ </example>
104
+
105
+ ### QueryBuilder → Raw Query 전환
106
+
107
+ <example type="bad">
108
+ ```typescript
109
+ // ❌ 서브쿼리 중첩으로 가독성 저하
110
+ const result = await this.orderRepository
111
+ .createQueryBuilder('order')
112
+ .where((qb) => {
113
+ const subQuery = qb
114
+ .subQuery()
115
+ .select('oi.orderId')
116
+ .from(OrderItem, 'oi')
117
+ .where((qb2) => {
118
+ const subQuery2 = qb2
119
+ .subQuery()
120
+ .select('p.id')
121
+ .from(Product, 'p')
122
+ .where('p.category = :cat', { cat: 'electronics' })
123
+ .getQuery();
124
+ return 'oi.productId IN ' + subQuery2;
125
+ })
126
+ .getQuery();
127
+ return 'order.id IN ' + subQuery;
128
+ })
129
+ .getMany();
130
+ ```
131
+ </example>
132
+ <example type="good">
133
+ ```typescript
134
+ // ✅ Raw Query로 가독성 확보
135
+ const result = await this.orderRepository.query(
136
+ `SELECT o.*
137
+ FROM orders o
138
+ WHERE o.id IN (
139
+ SELECT oi.order_id
140
+ FROM order_items oi
141
+ WHERE oi.product_id IN (
142
+ SELECT p.id FROM products p WHERE p.category = $1
143
+ )
144
+ )`,
145
+ ['electronics'],
146
+ );
147
+ ```
148
+ </example>
149
+
150
+ </examples>
151
+
152
+ <rules>
153
+
154
+ ## Migration 생성 규칙
155
+
156
+ > **Migration 파일은 직접 생성하지 않고, `yarn migration:create` 명령어로 생성한 뒤 수정한다.**
157
+
158
+ ### 이유
159
+
160
+ 직접 파일을 생성하면 타임스탬프 충돌이 발생할 수 있다. CLI 명령어를 통해 생성해야 TypeORM이 타임스탬프를 자동 관리한다.
161
+
162
+ ### 생성 방법
163
+
164
+ ```bash
165
+ # package.json에 migration:create가 정의된 경우
166
+ yarn migration:create src/migrations/MigrationName
167
+
168
+ # 정의되지 않은 경우 직접 실행
169
+ yarn run typeorm migration:create src/migrations/MigrationName
170
+ ```
171
+
172
+ ### 작업 순서
173
+
174
+ 1. `yarn migration:create`로 빈 migration 파일 생성
175
+ 2. 생성된 파일의 `up()` / `down()` 메서드 작성
176
+ 3. `yarn migration:run`으로 적용 확인
177
+
178
+ </rules>
179
+
180
+ <checklist>
181
+
182
+ ## 체크리스트
183
+
184
+ - [ ] find 메서드를 우선 사용하는가?
185
+ - [ ] QueryBuilder는 find로 불가능한 경우에만 사용하는가?
186
+ - [ ] QueryBuilder 서브쿼리 중첩으로 가독성이 떨어지면 Raw Query로 전환했는가?
187
+ - [ ] Migration 파일을 `yarn migration:create`로 생성했는가? (직접 파일 생성 금지)
188
+
189
+ </checklist>