muno-claude-plugin 1.2.0 → 1.4.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.
- package/package.json +1 -1
- package/templates/WORKFLOW.md +59 -213
- package/templates/agents/acceptance-test-generator.md +124 -119
- package/templates/agents/epic-story-reviewer.md +1 -1
- package/templates/agents/prd-reviewer.md +1 -1
- package/templates/agents/tc-reviewer.md +1 -1
- package/templates/agents/unit-test-generator.md +137 -62
- package/templates/agents/workflow-navigator.md +256 -0
- package/templates/skills/hld-generator/SKILL.md +1 -1
package/package.json
CHANGED
package/templates/WORKFLOW.md
CHANGED
|
@@ -2,129 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
AI 기반 개발 워크플로우 스킬 체계입니다.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
| 스킬 | 설명 | 입력 | 출력 |
|
|
8
|
-
|------|------|------|------|
|
|
9
|
-
| `/prd-generator` | PRD 생성 | 요구사항 | PRD 문서 |
|
|
10
|
-
| `/epic-story-generator` | Epic & Story 생성 | PRD | Epic, Story (AC 포함) |
|
|
11
|
-
| `/hld-generator` | High-Level Design | Story | 아키텍처 설계 |
|
|
12
|
-
| `/lld-generator` | Low-Level Design | Story, HLD | 상세 설계 (ERD, API) |
|
|
13
|
-
| `/task-generator` | Implementation Task | Story, LLD | 구현 태스크 |
|
|
14
|
-
| `/tc-generator` | Test Case 생성 | Story (AC) | TC 문서 |
|
|
5
|
+
> **Note**: 워크플로우 순서는 `workflow-navigator` agent가 상황에 맞게 제안합니다.
|
|
6
|
+
> 모든 단계가 항상 필요한 것은 아닙니다.
|
|
15
7
|
|
|
16
|
-
|
|
8
|
+
---
|
|
17
9
|
|
|
18
|
-
|
|
19
|
-
|----------|------|---------------|
|
|
20
|
-
| `tc-reviewer` | TC 품질 리뷰 및 보완 | TC 생성 **직후** |
|
|
21
|
-
| `acceptance-test-generator` | TC 기반 인수 테스트 생성 | TC 리뷰 **완료 후** (테스트 먼저) |
|
|
22
|
-
| `unit-test-generator` | TDD 기반 Unit Test 생성 | Task 구현 **시작 전** (테스트 먼저) |
|
|
23
|
-
| `task-tracker` | Epic/Story/Task 상태 추적 | Task 완료, TC 통과, 리뷰 승인 시 |
|
|
10
|
+
## 스킬 목록
|
|
24
11
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
> **Task Level (단위 테스트)**:
|
|
34
|
-
> 1. unit-test-generator가 Task 완료 조건 기반 Unit Test 생성 (Red)
|
|
35
|
-
> 2. 코딩 agent가 테스트 통과하도록 구현 (Green)
|
|
36
|
-
> 3. 리팩토링 (Refactor)
|
|
12
|
+
| 스킬 | 설명 | 주요 입력 |
|
|
13
|
+
|------|------|----------|
|
|
14
|
+
| `/prd-generator` | PRD 생성 | 요구사항 |
|
|
15
|
+
| `/epic-story-generator` | Epic & Story 생성 | PRD (선택) |
|
|
16
|
+
| `/hld-generator` | High-Level Design | PRD + Epic/Story |
|
|
17
|
+
| `/lld-generator` | Low-Level Design | HLD |
|
|
18
|
+
| `/task-generator` | Implementation Task | Story + LLD |
|
|
19
|
+
| `/tc-generator` | Test Case 생성 | Story AC |
|
|
37
20
|
|
|
38
21
|
---
|
|
39
22
|
|
|
40
|
-
##
|
|
23
|
+
## Subagent 목록
|
|
41
24
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
▼
|
|
54
|
-
[2. 백로그] /epic-story-generator
|
|
55
|
-
│
|
|
56
|
-
┌─────────────────┴─────────────────┐
|
|
57
|
-
▼ ▼
|
|
58
|
-
Epic Story
|
|
59
|
-
│ │
|
|
60
|
-
│ ┌──────────────┤
|
|
61
|
-
│ │ │
|
|
62
|
-
│ ▼ │
|
|
63
|
-
[3. 테스트 설계] │ /tc-generator │
|
|
64
|
-
│ │ │
|
|
65
|
-
│ ▼ │
|
|
66
|
-
│ TC 문서 │
|
|
67
|
-
│ │ │
|
|
68
|
-
│ ▼ │
|
|
69
|
-
│ tc-reviewer (subagent) │
|
|
70
|
-
│ │ │
|
|
71
|
-
│ ▼ │
|
|
72
|
-
│ TC 리뷰 및 보완 │
|
|
73
|
-
│ │ │
|
|
74
|
-
│ ▼ │
|
|
75
|
-
│ acceptance-test-generator │
|
|
76
|
-
│ (subagent) │
|
|
77
|
-
│ │ │
|
|
78
|
-
│ ▼ │
|
|
79
|
-
│ 인수 테스트 생성 (Red) │
|
|
80
|
-
│ │
|
|
81
|
-
▼ ▼
|
|
82
|
-
[4. 설계] /hld-generator /lld-generator
|
|
83
|
-
│ │
|
|
84
|
-
▼ ▼
|
|
85
|
-
HLD 문서 LLD 문서
|
|
86
|
-
│
|
|
87
|
-
▼
|
|
88
|
-
[5. 구현] /task-generator
|
|
89
|
-
│
|
|
90
|
-
▼
|
|
91
|
-
Implementation Tasks
|
|
92
|
-
│
|
|
93
|
-
┌───────────────────────────────────┤
|
|
94
|
-
▼ │
|
|
95
|
-
[5-1. TDD] unit-test-generator (subagent) │
|
|
96
|
-
│ │
|
|
97
|
-
▼ │
|
|
98
|
-
Unit Test 생성 (Red) │
|
|
99
|
-
│ │
|
|
100
|
-
▼ ▼
|
|
101
|
-
[5-2. 구현] 코드 구현 ◄──────────────────── Task 기반 구현
|
|
102
|
-
│
|
|
103
|
-
│ Unit Test 통과 (Green)
|
|
104
|
-
▼
|
|
105
|
-
[6. 인수 테스트] 인수 테스트 실행
|
|
106
|
-
│
|
|
107
|
-
▼
|
|
108
|
-
모든 TC passed 확인
|
|
109
|
-
│
|
|
110
|
-
▼
|
|
111
|
-
[7. 자동 추적] task-tracker (subagent)
|
|
112
|
-
│
|
|
113
|
-
┌──────────┴──────────┐
|
|
114
|
-
▼ ▼
|
|
115
|
-
Task 상태 자동 업데이트 Story/Epic 완료 체크
|
|
116
|
-
│ │
|
|
117
|
-
▼ ▼
|
|
118
|
-
todo → inprogress 모든 Task close
|
|
119
|
-
→ resolve → review + 모든 TC passed
|
|
120
|
-
→ close │
|
|
121
|
-
▼
|
|
122
|
-
Story close
|
|
123
|
-
```
|
|
25
|
+
| Subagent | 설명 | 호출 시점 |
|
|
26
|
+
|----------|------|----------|
|
|
27
|
+
| `workflow-navigator` | 다음 워크플로우 제안 | 작업 시작 시 |
|
|
28
|
+
| `prd-reviewer` | PRD 품질 리뷰 | PRD 생성 후 |
|
|
29
|
+
| `epic-story-reviewer` | Epic/Story 리뷰 | Epic/Story 생성 후 |
|
|
30
|
+
| `hld-reviewer` | HLD 아키텍처 리뷰 | HLD 생성 후 |
|
|
31
|
+
| `lld-reviewer` | LLD 상세 설계 리뷰 | LLD 생성 후 |
|
|
32
|
+
| `tc-reviewer` | TC 품질 리뷰 | TC 생성 후 |
|
|
33
|
+
| `acceptance-test-generator` | 인수 테스트 생성 | TC 리뷰 후 |
|
|
34
|
+
| `unit-test-generator` | Unit Test 생성 (TDD) | Task 구현 전 |
|
|
35
|
+
| `task-tracker` | 상태 추적 | Task 완료 시 |
|
|
124
36
|
|
|
125
37
|
---
|
|
126
38
|
|
|
127
|
-
## 복잡도별 워크플로우
|
|
39
|
+
## 복잡도별 권장 워크플로우
|
|
128
40
|
|
|
129
41
|
### L1~L2: 단순 작업
|
|
130
42
|
```
|
|
@@ -133,14 +45,28 @@ AI 기반 개발 워크플로우 스킬 체계입니다.
|
|
|
133
45
|
|
|
134
46
|
### L3: 중간 복잡도
|
|
135
47
|
```
|
|
136
|
-
요구사항 → Story → Task
|
|
48
|
+
요구사항 → Story → Task → 구현
|
|
49
|
+
└→ LLD (선택)
|
|
137
50
|
```
|
|
138
51
|
|
|
139
52
|
### L4~L5: 높은 복잡도
|
|
140
53
|
```
|
|
141
|
-
요구사항 → PRD → Epic/Story → HLD → LLD → Task → 구현
|
|
54
|
+
요구사항 → PRD → Epic/Story → HLD (선택) → LLD → Task → 구현
|
|
142
55
|
```
|
|
143
56
|
|
|
57
|
+
> 복잡도 판단은 `workflow-navigator`에게 문의하세요.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 건너뛰기 가능한 단계
|
|
62
|
+
|
|
63
|
+
| 단계 | 건너뛰기 조건 |
|
|
64
|
+
|------|-------------|
|
|
65
|
+
| PRD | 이해관계자 합의 불필요, 단순 기능 |
|
|
66
|
+
| Epic/Story | 사용자 가치 없음 (기술 부채, 리팩토링) |
|
|
67
|
+
| HLD | 아키텍처 변경 없음, 기존 패턴 내 |
|
|
68
|
+
| LLD | 서빙 전략/장애 대응 변경 없음 |
|
|
69
|
+
|
|
144
70
|
---
|
|
145
71
|
|
|
146
72
|
## 문서 저장 구조
|
|
@@ -148,113 +74,33 @@ AI 기반 개발 워크플로우 스킬 체계입니다.
|
|
|
148
74
|
```
|
|
149
75
|
project/
|
|
150
76
|
├── documents/
|
|
151
|
-
│ ├── prd/
|
|
152
|
-
│
|
|
153
|
-
│
|
|
154
|
-
│ │ └── feature-hld.md
|
|
155
|
-
│ └── lld/
|
|
156
|
-
│ └── feature-lld.md
|
|
77
|
+
│ ├── prd/ # PRD 문서
|
|
78
|
+
│ ├── hld/ # HLD 문서
|
|
79
|
+
│ └── lld/ # LLD 문서
|
|
157
80
|
│
|
|
158
|
-
├── backlogs/
|
|
81
|
+
├── backlogs/ # Epic/Story
|
|
159
82
|
│ └── feature-name/
|
|
160
83
|
│ ├── index.md
|
|
161
|
-
│
|
|
162
|
-
│ ├── STORY-001-xxx.md
|
|
163
|
-
│ └── STORY-002-xxx.md
|
|
84
|
+
│ └── STORY-xxx.md
|
|
164
85
|
│
|
|
165
|
-
├── .tasks/
|
|
166
|
-
│ └── STORY-
|
|
167
|
-
│
|
|
168
|
-
│ ├── TASK-001-entity.md
|
|
169
|
-
│ ├── TASK-002-repository.md
|
|
170
|
-
│ └── ...
|
|
86
|
+
├── .tasks/ # Task
|
|
87
|
+
│ └── STORY-xxx/
|
|
88
|
+
│ └── TASK-xxx.md
|
|
171
89
|
│
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
│ ├── TC-001-01-happy-path.md
|
|
176
|
-
│ └── TC-001-02-error-case.md
|
|
177
|
-
│
|
|
178
|
-
└── src/test/
|
|
179
|
-
└── ... (테스트 코드)
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
## 상태 관리
|
|
185
|
-
|
|
186
|
-
### 상태 라이프사이클
|
|
187
|
-
|
|
188
|
-
**공통 상태 흐름**: `todo → inprogress → resolve → review → close`
|
|
189
|
-
|
|
190
|
-
```
|
|
191
|
-
todo → inprogress → resolve → review → close
|
|
192
|
-
│ │ │ │ │
|
|
193
|
-
│ │ │ │ └─ 최종 완료
|
|
194
|
-
│ │ │ └─ 코드 리뷰 중
|
|
195
|
-
│ │ └─ 구현 완료, 리뷰 대기
|
|
196
|
-
│ └─ 작업 진행 중
|
|
197
|
-
└─ 대기 중 (백로그)
|
|
198
|
-
|
|
199
|
-
blocked ← 어느 단계에서든 차단 가능
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### 2-Level 상태 관리
|
|
203
|
-
|
|
204
|
-
| Level | 담당 | 위치 | 설명 |
|
|
205
|
-
|-------|------|------|------|
|
|
206
|
-
| 개별 문서 | 코딩 Agent | `TASK-*.md`, `TC-*.md` | 개별 작업 상태 직접 관리 |
|
|
207
|
-
| 중앙 문서 | task-tracker | `.status/current.yaml` | 전체 상태 집계 및 동기화 |
|
|
208
|
-
|
|
209
|
-
### TC 상태
|
|
210
|
-
|
|
211
|
-
- `ready` → `passed` / `failed`
|
|
212
|
-
- `blocked` (구현 미완료)
|
|
213
|
-
|
|
214
|
-
### 완료 조건
|
|
215
|
-
|
|
216
|
-
```
|
|
217
|
-
Story close = 모든 Task close + 모든 TC passed
|
|
218
|
-
Epic close = 모든 Story close
|
|
90
|
+
└── test-cases/ # Test Cases
|
|
91
|
+
└── STORY-xxx/
|
|
92
|
+
└── TC-xxx.md
|
|
219
93
|
```
|
|
220
94
|
|
|
221
95
|
---
|
|
222
96
|
|
|
223
|
-
##
|
|
224
|
-
|
|
225
|
-
```bash
|
|
226
|
-
# PRD 생성
|
|
227
|
-
/prd-generator
|
|
228
|
-
|
|
229
|
-
# Story 생성
|
|
230
|
-
/epic-story-generator
|
|
231
|
-
|
|
232
|
-
# 설계
|
|
233
|
-
/hld-generator
|
|
234
|
-
/lld-generator
|
|
235
|
-
|
|
236
|
-
# 구현 태스크
|
|
237
|
-
/task-generator
|
|
238
|
-
|
|
239
|
-
# 테스트
|
|
240
|
-
/tc-generator # TC 문서 생성
|
|
241
|
-
# → tc-reviewer 자동 호출 (TC 품질 리뷰)
|
|
242
|
-
# → acceptance-test-generator 자동 호출 (인수 테스트 생성)
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
> **Note**:
|
|
246
|
-
> - TC 생성 시 `tc-reviewer` → `acceptance-test-generator` 순서로 자동 호출
|
|
247
|
-
> - Unit Test는 `unit-test-generator` subagent가 Task 구현 전에 자동 생성 (TDD)
|
|
248
|
-
> - 상태 추적은 `task-tracker` subagent가 자동 수행
|
|
249
|
-
|
|
250
|
-
---
|
|
251
|
-
|
|
252
|
-
## 관련 페르소나
|
|
97
|
+
## 페르소나
|
|
253
98
|
|
|
254
|
-
| 페르소나 | 역할 | 주요
|
|
255
|
-
|
|
256
|
-
| `/po` | Product Owner | prd-generator
|
|
99
|
+
| 페르소나 | 역할 | 주요 스킬 |
|
|
100
|
+
|----------|------|----------|
|
|
101
|
+
| `/po` | Product Owner | prd-generator |
|
|
257
102
|
| `/sm` | Scrum Master | epic-story-generator |
|
|
258
103
|
| `/principal-engineer` | 아키텍처 | hld-generator |
|
|
259
|
-
| `/
|
|
260
|
-
| `/
|
|
104
|
+
| `/staff-engineer` | 상세 설계 | lld-generator |
|
|
105
|
+
| `/dev` | 개발자 | task-generator |
|
|
106
|
+
| `/qa` | QA 엔지니어 | tc-generator |
|
|
@@ -169,37 +169,33 @@ status: ready
|
|
|
169
169
|
|
|
170
170
|
## 출력: 인수 테스트 코드
|
|
171
171
|
|
|
172
|
-
###
|
|
172
|
+
### 프로젝트 언어 감지
|
|
173
|
+
|
|
174
|
+
**테스트 코드 생성 전 반드시 프로젝트의 기술 스택을 파악합니다:**
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
1. build.gradle.kts / build.gradle → Kotlin/Java + Spring
|
|
178
|
+
2. pom.xml → Java + Spring
|
|
179
|
+
3. package.json → TypeScript/JavaScript + Node.js
|
|
180
|
+
4. requirements.txt / pyproject.toml → Python
|
|
181
|
+
5. go.mod → Go
|
|
182
|
+
6. 기존 테스트 코드 패턴 확인 → 프로젝트 컨벤션 따르기
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 언어별 Integration Test 패턴
|
|
188
|
+
|
|
189
|
+
### Kotlin + Spring Boot
|
|
173
190
|
|
|
174
191
|
```kotlin
|
|
175
|
-
package com.example.integration.chat
|
|
176
|
-
|
|
177
|
-
import org.junit.jupiter.api.*
|
|
178
|
-
import org.springframework.beans.factory.annotation.Autowired
|
|
179
|
-
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
|
|
180
|
-
import org.springframework.boot.test.context.SpringBootTest
|
|
181
|
-
import org.springframework.http.MediaType
|
|
182
|
-
import org.springframework.test.web.servlet.MockMvc
|
|
183
|
-
import org.springframework.test.web.servlet.post
|
|
184
|
-
import org.springframework.transaction.annotation.Transactional
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* STORY-001: 채팅방 관리
|
|
188
|
-
*
|
|
189
|
-
* TC 기반 인수 테스트 - Story 완료 검증
|
|
190
|
-
* TC 생성 직후 작성 (Test First)
|
|
191
|
-
*/
|
|
192
192
|
@SpringBootTest
|
|
193
193
|
@AutoConfigureMockMvc
|
|
194
194
|
@Transactional
|
|
195
195
|
@DisplayName("STORY-001: 채팅방 관리 인수 테스트")
|
|
196
196
|
class ChatRoomAcceptanceTest {
|
|
197
197
|
|
|
198
|
-
@Autowired
|
|
199
|
-
lateinit var mockMvc: MockMvc
|
|
200
|
-
|
|
201
|
-
@Autowired
|
|
202
|
-
lateinit var chatRoomRepository: ChatRoomRepository
|
|
198
|
+
@Autowired lateinit var mockMvc: MockMvc
|
|
203
199
|
|
|
204
200
|
@Nested
|
|
205
201
|
@DisplayName("TC-001-01: 채팅방 생성 - Happy Path")
|
|
@@ -207,50 +203,10 @@ class ChatRoomAcceptanceTest {
|
|
|
207
203
|
|
|
208
204
|
@Test
|
|
209
205
|
@DisplayName("인증된 사용자가 유효한 제목으로 채팅방 생성 시 성공")
|
|
210
|
-
fun `POST api_chat-rooms - 201 Created
|
|
211
|
-
// Arrange (사전 조건)
|
|
212
|
-
val token = createTestUserToken(userId = 1L)
|
|
213
|
-
val requestBody = """
|
|
214
|
-
{
|
|
215
|
-
"title": "새 채팅방"
|
|
216
|
-
}
|
|
217
|
-
""".trimIndent()
|
|
218
|
-
|
|
219
|
-
// Act (실행 단계)
|
|
220
|
-
val result = mockMvc.post("/api/chat-rooms") {
|
|
221
|
-
contentType = MediaType.APPLICATION_JSON
|
|
222
|
-
content = requestBody
|
|
223
|
-
header("Authorization", "Bearer $token")
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Assert (기대 결과)
|
|
227
|
-
result.andExpect {
|
|
228
|
-
status { isCreated() }
|
|
229
|
-
jsonPath("$.chatRoomId") { exists() }
|
|
230
|
-
jsonPath("$.title") { value("새 채팅방") }
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// DB 검증
|
|
234
|
-
val chatRooms = chatRoomRepository.findAll()
|
|
235
|
-
assertThat(chatRooms).hasSize(1)
|
|
236
|
-
assertThat(chatRooms[0].title).isEqualTo("새 채팅방")
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
@Nested
|
|
241
|
-
@DisplayName("TC-001-02: 채팅방 생성 - 빈 제목 오류")
|
|
242
|
-
inner class TC_001_02_EmptyTitle {
|
|
243
|
-
|
|
244
|
-
@Test
|
|
245
|
-
@DisplayName("빈 제목으로 채팅방 생성 시 400 Bad Request")
|
|
246
|
-
fun `POST api_chat-rooms with empty title - 400 Bad Request`() {
|
|
206
|
+
fun `POST api_chat-rooms - 201 Created`() {
|
|
247
207
|
// Arrange
|
|
248
208
|
val token = createTestUserToken(userId = 1L)
|
|
249
|
-
val requestBody = """
|
|
250
|
-
{
|
|
251
|
-
"title": ""
|
|
252
|
-
}
|
|
253
|
-
""".trimIndent()
|
|
209
|
+
val requestBody = """{"title": "새 채팅방"}"""
|
|
254
210
|
|
|
255
211
|
// Act & Assert
|
|
256
212
|
mockMvc.post("/api/chat-rooms") {
|
|
@@ -258,86 +214,135 @@ class ChatRoomAcceptanceTest {
|
|
|
258
214
|
content = requestBody
|
|
259
215
|
header("Authorization", "Bearer $token")
|
|
260
216
|
}.andExpect {
|
|
261
|
-
status {
|
|
262
|
-
jsonPath("$.
|
|
217
|
+
status { isCreated() }
|
|
218
|
+
jsonPath("$.chatRoomId") { exists() }
|
|
263
219
|
}
|
|
264
220
|
}
|
|
265
221
|
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Java + Spring Boot
|
|
226
|
+
|
|
227
|
+
```java
|
|
228
|
+
@SpringBootTest
|
|
229
|
+
@AutoConfigureMockMvc
|
|
230
|
+
@Transactional
|
|
231
|
+
@DisplayName("STORY-001: 채팅방 관리 인수 테스트")
|
|
232
|
+
class ChatRoomAcceptanceTest {
|
|
233
|
+
|
|
234
|
+
@Autowired
|
|
235
|
+
private MockMvc mockMvc;
|
|
266
236
|
|
|
267
237
|
@Nested
|
|
268
|
-
@DisplayName("TC-001-
|
|
269
|
-
|
|
238
|
+
@DisplayName("TC-001-01: 채팅방 생성 - Happy Path")
|
|
239
|
+
class TC_001_01_CreateChatRoom {
|
|
270
240
|
|
|
271
241
|
@Test
|
|
272
|
-
@DisplayName("
|
|
273
|
-
|
|
242
|
+
@DisplayName("인증된 사용자가 유효한 제목으로 채팅방 생성 시 성공")
|
|
243
|
+
void createChatRoom_withValidTitle_returns201() throws Exception {
|
|
274
244
|
// Arrange
|
|
275
|
-
|
|
276
|
-
{
|
|
277
|
-
"title": "새 채팅방"
|
|
278
|
-
}
|
|
279
|
-
""".trimIndent()
|
|
245
|
+
String requestBody = """{"title": "새 채팅방"}""";
|
|
280
246
|
|
|
281
247
|
// Act & Assert
|
|
282
|
-
mockMvc.post("/api/chat-rooms")
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
status
|
|
287
|
-
|
|
248
|
+
mockMvc.perform(post("/api/chat-rooms")
|
|
249
|
+
.contentType(MediaType.APPLICATION_JSON)
|
|
250
|
+
.content(requestBody)
|
|
251
|
+
.header("Authorization", "Bearer " + testToken))
|
|
252
|
+
.andExpect(status().isCreated())
|
|
253
|
+
.andExpect(jsonPath("$.chatRoomId").exists());
|
|
288
254
|
}
|
|
289
255
|
}
|
|
290
|
-
|
|
291
|
-
private fun createTestUserToken(userId: Long): String {
|
|
292
|
-
// 테스트용 JWT 토큰 생성
|
|
293
|
-
return "test-token-for-user-$userId"
|
|
294
|
-
}
|
|
295
256
|
}
|
|
296
257
|
```
|
|
297
258
|
|
|
298
|
-
|
|
259
|
+
### TypeScript + Jest + Supertest
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
describe('STORY-001: 채팅방 관리 인수 테스트', () => {
|
|
263
|
+
describe('TC-001-01: 채팅방 생성 - Happy Path', () => {
|
|
264
|
+
it('인증된 사용자가 유효한 제목으로 채팅방 생성 시 성공', async () => {
|
|
265
|
+
// Arrange
|
|
266
|
+
const token = await createTestUserToken(1);
|
|
267
|
+
const requestBody = { title: '새 채팅방' };
|
|
268
|
+
|
|
269
|
+
// Act
|
|
270
|
+
const response = await request(app)
|
|
271
|
+
.post('/api/chat-rooms')
|
|
272
|
+
.set('Authorization', `Bearer ${token}`)
|
|
273
|
+
.send(requestBody);
|
|
274
|
+
|
|
275
|
+
// Assert
|
|
276
|
+
expect(response.status).toBe(201);
|
|
277
|
+
expect(response.body.chatRoomId).toBeDefined();
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
```
|
|
299
282
|
|
|
300
|
-
|
|
283
|
+
### Python + pytest + FastAPI/Django
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
import pytest
|
|
287
|
+
from httpx import AsyncClient
|
|
288
|
+
|
|
289
|
+
class TestChatRoomAcceptance:
|
|
290
|
+
"""STORY-001: 채팅방 관리 인수 테스트"""
|
|
291
|
+
|
|
292
|
+
class TestTC_001_01_CreateChatRoom:
|
|
293
|
+
"""TC-001-01: 채팅방 생성 - Happy Path"""
|
|
294
|
+
|
|
295
|
+
@pytest.mark.asyncio
|
|
296
|
+
async def test_create_chatroom_with_valid_title_returns_201(
|
|
297
|
+
self, client: AsyncClient, auth_token: str
|
|
298
|
+
):
|
|
299
|
+
response = await client.post(
|
|
300
|
+
"/api/chat-rooms",
|
|
301
|
+
json={"title": "새 채팅방"},
|
|
302
|
+
headers={"Authorization": f"Bearer {auth_token}"}
|
|
303
|
+
)
|
|
304
|
+
assert response.status_code == 201
|
|
305
|
+
assert "chatRoomId" in response.json()
|
|
306
|
+
```
|
|
301
307
|
|
|
302
|
-
###
|
|
308
|
+
### Go + testing + httptest
|
|
303
309
|
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
+
```go
|
|
311
|
+
func TestChatRoomAcceptance(t *testing.T) {
|
|
312
|
+
t.Run("TC-001-01: 채팅방 생성 - Happy Path", func(t *testing.T) {
|
|
313
|
+
// Arrange
|
|
314
|
+
token := createTestUserToken(1)
|
|
315
|
+
body := `{"title": "새 채팅방"}`
|
|
310
316
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
inner class TC_XXX_XX { ... }
|
|
314
|
-
}
|
|
315
|
-
```
|
|
317
|
+
req := httptest.NewRequest("POST", "/api/chat-rooms", strings.NewReader(body))
|
|
318
|
+
req.Header.Set("Authorization", "Bearer "+token)
|
|
316
319
|
|
|
317
|
-
|
|
320
|
+
// Act
|
|
321
|
+
rec := httptest.NewRecorder()
|
|
322
|
+
router.ServeHTTP(rec, req)
|
|
318
323
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
class XxxE2ETest {
|
|
323
|
-
@Autowired lateinit var restTemplate: TestRestTemplate
|
|
324
|
-
|
|
325
|
-
@Test
|
|
326
|
-
@Order(1)
|
|
327
|
-
fun `Step 1: 사용자 생성`() { ... }
|
|
328
|
-
|
|
329
|
-
@Test
|
|
330
|
-
@Order(2)
|
|
331
|
-
fun `Step 2: 로그인`() { ... }
|
|
332
|
-
|
|
333
|
-
@Test
|
|
334
|
-
@Order(3)
|
|
335
|
-
fun `Step 3: 채팅방 생성`() { ... }
|
|
324
|
+
// Assert
|
|
325
|
+
assert.Equal(t, http.StatusCreated, rec.Code)
|
|
326
|
+
})
|
|
336
327
|
}
|
|
337
328
|
```
|
|
338
329
|
|
|
339
330
|
---
|
|
340
331
|
|
|
332
|
+
## 테스트 유형별 패턴 (언어 공통)
|
|
333
|
+
|
|
334
|
+
### Integration Test (API 레벨)
|
|
335
|
+
- HTTP 요청/응답 검증
|
|
336
|
+
- DB 상태 검증
|
|
337
|
+
- 트랜잭션 롤백으로 격리
|
|
338
|
+
|
|
339
|
+
### E2E Test (전체 흐름)
|
|
340
|
+
- 실제 서버 구동 (랜덤 포트)
|
|
341
|
+
- 순차적 시나리오 실행
|
|
342
|
+
- 외부 의존성 Mock 또는 실제 연동
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
341
346
|
## 생성 워크플로우
|
|
342
347
|
|
|
343
348
|
### Step 1: TC 분석
|
|
@@ -153,21 +153,26 @@ status: todo
|
|
|
153
153
|
|
|
154
154
|
## 출력: Unit Test 코드
|
|
155
155
|
|
|
156
|
-
###
|
|
156
|
+
### 프로젝트 언어 감지
|
|
157
|
+
|
|
158
|
+
**테스트 코드 생성 전 반드시 프로젝트의 기술 스택을 파악합니다:**
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
1. build.gradle.kts / build.gradle → Kotlin/Java + Spring
|
|
162
|
+
2. pom.xml → Java + Spring
|
|
163
|
+
3. package.json → TypeScript/JavaScript + Node.js
|
|
164
|
+
4. requirements.txt / pyproject.toml → Python
|
|
165
|
+
5. go.mod → Go
|
|
166
|
+
6. 기존 테스트 코드 패턴 확인 → 프로젝트 컨벤션 따르기
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 언어별 Unit Test 패턴
|
|
172
|
+
|
|
173
|
+
### Kotlin + JUnit5
|
|
157
174
|
|
|
158
175
|
```kotlin
|
|
159
|
-
package com.example.domain.chat.model
|
|
160
|
-
|
|
161
|
-
import org.junit.jupiter.api.*
|
|
162
|
-
import org.assertj.core.api.Assertions.assertThat
|
|
163
|
-
import org.assertj.core.api.Assertions.assertThatThrownBy
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* TASK-001: ChatRoom Entity 생성
|
|
167
|
-
*
|
|
168
|
-
* TDD 기반 Unit Test - 구현 전 작성
|
|
169
|
-
* Task 완료 조건을 검증하는 테스트
|
|
170
|
-
*/
|
|
171
176
|
@DisplayName("ChatRoom Entity 테스트")
|
|
172
177
|
class ChatRoomTest {
|
|
173
178
|
|
|
@@ -176,8 +181,8 @@ class ChatRoomTest {
|
|
|
176
181
|
inner class Creation {
|
|
177
182
|
|
|
178
183
|
@Test
|
|
179
|
-
@DisplayName("유효한 값으로 Entity 생성 시
|
|
180
|
-
fun `유효한 userId와 title로 ChatRoom 생성 시
|
|
184
|
+
@DisplayName("유효한 값으로 Entity 생성 시 성공")
|
|
185
|
+
fun `유효한 userId와 title로 ChatRoom 생성 시 성공`() {
|
|
181
186
|
// Arrange
|
|
182
187
|
val userId = 1L
|
|
183
188
|
val title = "테스트 채팅방"
|
|
@@ -187,80 +192,150 @@ class ChatRoomTest {
|
|
|
187
192
|
|
|
188
193
|
// Assert
|
|
189
194
|
assertThat(chatRoom.externalId).isNotNull()
|
|
190
|
-
assertThat(chatRoom.userId).isEqualTo(userId)
|
|
191
195
|
assertThat(chatRoom.title).isEqualTo(title)
|
|
192
|
-
assertThat(chatRoom.status).isEqualTo(ChatRoomStatus.ACTIVE)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
@Test
|
|
196
|
-
@DisplayName("externalId는 UUIDv7 형식이다")
|
|
197
|
-
fun `externalId는 UUIDv7 형식으로 생성된다`() {
|
|
198
|
-
// Arrange & Act
|
|
199
|
-
val chatRoom = ChatRoom(userId = 1L, title = "테스트")
|
|
200
|
-
|
|
201
|
-
// Assert
|
|
202
|
-
assertThat(chatRoom.externalId).isNotNull()
|
|
203
|
-
assertThat(chatRoom.externalId.toString()).matches(
|
|
204
|
-
"^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
|
205
|
-
)
|
|
206
196
|
}
|
|
207
197
|
}
|
|
208
198
|
|
|
209
199
|
@Nested
|
|
210
|
-
@DisplayName("
|
|
211
|
-
inner class
|
|
200
|
+
@DisplayName("유효성 검증")
|
|
201
|
+
inner class Validation {
|
|
212
202
|
|
|
213
203
|
@Test
|
|
214
204
|
@DisplayName("빈 제목으로 생성 시 예외 발생")
|
|
215
|
-
fun `빈 title로 ChatRoom 생성 시
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
ChatRoom(userId = 1L, title = "")
|
|
219
|
-
}.isInstanceOf(IllegalArgumentException::class.java)
|
|
220
|
-
.hasMessageContaining("title")
|
|
205
|
+
fun `빈 title로 ChatRoom 생성 시 예외`() {
|
|
206
|
+
assertThatThrownBy { ChatRoom(userId = 1L, title = "") }
|
|
207
|
+
.isInstanceOf(IllegalArgumentException::class.java)
|
|
221
208
|
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
222
212
|
|
|
223
|
-
|
|
224
|
-
@DisplayName("100자 초과 제목으로 생성 시 예외 발생")
|
|
225
|
-
fun `100자 초과 title로 ChatRoom 생성 시 IllegalArgumentException 발생`() {
|
|
226
|
-
// Arrange
|
|
227
|
-
val longTitle = "가".repeat(101)
|
|
213
|
+
### Java + JUnit5
|
|
228
214
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
215
|
+
```java
|
|
216
|
+
@DisplayName("ChatRoom Entity 테스트")
|
|
217
|
+
class ChatRoomTest {
|
|
218
|
+
|
|
219
|
+
@Nested
|
|
220
|
+
@DisplayName("Entity 생성")
|
|
221
|
+
class Creation {
|
|
235
222
|
|
|
236
223
|
@Test
|
|
237
|
-
@DisplayName("
|
|
238
|
-
|
|
224
|
+
@DisplayName("유효한 값으로 Entity 생성 시 성공")
|
|
225
|
+
void createChatRoom_withValidInput_succeeds() {
|
|
239
226
|
// Arrange
|
|
240
|
-
|
|
227
|
+
Long userId = 1L;
|
|
228
|
+
String title = "테스트 채팅방";
|
|
241
229
|
|
|
242
230
|
// Act
|
|
243
|
-
|
|
231
|
+
ChatRoom chatRoom = new ChatRoom(userId, title);
|
|
244
232
|
|
|
245
233
|
// Assert
|
|
246
|
-
assertThat(chatRoom.
|
|
234
|
+
assertThat(chatRoom.getExternalId()).isNotNull();
|
|
235
|
+
assertThat(chatRoom.getTitle()).isEqualTo(title);
|
|
247
236
|
}
|
|
248
237
|
}
|
|
249
238
|
|
|
250
239
|
@Nested
|
|
251
|
-
@DisplayName("
|
|
252
|
-
|
|
240
|
+
@DisplayName("유효성 검증")
|
|
241
|
+
class Validation {
|
|
253
242
|
|
|
254
243
|
@Test
|
|
255
|
-
@DisplayName("생성 시
|
|
256
|
-
|
|
244
|
+
@DisplayName("빈 제목으로 생성 시 예외 발생")
|
|
245
|
+
void createChatRoom_withEmptyTitle_throwsException() {
|
|
246
|
+
assertThatThrownBy(() -> new ChatRoom(1L, ""))
|
|
247
|
+
.isInstanceOf(IllegalArgumentException.class);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### TypeScript + Jest
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
describe('ChatRoom Entity', () => {
|
|
257
|
+
describe('Entity 생성', () => {
|
|
258
|
+
it('유효한 값으로 Entity 생성 시 성공', () => {
|
|
259
|
+
// Arrange
|
|
260
|
+
const userId = 1;
|
|
261
|
+
const title = '테스트 채팅방';
|
|
262
|
+
|
|
263
|
+
// Act
|
|
264
|
+
const chatRoom = new ChatRoom({ userId, title });
|
|
265
|
+
|
|
266
|
+
// Assert
|
|
267
|
+
expect(chatRoom.externalId).toBeDefined();
|
|
268
|
+
expect(chatRoom.title).toBe(title);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe('유효성 검증', () => {
|
|
273
|
+
it('빈 제목으로 생성 시 예외 발생', () => {
|
|
274
|
+
expect(() => new ChatRoom({ userId: 1, title: '' }))
|
|
275
|
+
.toThrow('title');
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Python + pytest
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
import pytest
|
|
285
|
+
from domain.chat.model import ChatRoom
|
|
286
|
+
|
|
287
|
+
class TestChatRoom:
|
|
288
|
+
"""ChatRoom Entity 테스트"""
|
|
289
|
+
|
|
290
|
+
class TestCreation:
|
|
291
|
+
"""Entity 생성"""
|
|
292
|
+
|
|
293
|
+
def test_create_chatroom_with_valid_input_succeeds(self):
|
|
294
|
+
# Arrange
|
|
295
|
+
user_id = 1
|
|
296
|
+
title = "테스트 채팅방"
|
|
297
|
+
|
|
298
|
+
# Act
|
|
299
|
+
chat_room = ChatRoom(user_id=user_id, title=title)
|
|
300
|
+
|
|
301
|
+
# Assert
|
|
302
|
+
assert chat_room.external_id is not None
|
|
303
|
+
assert chat_room.title == title
|
|
304
|
+
|
|
305
|
+
class TestValidation:
|
|
306
|
+
"""유효성 검증"""
|
|
307
|
+
|
|
308
|
+
def test_create_chatroom_with_empty_title_raises_error(self):
|
|
309
|
+
with pytest.raises(ValueError, match="title"):
|
|
310
|
+
ChatRoom(user_id=1, title="")
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Go + testing
|
|
314
|
+
|
|
315
|
+
```go
|
|
316
|
+
func TestChatRoom(t *testing.T) {
|
|
317
|
+
t.Run("Entity 생성", func(t *testing.T) {
|
|
318
|
+
t.Run("유효한 값으로 Entity 생성 시 성공", func(t *testing.T) {
|
|
319
|
+
// Arrange
|
|
320
|
+
userID := int64(1)
|
|
321
|
+
title := "테스트 채팅방"
|
|
322
|
+
|
|
257
323
|
// Act
|
|
258
|
-
|
|
324
|
+
chatRoom, err := NewChatRoom(userID, title)
|
|
259
325
|
|
|
260
326
|
// Assert
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
327
|
+
assert.NoError(t, err)
|
|
328
|
+
assert.NotEmpty(t, chatRoom.ExternalID)
|
|
329
|
+
assert.Equal(t, title, chatRoom.Title)
|
|
330
|
+
})
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
t.Run("유효성 검증", func(t *testing.T) {
|
|
334
|
+
t.Run("빈 제목으로 생성 시 에러", func(t *testing.T) {
|
|
335
|
+
_, err := NewChatRoom(1, "")
|
|
336
|
+
assert.Error(t, err)
|
|
337
|
+
})
|
|
338
|
+
})
|
|
264
339
|
}
|
|
265
340
|
```
|
|
266
341
|
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workflow-navigator
|
|
3
|
+
description: |
|
|
4
|
+
현재 상황을 분석하고 다음 워크플로우를 제안합니다.
|
|
5
|
+
복잡도에 따라 불필요한 단계는 건너뛰고, 적절한 다음 스텝을 안내합니다.
|
|
6
|
+
"다음에 뭐 해야 해?", "워크플로우 안내", "다음 단계" 등의 요청에 자동 호출됩니다.
|
|
7
|
+
model: haiku
|
|
8
|
+
color: cyan
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Workflow Navigator
|
|
12
|
+
|
|
13
|
+
## 페르소나
|
|
14
|
+
|
|
15
|
+
@.claude/personas/sm.md
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 역할
|
|
20
|
+
|
|
21
|
+
현재 프로젝트 상황을 분석하여 **다음 워크플로우를 제안**합니다.
|
|
22
|
+
- 정적인 워크플로우를 강제하지 않음
|
|
23
|
+
- 복잡도와 상황에 따라 유연하게 판단
|
|
24
|
+
- 불필요한 단계는 건너뛰기 제안
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 복잡도 레벨 정의
|
|
29
|
+
|
|
30
|
+
| Level | 설명 | 예시 |
|
|
31
|
+
|-------|------|------|
|
|
32
|
+
| **L1** | 단순 수정, 버그 픽스 | 오타 수정, 간단한 버그 |
|
|
33
|
+
| **L2** | 단일 컴포넌트 변경 | 필드 추가, 단일 API 수정 |
|
|
34
|
+
| **L3** | 복수 컴포넌트 연동 | 새 기능 (1-2 Story) |
|
|
35
|
+
| **L4** | 시스템 간 연동 | 복잡한 기능 (여러 Story) |
|
|
36
|
+
| **L5** | 아키텍처 변경 | 신규 시스템, 대규모 리팩토링 |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 레벨별 권장 워크플로우
|
|
41
|
+
|
|
42
|
+
### L1~L2: 단순 작업
|
|
43
|
+
```
|
|
44
|
+
요구사항 → 바로 구현
|
|
45
|
+
```
|
|
46
|
+
- PRD 불필요
|
|
47
|
+
- Story 불필요
|
|
48
|
+
- 설계 문서 불필요
|
|
49
|
+
|
|
50
|
+
### L3: 중간 복잡도
|
|
51
|
+
```
|
|
52
|
+
요구사항 → Story (AC) → LLD (선택) → Task → 구현
|
|
53
|
+
```
|
|
54
|
+
- PRD 선택적 (간단한 배경 설명)
|
|
55
|
+
- HLD 불필요
|
|
56
|
+
- LLD는 기술적 복잡도에 따라 선택
|
|
57
|
+
|
|
58
|
+
### L4: 높은 복잡도
|
|
59
|
+
```
|
|
60
|
+
요구사항 → PRD → Epic/Story → HLD (선택) → LLD → Task → 구현
|
|
61
|
+
```
|
|
62
|
+
- PRD 필수 (이해관계자 합의)
|
|
63
|
+
- HLD는 아키텍처 변경 시에만
|
|
64
|
+
|
|
65
|
+
### L5: 아키텍처 변경
|
|
66
|
+
```
|
|
67
|
+
요구사항 → PRD → Epic/Story → HLD → LLD → Task → 구현
|
|
68
|
+
```
|
|
69
|
+
- 모든 단계 필수
|
|
70
|
+
- 아키텍처 리뷰 필수
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 건너뛰기 판단 기준
|
|
75
|
+
|
|
76
|
+
### HLD 건너뛰기 조건
|
|
77
|
+
- [ ] 기존 아키텍처 내에서 해결 가능
|
|
78
|
+
- [ ] 새로운 컴포넌트 추가 없음
|
|
79
|
+
- [ ] 시스템 간 연동 방식 변경 없음
|
|
80
|
+
- [ ] 기술 스택 변경 없음
|
|
81
|
+
|
|
82
|
+
**모두 해당 → HLD 건너뛰기**
|
|
83
|
+
|
|
84
|
+
### LLD 건너뛰기 조건
|
|
85
|
+
- [ ] 데이터 서빙 전략 변경 없음
|
|
86
|
+
- [ ] 새로운 외부 의존성 없음
|
|
87
|
+
- [ ] 장애 대응 전략 변경 없음
|
|
88
|
+
- [ ] 성능 최적화 불필요
|
|
89
|
+
|
|
90
|
+
**모두 해당 → LLD 건너뛰기**
|
|
91
|
+
|
|
92
|
+
### Story 건너뛰기 조건 (개발 단독 Task)
|
|
93
|
+
- [ ] 사용자 가치(비즈니스 가치) 없음
|
|
94
|
+
- [ ] 기술적 부채 해결
|
|
95
|
+
- [ ] 인프라/DevOps 작업
|
|
96
|
+
- [ ] 리팩토링
|
|
97
|
+
|
|
98
|
+
**해당 시 → Story 없이 Task 직접 생성**
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 상황 분석 프로세스
|
|
103
|
+
|
|
104
|
+
### Step 1: 현재 상태 파악
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
1. documents/ 폴더 확인
|
|
108
|
+
- documents/prd/ → PRD 존재 여부
|
|
109
|
+
- documents/hld/ → HLD 존재 여부
|
|
110
|
+
- documents/lld/ → LLD 존재 여부
|
|
111
|
+
|
|
112
|
+
2. backlogs/ 폴더 확인
|
|
113
|
+
- Epic/Story 존재 여부
|
|
114
|
+
- Story 상태 (open/close)
|
|
115
|
+
|
|
116
|
+
3. .tasks/ 폴더 확인
|
|
117
|
+
- Task 존재 여부
|
|
118
|
+
- Task 상태 (todo/inprogress/done)
|
|
119
|
+
|
|
120
|
+
4. test-cases/ 폴더 확인
|
|
121
|
+
- TC 존재 여부
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Step 2: 복잡도 판단
|
|
125
|
+
|
|
126
|
+
사용자에게 질문:
|
|
127
|
+
```
|
|
128
|
+
이 작업의 복잡도를 판단하기 위해 몇 가지 질문이 있습니다.
|
|
129
|
+
|
|
130
|
+
1. 이 작업이 영향을 주는 컴포넌트는 몇 개인가요?
|
|
131
|
+
- 1개 → L1~L2
|
|
132
|
+
- 2-3개 → L3
|
|
133
|
+
- 4개 이상 또는 시스템 간 → L4~L5
|
|
134
|
+
|
|
135
|
+
2. 아키텍처 변경이 필요한가요?
|
|
136
|
+
- 아니오 → L1~L3
|
|
137
|
+
- 예 → L4~L5
|
|
138
|
+
|
|
139
|
+
3. 이해관계자 합의가 필요한가요?
|
|
140
|
+
- 아니오 → L1~L3
|
|
141
|
+
- 예 → L4~L5
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Step 3: 다음 스텝 제안
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 출력 형식
|
|
149
|
+
|
|
150
|
+
### 상황 분석 결과
|
|
151
|
+
```markdown
|
|
152
|
+
# 워크플로우 분석
|
|
153
|
+
|
|
154
|
+
## 현재 상태
|
|
155
|
+
| 문서 | 상태 |
|
|
156
|
+
|------|------|
|
|
157
|
+
| PRD | ✅ 있음 / ❌ 없음 |
|
|
158
|
+
| Epic/Story | ✅ 있음 / ❌ 없음 |
|
|
159
|
+
| HLD | ✅ 있음 / ❌ 없음 / ⏭️ 건너뛰기 |
|
|
160
|
+
| LLD | ✅ 있음 / ❌ 없음 / ⏭️ 건너뛰기 |
|
|
161
|
+
| Task | ✅ 있음 / ❌ 없음 |
|
|
162
|
+
| TC | ✅ 있음 / ❌ 없음 |
|
|
163
|
+
|
|
164
|
+
## 복잡도 판단
|
|
165
|
+
**Level: L[N]** - [설명]
|
|
166
|
+
|
|
167
|
+
## 다음 스텝 제안
|
|
168
|
+
1. **[권장]** `/skill-name` - [이유]
|
|
169
|
+
2. [대안] `/skill-name` - [이유]
|
|
170
|
+
|
|
171
|
+
## 건너뛸 수 있는 단계
|
|
172
|
+
- [단계명]: [건너뛰기 가능 이유]
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 예시
|
|
178
|
+
|
|
179
|
+
### 예시 1: 단순 버그 수정
|
|
180
|
+
```
|
|
181
|
+
사용자: "로그인 버튼 클릭 시 에러 발생 수정"
|
|
182
|
+
|
|
183
|
+
분석 결과:
|
|
184
|
+
- 복잡도: L1 (단순 버그 수정)
|
|
185
|
+
- PRD: 불필요
|
|
186
|
+
- Story: 불필요
|
|
187
|
+
- 설계: 불필요
|
|
188
|
+
|
|
189
|
+
제안: 바로 코드 분석 및 수정 진행
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 예시 2: 새 기능 추가 (중간)
|
|
193
|
+
```
|
|
194
|
+
사용자: "사용자 프로필에 프로필 이미지 업로드 기능 추가"
|
|
195
|
+
|
|
196
|
+
분석 결과:
|
|
197
|
+
- 복잡도: L3 (복수 컴포넌트: 프론트엔드 + API + 스토리지)
|
|
198
|
+
- PRD: 선택적 (간단한 요구사항 정리)
|
|
199
|
+
- Story: 필요 (사용자 가치 있음)
|
|
200
|
+
- HLD: 불필요 (기존 아키텍처 내)
|
|
201
|
+
- LLD: 선택적 (스토리지 전략 복잡도에 따라)
|
|
202
|
+
|
|
203
|
+
제안:
|
|
204
|
+
1. [권장] `/epic-story-generator` - Story AC 정의
|
|
205
|
+
2. [이후] `/task-generator` - 구현 Task 분해
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 예시 3: 기술 부채 해결
|
|
209
|
+
```
|
|
210
|
+
사용자: "레거시 코드 리팩토링 - 서비스 레이어 분리"
|
|
211
|
+
|
|
212
|
+
분석 결과:
|
|
213
|
+
- 복잡도: L3 (리팩토링)
|
|
214
|
+
- PRD: 불필요 (기술적 작업)
|
|
215
|
+
- Story: 불필요 (사용자 가치 없음, 개발 단독)
|
|
216
|
+
- HLD: 불필요 (아키텍처 변경 없음)
|
|
217
|
+
- LLD: 선택적
|
|
218
|
+
|
|
219
|
+
제안:
|
|
220
|
+
1. [권장] `/task-generator` - Task 직접 생성 (Story 건너뛰기)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### 예시 4: 신규 시스템 구축
|
|
224
|
+
```
|
|
225
|
+
사용자: "실시간 알림 시스템 구축"
|
|
226
|
+
|
|
227
|
+
분석 결과:
|
|
228
|
+
- 복잡도: L5 (신규 시스템)
|
|
229
|
+
- PRD: 필수 (이해관계자 합의 필요)
|
|
230
|
+
- Epic/Story: 필수
|
|
231
|
+
- HLD: 필수 (새 아키텍처)
|
|
232
|
+
- LLD: 필수 (서빙 전략, 장애 대응)
|
|
233
|
+
|
|
234
|
+
제안:
|
|
235
|
+
1. [권장] `/prd-generator` - 요구사항 정의
|
|
236
|
+
2. [이후 순서] Epic/Story → HLD → LLD → Task
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 자동 호출 조건
|
|
242
|
+
|
|
243
|
+
이 agent는 다음 상황에서 자동 호출됩니다:
|
|
244
|
+
- "다음에 뭐 해야 해?"
|
|
245
|
+
- "워크플로우 안내해줘"
|
|
246
|
+
- "다음 단계가 뭐야?"
|
|
247
|
+
- 작업 시작 시 (스킬 호출 전)
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## 원칙
|
|
252
|
+
|
|
253
|
+
1. **유연성**: 정해진 순서를 강요하지 않음
|
|
254
|
+
2. **효율성**: 불필요한 단계는 건너뛰기 제안
|
|
255
|
+
3. **맥락 인식**: 현재 문서/상태 기반 판단
|
|
256
|
+
4. **실용성**: 형식보다 실질적 가치 추구
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hld-generator
|
|
3
3
|
description: |
|
|
4
|
-
PRD를 기반으로 HLD(High-Level Design)를 생성합니다.
|
|
4
|
+
PRD와 Epic/Story를 기반으로 HLD(High-Level Design)를 생성합니다.
|
|
5
5
|
HLD는 구현 전에 시스템의 큰 그림을 설계하는 문서로, 방향 정렬, 리스크 조기 발견,
|
|
6
6
|
의사결정 기록, 시니어/아키텍트의 조기 피드백을 위해 작성합니다.
|
|
7
7
|
"HLD 작성해줘", "상위 설계 문서 만들어줘", "시스템 설계", "아키텍처 설계" 등의 요청에 사용합니다.
|