timsquad 2.0.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 (181) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +347 -0
  3. package/bin/tsq.js +6 -0
  4. package/dist/commands/feedback.d.ts +3 -0
  5. package/dist/commands/feedback.d.ts.map +1 -0
  6. package/dist/commands/feedback.js +142 -0
  7. package/dist/commands/feedback.js.map +1 -0
  8. package/dist/commands/full.d.ts +3 -0
  9. package/dist/commands/full.d.ts.map +1 -0
  10. package/dist/commands/full.js +87 -0
  11. package/dist/commands/full.js.map +1 -0
  12. package/dist/commands/git/commit.d.ts +3 -0
  13. package/dist/commands/git/commit.d.ts.map +1 -0
  14. package/dist/commands/git/commit.js +88 -0
  15. package/dist/commands/git/commit.js.map +1 -0
  16. package/dist/commands/git/index.d.ts +5 -0
  17. package/dist/commands/git/index.d.ts.map +1 -0
  18. package/dist/commands/git/index.js +5 -0
  19. package/dist/commands/git/index.js.map +1 -0
  20. package/dist/commands/git/pr.d.ts +3 -0
  21. package/dist/commands/git/pr.d.ts.map +1 -0
  22. package/dist/commands/git/pr.js +138 -0
  23. package/dist/commands/git/pr.js.map +1 -0
  24. package/dist/commands/git/release.d.ts +3 -0
  25. package/dist/commands/git/release.d.ts.map +1 -0
  26. package/dist/commands/git/release.js +158 -0
  27. package/dist/commands/git/release.js.map +1 -0
  28. package/dist/commands/git/sync.d.ts +3 -0
  29. package/dist/commands/git/sync.d.ts.map +1 -0
  30. package/dist/commands/git/sync.js +132 -0
  31. package/dist/commands/git/sync.js.map +1 -0
  32. package/dist/commands/init.d.ts +3 -0
  33. package/dist/commands/init.d.ts.map +1 -0
  34. package/dist/commands/init.js +150 -0
  35. package/dist/commands/init.js.map +1 -0
  36. package/dist/commands/log.d.ts +3 -0
  37. package/dist/commands/log.d.ts.map +1 -0
  38. package/dist/commands/log.js +271 -0
  39. package/dist/commands/log.js.map +1 -0
  40. package/dist/commands/metrics.d.ts +3 -0
  41. package/dist/commands/metrics.d.ts.map +1 -0
  42. package/dist/commands/metrics.js +299 -0
  43. package/dist/commands/metrics.js.map +1 -0
  44. package/dist/commands/quick.d.ts +3 -0
  45. package/dist/commands/quick.d.ts.map +1 -0
  46. package/dist/commands/quick.js +136 -0
  47. package/dist/commands/quick.js.map +1 -0
  48. package/dist/commands/retro.d.ts +3 -0
  49. package/dist/commands/retro.d.ts.map +1 -0
  50. package/dist/commands/retro.js +280 -0
  51. package/dist/commands/retro.js.map +1 -0
  52. package/dist/commands/status.d.ts +3 -0
  53. package/dist/commands/status.d.ts.map +1 -0
  54. package/dist/commands/status.js +127 -0
  55. package/dist/commands/status.js.map +1 -0
  56. package/dist/commands/watch.d.ts +3 -0
  57. package/dist/commands/watch.d.ts.map +1 -0
  58. package/dist/commands/watch.js +213 -0
  59. package/dist/commands/watch.js.map +1 -0
  60. package/dist/index.d.ts +3 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +50 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/lib/config.d.ts +34 -0
  65. package/dist/lib/config.d.ts.map +1 -0
  66. package/dist/lib/config.js +108 -0
  67. package/dist/lib/config.js.map +1 -0
  68. package/dist/lib/project.d.ts +47 -0
  69. package/dist/lib/project.d.ts.map +1 -0
  70. package/dist/lib/project.js +191 -0
  71. package/dist/lib/project.js.map +1 -0
  72. package/dist/lib/template.d.ts +33 -0
  73. package/dist/lib/template.d.ts.map +1 -0
  74. package/dist/lib/template.js +151 -0
  75. package/dist/lib/template.js.map +1 -0
  76. package/dist/types/config.d.ts +75 -0
  77. package/dist/types/config.d.ts.map +1 -0
  78. package/dist/types/config.js +66 -0
  79. package/dist/types/config.js.map +1 -0
  80. package/dist/types/feedback.d.ts +59 -0
  81. package/dist/types/feedback.d.ts.map +1 -0
  82. package/dist/types/feedback.js +26 -0
  83. package/dist/types/feedback.js.map +1 -0
  84. package/dist/types/index.d.ts +4 -0
  85. package/dist/types/index.d.ts.map +1 -0
  86. package/dist/types/index.js +5 -0
  87. package/dist/types/index.js.map +1 -0
  88. package/dist/types/project.d.ts +89 -0
  89. package/dist/types/project.d.ts.map +1 -0
  90. package/dist/types/project.js +44 -0
  91. package/dist/types/project.js.map +1 -0
  92. package/dist/utils/colors.d.ts +30 -0
  93. package/dist/utils/colors.d.ts.map +1 -0
  94. package/dist/utils/colors.js +54 -0
  95. package/dist/utils/colors.js.map +1 -0
  96. package/dist/utils/date.d.ts +25 -0
  97. package/dist/utils/date.d.ts.map +1 -0
  98. package/dist/utils/date.js +65 -0
  99. package/dist/utils/date.js.map +1 -0
  100. package/dist/utils/fs.d.ts +49 -0
  101. package/dist/utils/fs.d.ts.map +1 -0
  102. package/dist/utils/fs.js +84 -0
  103. package/dist/utils/fs.js.map +1 -0
  104. package/dist/utils/prompts.d.ts +31 -0
  105. package/dist/utils/prompts.d.ts.map +1 -0
  106. package/dist/utils/prompts.js +95 -0
  107. package/dist/utils/prompts.js.map +1 -0
  108. package/dist/utils/yaml.d.ts +21 -0
  109. package/dist/utils/yaml.d.ts.map +1 -0
  110. package/dist/utils/yaml.js +40 -0
  111. package/dist/utils/yaml.js.map +1 -0
  112. package/package.json +71 -0
  113. package/templates/common/CLAUDE.md.template +254 -0
  114. package/templates/common/claude/agents/tsq-dba.md +290 -0
  115. package/templates/common/claude/agents/tsq-designer.md +304 -0
  116. package/templates/common/claude/agents/tsq-developer.md +118 -0
  117. package/templates/common/claude/agents/tsq-planner.md +90 -0
  118. package/templates/common/claude/agents/tsq-prompter.md +336 -0
  119. package/templates/common/claude/agents/tsq-qa.md +134 -0
  120. package/templates/common/claude/agents/tsq-retro.md +168 -0
  121. package/templates/common/claude/agents/tsq-security.md +190 -0
  122. package/templates/common/claude/skills/architecture/SKILL.md +123 -0
  123. package/templates/common/claude/skills/backend/node/SKILL.md +1015 -0
  124. package/templates/common/claude/skills/coding/SKILL.md +171 -0
  125. package/templates/common/claude/skills/database/prisma/SKILL.md +357 -0
  126. package/templates/common/claude/skills/frontend/nextjs/SKILL.md +279 -0
  127. package/templates/common/claude/skills/frontend/react/SKILL.md +1729 -0
  128. package/templates/common/claude/skills/methodology/bdd/SKILL.md +234 -0
  129. package/templates/common/claude/skills/methodology/ddd/SKILL.md +311 -0
  130. package/templates/common/claude/skills/methodology/tdd/SKILL.md +512 -0
  131. package/templates/common/claude/skills/planning/SKILL.md +90 -0
  132. package/templates/common/claude/skills/security/SKILL.md +234 -0
  133. package/templates/common/claude/skills/testing/SKILL.md +146 -0
  134. package/templates/common/claude/skills/typescript/SKILL.md +435 -0
  135. package/templates/common/config.template.yaml +131 -0
  136. package/templates/common/timsquad/architectures/clean/ARCHITECTURE.md +49 -0
  137. package/templates/common/timsquad/architectures/clean/backend.xml +210 -0
  138. package/templates/common/timsquad/architectures/clean/frontend.xml +148 -0
  139. package/templates/common/timsquad/architectures/fsd/ARCHITECTURE.md +67 -0
  140. package/templates/common/timsquad/architectures/fsd/frontend.xml +288 -0
  141. package/templates/common/timsquad/architectures/hexagonal/ARCHITECTURE.md +60 -0
  142. package/templates/common/timsquad/architectures/hexagonal/backend.xml +300 -0
  143. package/templates/common/timsquad/constraints/competency-framework.xml +501 -0
  144. package/templates/common/timsquad/constraints/ssot-schema.xml +433 -0
  145. package/templates/common/timsquad/feedback/feedback-router.sh +341 -0
  146. package/templates/common/timsquad/feedback/routing-rules.yaml +352 -0
  147. package/templates/common/timsquad/generators/data-design.xml +290 -0
  148. package/templates/common/timsquad/generators/prd.xml +280 -0
  149. package/templates/common/timsquad/generators/requirements.xml +220 -0
  150. package/templates/common/timsquad/generators/service-spec.xml +266 -0
  151. package/templates/common/timsquad/logs/_example.md +81 -0
  152. package/templates/common/timsquad/logs/_template.md +46 -0
  153. package/templates/common/timsquad/patterns/cqrs.xml +127 -0
  154. package/templates/common/timsquad/patterns/event-sourcing.xml +85 -0
  155. package/templates/common/timsquad/patterns/repository.xml +64 -0
  156. package/templates/common/timsquad/process/state-machine.xml +343 -0
  157. package/templates/common/timsquad/process/validation-rules.xml +308 -0
  158. package/templates/common/timsquad/process/workflow-base.xml +202 -0
  159. package/templates/common/timsquad/retrospective/cycle-report.template.md +205 -0
  160. package/templates/common/timsquad/retrospective/metrics/metrics-schema.json +203 -0
  161. package/templates/common/timsquad/retrospective/patterns/failure-patterns.md +199 -0
  162. package/templates/common/timsquad/retrospective/patterns/success-patterns.md +262 -0
  163. package/templates/common/timsquad/retrospective/retrospective-config.xml +294 -0
  164. package/templates/common/timsquad/retrospective/retrospective-state.xml +210 -0
  165. package/templates/common/timsquad/ssot/adr/ADR-000-template.md +121 -0
  166. package/templates/common/timsquad/ssot/adr/ADR-001-example.md +115 -0
  167. package/templates/common/timsquad/ssot/data-design.template.md +132 -0
  168. package/templates/common/timsquad/ssot/deployment-spec.template.md +384 -0
  169. package/templates/common/timsquad/ssot/env-config.template.md +346 -0
  170. package/templates/common/timsquad/ssot/error-codes.template.md +114 -0
  171. package/templates/common/timsquad/ssot/functional-spec.template.md +185 -0
  172. package/templates/common/timsquad/ssot/glossary.template.md +148 -0
  173. package/templates/common/timsquad/ssot/integration-spec.template.md +391 -0
  174. package/templates/common/timsquad/ssot/planning.template.md +94 -0
  175. package/templates/common/timsquad/ssot/prd.template.md +102 -0
  176. package/templates/common/timsquad/ssot/requirements.template.md +117 -0
  177. package/templates/common/timsquad/ssot/security-spec.template.md +309 -0
  178. package/templates/common/timsquad/ssot/service-spec.template.md +194 -0
  179. package/templates/common/timsquad/ssot/test-spec.template.md +264 -0
  180. package/templates/common/timsquad/ssot/ui-ux-spec.template.md +262 -0
  181. package/templates/common/timsquad/state/workspace.xml +217 -0
@@ -0,0 +1,234 @@
1
+ ---
2
+ name: bdd
3
+ description: Behavior-Driven Development κ°€μ΄λ“œλΌμΈ
4
+ user-invocable: false
5
+ compatible-with: [tdd, ddd]
6
+ ---
7
+
8
+ <skill name="bdd">
9
+ <purpose>λΉ„μ¦ˆλ‹ˆμŠ€ ν–‰μœ„ μ€‘μ‹¬μ˜ ν…ŒμŠ€νŠΈ 및 개발 방법둠</purpose>
10
+
11
+ <philosophy>
12
+ <principle>λΉ„μ¦ˆλ‹ˆμŠ€ μ–Έμ–΄λ‘œ μš”κ΅¬μ‚¬ν•­ ν‘œν˜„</principle>
13
+ <principle>Given-When-Then으둜 μ‹œλ‚˜λ¦¬μ˜€ λͺ…μ„Έ</principle>
14
+ <principle>μ‚΄μ•„μžˆλŠ” λ¬Έμ„œ (Living Documentation)</principle>
15
+ <principle>개발자, QA, 기획자 κ°„ 곡톡 μ–Έμ–΄</principle>
16
+ </philosophy>
17
+
18
+ <core-concepts>
19
+ <concept name="user-story">
20
+ <description>μ‚¬μš©μž κ΄€μ μ˜ κΈ°λŠ₯ μ„€λͺ…</description>
21
+ <template>
22
+ As a [μ—­ν• ]
23
+ I want [κΈ°λŠ₯]
24
+ So that [κ°€μΉ˜/이유]
25
+ </template>
26
+ <example>
27
+ As a νšŒμ›
28
+ I want λΉ„λ°€λ²ˆν˜Έλ₯Ό μž¬μ„€μ •
29
+ So that 계정에 λ‹€μ‹œ μ ‘κ·Όν•  수 μžˆλ‹€
30
+ </example>
31
+ </concept>
32
+
33
+ <concept name="scenario">
34
+ <description>ꡬ체적인 ν–‰μœ„ μ‹œλ‚˜λ¦¬μ˜€</description>
35
+ <template>
36
+ Given [사전 쑰건]
37
+ When [ν–‰μœ„]
38
+ Then [κΈ°λŒ€ κ²°κ³Ό]
39
+ </template>
40
+ </concept>
41
+
42
+ <concept name="acceptance-criteria">
43
+ <description>인수 쑰건 - κΈ°λŠ₯ μ™„λ£Œ κΈ°μ€€</description>
44
+ </concept>
45
+ </core-concepts>
46
+
47
+ <gherkin-syntax>
48
+ <description>BDD μ‹œλ‚˜λ¦¬μ˜€ μž‘μ„± 문법</description>
49
+
50
+ <example type="feature-file">
51
+ <title>둜그인 κΈ°λŠ₯</title>
52
+ <code><![CDATA[
53
+ Feature: μ‚¬μš©μž 둜그인
54
+ μ‚¬μš©μžκ°€ 이메일과 λΉ„λ°€λ²ˆν˜Έλ‘œ λ‘œκ·ΈμΈν•  수 μžˆλ‹€
55
+
56
+ Background:
57
+ Given νšŒμ›κ°€μž…λœ μ‚¬μš©μžκ°€ μ‘΄μž¬ν•œλ‹€
58
+ | email | password |
59
+ | user@test.com | password1 |
60
+
61
+ Scenario: μ˜¬λ°”λ₯Έ 자격 증λͺ…μœΌλ‘œ 둜그인 성곡
62
+ Given 둜그인 νŽ˜μ΄μ§€μ— μ ‘μ†ν•œλ‹€
63
+ When 이메일 "user@test.com"을 μž…λ ₯ν•œλ‹€
64
+ And λΉ„λ°€λ²ˆν˜Έ "password1"을 μž…λ ₯ν•œλ‹€
65
+ And 둜그인 λ²„νŠΌμ„ ν΄λ¦­ν•œλ‹€
66
+ Then λŒ€μ‹œλ³΄λ“œ νŽ˜μ΄μ§€λ‘œ μ΄λ™ν•œλ‹€
67
+ And "둜그인 성곡" λ©”μ‹œμ§€κ°€ ν‘œμ‹œλœλ‹€
68
+
69
+ Scenario: 잘λͺ»λœ λΉ„λ°€λ²ˆν˜Έλ‘œ 둜그인 μ‹€νŒ¨
70
+ Given 둜그인 νŽ˜μ΄μ§€μ— μ ‘μ†ν•œλ‹€
71
+ When 이메일 "user@test.com"을 μž…λ ₯ν•œλ‹€
72
+ And λΉ„λ°€λ²ˆν˜Έ "wrongpassword"λ₯Ό μž…λ ₯ν•œλ‹€
73
+ And 둜그인 λ²„νŠΌμ„ ν΄λ¦­ν•œλ‹€
74
+ Then 둜그인 νŽ˜μ΄μ§€μ— 머무λ₯Έλ‹€
75
+ And "이메일 λ˜λŠ” λΉ„λ°€λ²ˆν˜Έκ°€ μ˜¬λ°”λ₯΄μ§€ μ•ŠμŠ΅λ‹ˆλ‹€" μ—λŸ¬κ°€ ν‘œμ‹œλœλ‹€
76
+
77
+ Scenario Outline: λ‹€μ–‘ν•œ 잘λͺ»λœ μž…λ ₯
78
+ When 이메일 "<email>"을 μž…λ ₯ν•œλ‹€
79
+ And λΉ„λ°€λ²ˆν˜Έ "<password>"λ₯Ό μž…λ ₯ν•œλ‹€
80
+ And 둜그인 λ²„νŠΌμ„ ν΄λ¦­ν•œλ‹€
81
+ Then "<error>" μ—λŸ¬κ°€ ν‘œμ‹œλœλ‹€
82
+
83
+ Examples:
84
+ | email | password | error |
85
+ | | pass | 이메일을 μž…λ ₯ν•΄μ£Όμ„Έμš” |
86
+ | invalid-email | pass | μ˜¬λ°”λ₯Έ 이메일 ν˜•μ‹μ΄ μ•„λ‹™λ‹ˆλ‹€ |
87
+ | user@test.com | | λΉ„λ°€λ²ˆν˜Έλ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš” |
88
+ ]]></code>
89
+ </example>
90
+ </gherkin-syntax>
91
+
92
+ <implementation-patterns>
93
+ <pattern name="step-definitions" language="typescript">
94
+ <description>Gherkin μŠ€ν…μ„ μ½”λ“œλ‘œ μ—°κ²°</description>
95
+ <example>
96
+ <title>Playwright + Cucumber μ˜ˆμ‹œ</title>
97
+ <code><![CDATA[
98
+ import { Given, When, Then } from '@cucumber/cucumber';
99
+ import { expect } from '@playwright/test';
100
+
101
+ Given('둜그인 νŽ˜μ΄μ§€μ— μ ‘μ†ν•œλ‹€', async function() {
102
+ await this.page.goto('/login');
103
+ });
104
+
105
+ When('이메일 {string}을 μž…λ ₯ν•œλ‹€', async function(email: string) {
106
+ await this.page.fill('[data-testid="email-input"]', email);
107
+ });
108
+
109
+ When('λΉ„λ°€λ²ˆν˜Έ {string}λ₯Ό μž…λ ₯ν•œλ‹€', async function(password: string) {
110
+ await this.page.fill('[data-testid="password-input"]', password);
111
+ });
112
+
113
+ When('둜그인 λ²„νŠΌμ„ ν΄λ¦­ν•œλ‹€', async function() {
114
+ await this.page.click('[data-testid="login-button"]');
115
+ });
116
+
117
+ Then('λŒ€μ‹œλ³΄λ“œ νŽ˜μ΄μ§€λ‘œ μ΄λ™ν•œλ‹€', async function() {
118
+ await expect(this.page).toHaveURL('/dashboard');
119
+ });
120
+
121
+ Then('{string} λ©”μ‹œμ§€κ°€ ν‘œμ‹œλœλ‹€', async function(message: string) {
122
+ await expect(this.page.locator('[data-testid="toast"]'))
123
+ .toContainText(message);
124
+ });
125
+ ]]></code>
126
+ </example>
127
+ </pattern>
128
+
129
+ <pattern name="api-bdd" language="typescript">
130
+ <description>API ν…ŒμŠ€νŠΈμš© BDD</description>
131
+ <example>
132
+ <title>Vitest + SuperTest μ˜ˆμ‹œ</title>
133
+ <code><![CDATA[
134
+ describe('Feature: μ‚¬μš©μž 등둝 API', () => {
135
+ describe('Scenario: μœ νš¨ν•œ μ •λ³΄λ‘œ νšŒμ›κ°€μž…', () => {
136
+ it('Given μœ νš¨ν•œ μ‚¬μš©μž 정보가 μžˆλ‹€', () => {
137
+ ctx.userData = {
138
+ email: 'new@test.com',
139
+ password: 'SecurePass123!',
140
+ name: '홍길동'
141
+ };
142
+ });
143
+
144
+ it('When POST /api/users μš”μ²­μ„ 보낸닀', async () => {
145
+ ctx.response = await request(app)
146
+ .post('/api/users')
147
+ .send(ctx.userData);
148
+ });
149
+
150
+ it('Then 201 μƒνƒœ μ½”λ“œλ₯Ό λ°˜ν™˜ν•œλ‹€', () => {
151
+ expect(ctx.response.status).toBe(201);
152
+ });
153
+
154
+ it('And μƒμ„±λœ μ‚¬μš©μž IDλ₯Ό λ°˜ν™˜ν•œλ‹€', () => {
155
+ expect(ctx.response.body.data.id).toBeDefined();
156
+ });
157
+ });
158
+ });
159
+ ]]></code>
160
+ </example>
161
+ </pattern>
162
+ </implementation-patterns>
163
+
164
+ <bdd-vs-tdd>
165
+ <comparison>
166
+ <aspect name="관점">
167
+ <tdd>개발자 관점 (μ½”λ“œ λ‹¨μœ„)</tdd>
168
+ <bdd>μ‚¬μš©μž/λΉ„μ¦ˆλ‹ˆμŠ€ 관점 (ν–‰μœ„ λ‹¨μœ„)</bdd>
169
+ </aspect>
170
+ <aspect name="μ–Έμ–΄">
171
+ <tdd>기술 μš©μ–΄ (ν•¨μˆ˜, 클래슀)</tdd>
172
+ <bdd>λΉ„μ¦ˆλ‹ˆμŠ€ μš©μ–΄ (μ‹œλ‚˜λ¦¬μ˜€, κΈ°λŠ₯)</bdd>
173
+ </aspect>
174
+ <aspect name="ν…ŒμŠ€νŠΈ λŒ€μƒ">
175
+ <tdd>λ‹¨μœ„ ν…ŒμŠ€νŠΈ 쀑심</tdd>
176
+ <bdd>인수 ν…ŒμŠ€νŠΈ, E2E 쀑심</bdd>
177
+ </aspect>
178
+ <aspect name="λ¬Έμ„œν™”">
179
+ <tdd>μ½”λ“œκ°€ λ¬Έμ„œ</tdd>
180
+ <bdd>μ‹œλ‚˜λ¦¬μ˜€κ°€ μ‚΄μ•„μžˆλŠ” λ¬Έμ„œ</bdd>
181
+ </aspect>
182
+ </comparison>
183
+ <recommendation>TDD + BDD ν•¨κ»˜ μ‚¬μš© ꢌμž₯ (보완 관계)</recommendation>
184
+ </bdd-vs-tdd>
185
+
186
+ <directory-structure>
187
+ <dir name="features">
188
+ <description>Gherkin feature 파일</description>
189
+ <file name="auth/login.feature" />
190
+ <file name="auth/register.feature" />
191
+ <file name="order/checkout.feature" />
192
+ </dir>
193
+ <dir name="step-definitions">
194
+ <description>μŠ€ν… κ΅¬ν˜„</description>
195
+ <file name="auth.steps.ts" />
196
+ <file name="order.steps.ts" />
197
+ <file name="common.steps.ts" />
198
+ </dir>
199
+ <dir name="support">
200
+ <description>헬퍼, ν›…</description>
201
+ <file name="world.ts" example="ν…ŒμŠ€νŠΈ μ»¨ν…μŠ€νŠΈ" />
202
+ <file name="hooks.ts" example="Before, After ν›…" />
203
+ </dir>
204
+ </directory-structure>
205
+
206
+ <tools>
207
+ <tool name="cucumber-js">JavaScript/TypeScript Cucumber κ΅¬ν˜„</tool>
208
+ <tool name="playwright">E2E ν…ŒμŠ€νŠΈ λŸ¬λ„ˆ</tool>
209
+ <tool name="jest-cucumber">Jest와 Cucumber 톡합</tool>
210
+ </tools>
211
+
212
+ <rules>
213
+ <category name="ν•„μˆ˜">
214
+ <must>μ‹œλ‚˜λ¦¬μ˜€λŠ” λΉ„μ¦ˆλ‹ˆμŠ€ μ–Έμ–΄λ‘œ μž‘μ„±</must>
215
+ <must>Given-When-Then ν˜•μ‹ μ€€μˆ˜</must>
216
+ <must>ν•˜λ‚˜μ˜ μ‹œλ‚˜λ¦¬μ˜€λŠ” ν•˜λ‚˜μ˜ ν–‰μœ„λ§Œ ν…ŒμŠ€νŠΈ</must>
217
+ <must>μ‹œλ‚˜λ¦¬μ˜€λŠ” λ…λ¦½μ μœΌλ‘œ μ‹€ν–‰ κ°€λŠ₯ν•΄μ•Ό 함</must>
218
+ </category>
219
+ <category name="κΈˆμ§€">
220
+ <must-not>기술 μš©μ–΄λ₯Ό μ‹œλ‚˜λ¦¬μ˜€μ— λ…ΈμΆœ (CSS selector, API endpoint λ“±)</must-not>
221
+ <must-not>ν…ŒμŠ€νŠΈ κ°„ μƒνƒœ 곡유</must-not>
222
+ <must-not>UI κ΅¬ν˜„ 세뢀사항 μ‹œλ‚˜λ¦¬μ˜€μ— 포함</must-not>
223
+ </category>
224
+ </rules>
225
+
226
+ <checklist>
227
+ <item priority="critical">Feature 파일이 λΉ„μ¦ˆλ‹ˆμŠ€ μ–Έμ–΄λ‘œ μž‘μ„±λ¨</item>
228
+ <item priority="critical">Given-When-Then ν˜•μ‹ μ€€μˆ˜</item>
229
+ <item priority="high">μ‹œλ‚˜λ¦¬μ˜€κ°€ λ…λ¦½μ μœΌλ‘œ μ‹€ν–‰ κ°€λŠ₯</item>
230
+ <item priority="high">Step Definition μž¬μ‚¬μš©μ„± 확보</item>
231
+ <item priority="medium">Background둜 곡톡 μ „μ œμ‘°κ±΄ μΆ”μΆœ</item>
232
+ <item priority="medium">Scenario Outline으둜 데이터 주도 ν…ŒμŠ€νŠΈ</item>
233
+ </checklist>
234
+ </skill>
@@ -0,0 +1,311 @@
1
+ ---
2
+ name: ddd
3
+ description: Domain-Driven Design μ „λž΅μ  섀계 κ°€μ΄λ“œλΌμΈ
4
+ user-invocable: false
5
+ compatible-with: [tdd, bdd]
6
+ note: μ „μˆ μ  νŒ¨ν„΄(Aggregate, Entity λ“±)은 architectures/에 포함됨
7
+ ---
8
+
9
+ <skill name="ddd">
10
+ <purpose>λ³΅μž‘ν•œ 도메인을 효과적으둜 λͺ¨λΈλ§ν•˜λŠ” μ „λž΅μ  섀계 방법둠</purpose>
11
+
12
+ <philosophy>
13
+ <principle>도메인 전문가와 개발자의 ν˜‘μ—…</principle>
14
+ <principle>μœ λΉ„μΏΌν„°μŠ€ μ–Έμ–΄ (Ubiquitous Language)</principle>
15
+ <principle>λͺ¨λΈ 주도 섀계 (Model-Driven Design)</principle>
16
+ <principle>경계 μ»¨ν…μŠ€νŠΈλ‘œ λ³΅μž‘μ„± 뢄리</principle>
17
+ </philosophy>
18
+
19
+ <strategic-vs-tactical>
20
+ <strategic location="이 μŠ€ν‚¬">
21
+ <item>Bounded Context (경계 μ»¨ν…μŠ€νŠΈ)</item>
22
+ <item>Ubiquitous Language (μœ λΉ„μΏΌν„°μŠ€ μ–Έμ–΄)</item>
23
+ <item>Context Map (μ»¨ν…μŠ€νŠΈ λ§΅)</item>
24
+ <item>Subdomain (μ„œλΈŒλ„λ©”μΈ)</item>
25
+ </strategic>
26
+ <tactical location="architectures/ 및 patterns/">
27
+ <item>Aggregate, Entity, Value Object β†’ clean/backend.xml</item>
28
+ <item>Repository β†’ patterns/repository.xml</item>
29
+ <item>Domain Event β†’ patterns/event-sourcing.xml</item>
30
+ </tactical>
31
+ </strategic-vs-tactical>
32
+
33
+ <core-concepts>
34
+ <concept name="ubiquitous-language">
35
+ <description>
36
+ 도메인 전문가와 κ°œλ°œμžκ°€ κ³΅μœ ν•˜λŠ” 곡톡 μ–Έμ–΄.
37
+ μ½”λ“œ, λ¬Έμ„œ, λŒ€ν™” λͺ¨λ‘μ—μ„œ λ™μΌν•œ μš©μ–΄ μ‚¬μš©.
38
+ </description>
39
+ <example type="bad">
40
+ <code><![CDATA[
41
+ // 개발자 μš©μ–΄λ‘œ μž‘μ„± - 도메인 μ „λ¬Έκ°€κ°€ 이해 λͺ»ν•¨
42
+ class DataProcessor {
43
+ processRecord(record: Record) {
44
+ if (record.status === 1) {
45
+ this.updateFlag(record, true);
46
+ }
47
+ }
48
+ }
49
+ ]]></code>
50
+ </example>
51
+ <example type="good">
52
+ <code><![CDATA[
53
+ // μœ λΉ„μΏΌν„°μŠ€ μ–Έμ–΄ μ‚¬μš© - 도메인 전문가도 이해 κ°€λŠ₯
54
+ class OrderFulfillment {
55
+ shipOrder(order: Order) {
56
+ if (order.isPaid()) {
57
+ order.markAsShipped();
58
+ }
59
+ }
60
+ }
61
+ ]]></code>
62
+ </example>
63
+ <practice>
64
+ <item>μš©μ–΄ 사전 (Glossary) μž‘μ„± 및 μœ μ§€</item>
65
+ <item>μ½”λ“œ 리뷰 μ‹œ 도메인 μš©μ–΄ μ€€μˆ˜ 확인</item>
66
+ <item>도메인 전문가와 정기적 μš©μ–΄ κ²€ν† </item>
67
+ </practice>
68
+ </concept>
69
+
70
+ <concept name="bounded-context">
71
+ <description>
72
+ νŠΉμ • 도메인 λͺ¨λΈμ΄ μ μš©λ˜λŠ” λͺ…μ‹œμ  경계.
73
+ 같은 μš©μ–΄λ„ μ»¨ν…μŠ€νŠΈλ§ˆλ‹€ λ‹€λ₯Έ 의미λ₯Ό κ°€μ§ˆ 수 있음.
74
+ </description>
75
+ <example>
76
+ <title>"Product"의 λ‹€λ₯Έ 의미</title>
77
+ <context name="Catalog">μƒν’ˆ 정보, μ„€λͺ…, 이미지</context>
78
+ <context name="Inventory">재고 μˆ˜λŸ‰, μœ„μΉ˜, SKU</context>
79
+ <context name="Pricing">가격, 할인, ν”„λ‘œλͺ¨μ…˜</context>
80
+ <context name="Shipping">무게, 크기, 배솑 μ œν•œ</context>
81
+ </example>
82
+ <guidelines>
83
+ <guideline>ν•˜λ‚˜μ˜ Bounded Context = ν•˜λ‚˜μ˜ νŒ€</guideline>
84
+ <guideline>ν•˜λ‚˜μ˜ Bounded Context = ν•˜λ‚˜μ˜ μ½”λ“œλ² μ΄μŠ€ (λ˜λŠ” λͺ¨λ“ˆ)</guideline>
85
+ <guideline>μ»¨ν…μŠ€νŠΈ κ°„ 톡신은 λͺ…μ‹œμ  μΈν„°νŽ˜μ΄μŠ€λ‘œ</guideline>
86
+ </guidelines>
87
+ </concept>
88
+
89
+ <concept name="subdomain">
90
+ <description>λΉ„μ¦ˆλ‹ˆμŠ€ λ„λ©”μΈμ˜ 논리적 λΆ„ν• </description>
91
+ <types>
92
+ <type name="core">
93
+ <description>λΉ„μ¦ˆλ‹ˆμŠ€ 핡심 차별화 μš”μ†Œ</description>
94
+ <strategy>졜고 인λ ₯ νˆ¬μž…, 자체 개발</strategy>
95
+ <example>쿠팑의 λ‘œμΌ“λ°°μ†‘ μ‹œμŠ€ν…œ</example>
96
+ </type>
97
+ <type name="supporting">
98
+ <description>Coreλ₯Ό μ§€μ›ν•˜μ§€λ§Œ 차별화 μš”μ†Œ μ•„λ‹˜</description>
99
+ <strategy>μ•„μ›ƒμ†Œμ‹± λ˜λŠ” μ λ‹Ήν•œ ν’ˆμ§ˆ</strategy>
100
+ <example>λ‚΄λΆ€ 관리 μ‹œμŠ€ν…œ</example>
101
+ </type>
102
+ <type name="generic">
103
+ <description>λͺ¨λ“  λΉ„μ¦ˆλ‹ˆμŠ€μ— 곡톡</description>
104
+ <strategy>ꡬ맀 λ˜λŠ” μ˜€ν”ˆμ†ŒμŠ€ μ‚¬μš©</strategy>
105
+ <example>결제, 인증, 이메일 λ°œμ†‘</example>
106
+ </type>
107
+ </types>
108
+ </concept>
109
+
110
+ <concept name="context-map">
111
+ <description>Bounded Context κ°„μ˜ 관계λ₯Ό μ‹œκ°ν™”</description>
112
+ <relationships>
113
+ <relationship name="partnership">
114
+ <description>두 νŒ€μ΄ ν•¨κ»˜ 성곡/μ‹€νŒ¨. κΈ΄λ°€ν•œ ν˜‘λ ₯</description>
115
+ <diagram>[Context A] ←Partnershipβ†’ [Context B]</diagram>
116
+ </relationship>
117
+ <relationship name="shared-kernel">
118
+ <description>곡유 λͺ¨λΈ. μ–‘μͺ½ ν•©μ˜ ν•„μš”</description>
119
+ <diagram>[Context A] ←Shared Kernelβ†’ [Context B]</diagram>
120
+ <warning>λ³€κ²½ μ‹œ μ–‘μͺ½ 영ν–₯. μ΅œμ†Œν™” ꢌμž₯</warning>
121
+ </relationship>
122
+ <relationship name="customer-supplier">
123
+ <description>Upstream(κ³΅κΈ‰μž)이 Downstream(고객) μš”κ΅¬ 수용</description>
124
+ <diagram>[Supplier] →Customer-Supplier→ [Customer]</diagram>
125
+ </relationship>
126
+ <relationship name="conformist">
127
+ <description>Downstream이 Upstream λͺ¨λΈμ„ κ·ΈλŒ€λ‘œ 수용</description>
128
+ <diagram>[Upstream] →Conformist→ [Downstream]</diagram>
129
+ <usecase>μ™ΈλΆ€ API 연동 μ‹œ</usecase>
130
+ </relationship>
131
+ <relationship name="anticorruption-layer">
132
+ <description>λ²ˆμ—­ λ ˆμ΄μ–΄λ‘œ μ™ΈλΆ€ λͺ¨λΈλ‘œλΆ€ν„° 보호</description>
133
+ <diagram>[External] →ACL→ [Our Context]</diagram>
134
+ <usecase>λ ˆκ±°μ‹œ μ‹œμŠ€ν…œ 연동, μ™ΈλΆ€ API</usecase>
135
+ </relationship>
136
+ <relationship name="open-host-service">
137
+ <description>잘 μ •μ˜λœ ν”„λ‘œν† μ½œλ‘œ μ„œλΉ„μŠ€ 제곡</description>
138
+ <diagram>[Context] →OHS/PL→ [Multiple Consumers]</diagram>
139
+ </relationship>
140
+ <relationship name="published-language">
141
+ <description>ν‘œμ€€ν™”λœ κ΅ν™˜ μ–Έμ–΄ (JSON Schema, Protobuf)</description>
142
+ </relationship>
143
+ </relationships>
144
+ </concept>
145
+ </core-concepts>
146
+
147
+ <context-map-example>
148
+ <title>이컀머슀 Context Map</title>
149
+ <diagram><![CDATA[
150
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
151
+ β”‚ E-Commerce System β”‚
152
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
153
+ β”‚ β”‚
154
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” Partnership β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
155
+ β”‚ β”‚ Catalog │◄─────────────────────►│ Pricing β”‚ β”‚
156
+ β”‚ β”‚ Context β”‚ β”‚ Context β”‚ β”‚
157
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
158
+ β”‚ β”‚ β”‚
159
+ β”‚ β”‚ Customer-Supplier β”‚
160
+ β”‚ β–Ό β”‚
161
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
162
+ β”‚ β”‚ Order β”‚ Shared Kernel β”‚ Inventory β”‚ β”‚
163
+ β”‚ β”‚ Context │◄─────────────────────►│ Context β”‚ β”‚
164
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ (Product ID) β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
165
+ β”‚ β”‚ β”‚
166
+ β”‚ β”‚ ACL β”‚
167
+ β”‚ β–Ό β”‚
168
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
169
+ β”‚ β”‚ Shipping β”‚ β”‚ Payment β”‚ β”‚
170
+ β”‚ β”‚ Context β”‚ β”‚ Context β”‚ β”‚
171
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
172
+ β”‚ β”‚ ACL β”‚
173
+ β”‚ β–Ό β”‚
174
+ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
175
+ β”‚ β”‚ Stripe β”‚ β”‚
176
+ β”‚ β”‚ (External) β”‚ β”‚
177
+ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
178
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
179
+ ]]></diagram>
180
+ </context-map-example>
181
+
182
+ <anticorruption-layer-pattern>
183
+ <description>μ™ΈλΆ€ λͺ¨λΈλ‘œλΆ€ν„° 도메인 보호</description>
184
+ <example language="typescript">
185
+ <title>Stripe 결제 연동 ACL</title>
186
+ <code><![CDATA[
187
+ // μ™ΈλΆ€ λͺ¨λΈ (Stripe)
188
+ interface StripePaymentIntent {
189
+ id: string;
190
+ amount: number; // cents
191
+ currency: string;
192
+ status: 'succeeded' | 'pending' | 'failed';
193
+ payment_method: string;
194
+ }
195
+
196
+ // 우리 도메인 λͺ¨λΈ
197
+ interface Payment {
198
+ id: PaymentId;
199
+ amount: Money; // 우리의 Money Value Object
200
+ status: PaymentStatus; // 우리의 enum
201
+ orderId: OrderId;
202
+ }
203
+
204
+ // Anti-Corruption Layer
205
+ class StripePaymentAdapter implements PaymentGateway {
206
+ constructor(private stripe: Stripe) {}
207
+
208
+ async processPayment(payment: Payment): Promise<PaymentResult> {
209
+ // 도메인 β†’ μ™ΈλΆ€ λͺ¨λΈ λ³€ν™˜
210
+ const intent = await this.stripe.paymentIntents.create({
211
+ amount: payment.amount.toCents(), // Money β†’ cents
212
+ currency: payment.amount.currency.toLowerCase(),
213
+ });
214
+
215
+ // μ™ΈλΆ€ λͺ¨λΈ β†’ 도메인 λ³€ν™˜
216
+ return this.toPaymentResult(intent);
217
+ }
218
+
219
+ private toPaymentResult(intent: StripePaymentIntent): PaymentResult {
220
+ return {
221
+ success: intent.status === 'succeeded',
222
+ transactionId: TransactionId.create(intent.id),
223
+ status: this.mapStatus(intent.status),
224
+ };
225
+ }
226
+
227
+ private mapStatus(stripeStatus: string): PaymentStatus {
228
+ const mapping: Record<string, PaymentStatus> = {
229
+ 'succeeded': PaymentStatus.COMPLETED,
230
+ 'pending': PaymentStatus.PROCESSING,
231
+ 'failed': PaymentStatus.FAILED,
232
+ };
233
+ return mapping[stripeStatus] ?? PaymentStatus.UNKNOWN;
234
+ }
235
+ }
236
+ ]]></code>
237
+ </example>
238
+ </anticorruption-layer-pattern>
239
+
240
+ <event-storming>
241
+ <description>도메인 이벀트λ₯Ό μ€‘μ‹¬μœΌλ‘œ 도메인 λͺ¨λΈλ§ν•˜λŠ” μ›Œν¬μƒ΅ 기법</description>
242
+ <steps>
243
+ <step order="1">Domain Events λ„μΆœ (주황색 ν¬μŠ€νŠΈμž‡)</step>
244
+ <step order="2">Commands 식별 (νŒŒλž€μƒ‰)</step>
245
+ <step order="3">Aggregates κ·Έλ£Ήν•‘ (λ…Έλž€μƒ‰)</step>
246
+ <step order="4">Bounded Context 경계 그리기</step>
247
+ <step order="5">Context Map μž‘μ„±</step>
248
+ </steps>
249
+ <tip>도메인 전문가와 ν•¨κ»˜ μ§„ν–‰</tip>
250
+ </event-storming>
251
+
252
+ <directory-structure-by-context>
253
+ <description>Bounded Context별 μ½”λ“œ ꡬ쑰 (λͺ¨λ…Έλ ˆν¬)</description>
254
+ <structure><![CDATA[
255
+ packages/
256
+ β”œβ”€β”€ catalog/ # Catalog Context
257
+ β”‚ β”œβ”€β”€ src/
258
+ β”‚ β”‚ β”œβ”€β”€ domain/
259
+ β”‚ β”‚ β”œβ”€β”€ application/
260
+ β”‚ β”‚ └── infrastructure/
261
+ β”‚ └── package.json
262
+ β”‚
263
+ β”œβ”€β”€ order/ # Order Context
264
+ β”‚ β”œβ”€β”€ src/
265
+ β”‚ β”‚ β”œβ”€β”€ domain/
266
+ β”‚ β”‚ β”œβ”€β”€ application/
267
+ β”‚ β”‚ └── infrastructure/
268
+ β”‚ └── package.json
269
+ β”‚
270
+ β”œβ”€β”€ inventory/ # Inventory Context
271
+ β”‚ └── ...
272
+ β”‚
273
+ └── shared/ # Shared Kernel (μ΅œμ†Œν™”)
274
+ β”œβ”€β”€ src/
275
+ β”‚ β”œβ”€β”€ value-objects/ # ProductId, Money λ“± 곡유 VO
276
+ β”‚ └── events/ # 톡합 이벀트 μŠ€ν‚€λ§ˆ
277
+ └── package.json
278
+ ]]></structure>
279
+ </directory-structure-by-context>
280
+
281
+ <rules>
282
+ <category name="ν•„μˆ˜">
283
+ <must>μš©μ–΄ 사전 (Glossary) μž‘μ„± 및 μœ μ§€</must>
284
+ <must>μ½”λ“œμ—μ„œ μœ λΉ„μΏΌν„°μŠ€ μ–Έμ–΄ μ‚¬μš©</must>
285
+ <must>Bounded Context 경계 λͺ…ν™•νžˆ μ •μ˜</must>
286
+ <must>μ»¨ν…μŠ€νŠΈ κ°„ 톡신은 λͺ…μ‹œμ  μΈν„°νŽ˜μ΄μŠ€λ‘œ</must>
287
+ <must>μ™ΈλΆ€ μ‹œμŠ€ν…œ 연동 μ‹œ ACL μ‚¬μš©</must>
288
+ </category>
289
+ <category name="ꢌμž₯">
290
+ <should>Core Subdomain에 졜고 λ¦¬μ†ŒμŠ€ νˆ¬μž…</should>
291
+ <should>Generic Subdomain은 ꡬ맀/μ˜€ν”ˆμ†ŒμŠ€ ν™œμš©</should>
292
+ <should>Event Storming으둜 도메인 λͺ¨λΈλ§</should>
293
+ <should>Context Map λ¬Έμ„œν™” 및 μ΅œμ‹  μœ μ§€</should>
294
+ </category>
295
+ <category name="κΈˆμ§€">
296
+ <must-not>Bounded Context κ°„ 도메인 λͺ¨λΈ 직접 곡유</must-not>
297
+ <must-not>ν•˜λ‚˜μ˜ Aggregateκ°€ μ—¬λŸ¬ Context에 κ±ΈμΉ¨</must-not>
298
+ <must-not>기술 μš©μ–΄λ₯Ό 도메인 λͺ¨λΈμ— μ‚¬μš©</must-not>
299
+ </category>
300
+ </rules>
301
+
302
+ <checklist>
303
+ <item priority="critical">μš©μ–΄ 사전 쑴재 및 μ΅œμ‹ ν™”</item>
304
+ <item priority="critical">Bounded Context 경계 μ •μ˜λ¨</item>
305
+ <item priority="critical">μ½”λ“œκ°€ μœ λΉ„μΏΌν„°μŠ€ μ–Έμ–΄ μ‚¬μš©</item>
306
+ <item priority="high">Context Map λ¬Έμ„œν™”</item>
307
+ <item priority="high">μ™ΈλΆ€ 연동에 ACL 적용</item>
308
+ <item priority="high">Subdomain λΆ„λ₯˜ (Core/Supporting/Generic)</item>
309
+ <item priority="medium">Event Storming μ›Œν¬μƒ΅ μ§„ν–‰</item>
310
+ </checklist>
311
+ </skill>