@simplysm/sd-claude 14.0.49 → 14.0.51

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,41 @@
1
+ #!/bin/bash
2
+ # 리뷰 대상 소스 파일을 하나의 텍스트 파일로 병합
3
+ # Usage:
4
+ # merge-source.sh <출력파일> --dir <디렉토리경로>
5
+ # merge-source.sh <출력파일> --files <파일1> <파일2> ...
6
+
7
+ output="$1"
8
+ shift
9
+
10
+ if [ -z "$output" ]; then
11
+ echo "Usage: $0 <output-file> --dir <dir-path> | --files <file1> <file2> ..." >&2
12
+ exit 1
13
+ fi
14
+
15
+ mkdir -p "$(dirname "$output")"
16
+
17
+ mode="$1"
18
+ shift
19
+
20
+ files=""
21
+ if [ "$mode" = "--dir" ]; then
22
+ dir_path="$1"
23
+ if [ -z "$dir_path" ]; then
24
+ echo "Error: --dir requires a directory path" >&2
25
+ exit 1
26
+ fi
27
+ files=$(find "$dir_path/src" -name "*.ts" 2>/dev/null | sort)
28
+ [ -d "$dir_path/scss" ] && files+=$'\n'$(find "$dir_path/scss" -name "*.scss" 2>/dev/null | sort)
29
+ elif [ "$mode" = "--files" ]; then
30
+ files=$(printf '%s\n' "$@")
31
+ else
32
+ echo "Error: specify --dir or --files" >&2
33
+ exit 1
34
+ fi
35
+
36
+ echo "$files" | while IFS= read -r f; do
37
+ [ -z "$f" ] && continue
38
+ echo "=== $f ==="
39
+ cat "$f"
40
+ echo
41
+ done > "$output"
@@ -175,8 +175,8 @@ Gherkin Scenarios를 구현 단위(Slice)로 분할한다.
175
175
  # Feature {번호} {이름}
176
176
 
177
177
  ## 참조 자료
178
- - WBS: [{wbs 파일명}]({wbs 문서 상대경로})
179
- {수집한 구체적 정보}
178
+ - WBS: @{wbs 문서 상대경로}
179
+ {수집한 구체적 정보 — 참조 파일 경로는 `@path` 형태로 기록}
180
180
 
181
181
  ## 기준 코드
182
182
  {Step 4에서 작성한 기준 코드 섹션}
@@ -1,96 +1,28 @@
1
1
  ---
2
2
  name: sd-tdd
3
- description: 구현계획(plan) 기반으로 TDD 개발하는 스킬. "TDD 개발", "테스트 주도 개발", "plan 기반 구현" 등을 요청할 때 사용한다.
3
+ description: 구현계획(plan) 기반으로 TDD 개발하는 스킬
4
4
  effort: low
5
5
  ---
6
6
 
7
7
  # sd-tdd: TDD 개발
8
8
 
9
- Feature 문서(요구명세 + 구현계획)를 기반으로, Double Loop TDD로 코드를 구현한다.
9
+ Feature 문서(구현계획)를 기반으로, Double Loop TDD로 코드를 구현한다.
10
10
 
11
- 개발 프로세스: Feature 분해 → Feature 설계**TDD 개발**
11
+ 개발 프로세스: WBS 문서 작성 → Feature 문서 작성 → TDD 개발
12
12
 
13
- ## 공통 규칙
13
+ ## 기본 원칙
14
14
 
15
15
  ### 기술적 장벽 처리
16
16
 
17
- **CRITICAL: 설계 결정은 사용자가 확정한 구속력 있는 결정이다. 임의로 변경·축소·제외하는 것은 절대 금지다.**
17
+ 설계 결정은 사용자가 확정한 구속력 있는 결정이다. 임의로 변경·축소·제외하는 것은 절대 금지다.
18
18
 
19
- 구현 중 설계 결정을 그대로 구현할 수 없는 기술적 어려움을 발견하면,
20
- 1. **해당 구현을 중단**한다.
21
- 2. `.claude/rules/sd-options.md` 의 지침에 따라, 사용자에게 대안을 제시한다.
22
- 3. 사용자가 결정하면
23
- - 해당 결정을 Feature 문서의 `### 설계 결정` 섹션에 역방향 피드백으로 기록한다
24
- - 결정에 따라 구현계획을 갱신한다
25
- - 갱신된 Feature 문서로 구현을 재개한다
19
+ 구현 중 그대로 실현할 수 없는 기술적 장벽을 발견하면:
26
20
 
27
- 설계 결정의 변경은 사용자만이 수 있다.
28
- sd-tdd는 설계를 그대로 구현하는 역할이므로, 문서(요구명세·구현계획·설계 결정)를 사용자 결정 없이 독자적으로 변경하지 않는다(NEVER).
21
+ 1. 구현을 중단하고, 사용자에게 대안을 제시한다.
22
+ 2. 사용자의 결정을 Feature 문서의 `### 설계 결정` 섹션에 기록한다.
23
+ 3. 결정에 맞춰 구현계획을 갱신한 뒤, 갱신된 Feature 문서로 구현을 재개한다.
29
24
 
30
- ### 테스트 분류 기준
31
-
32
- **CRITICAL: 판단 단위는 Scenario가 아니라 검증 항목이다.**
33
- Scenario에 하드웨어 의존이 하나라도 있다고 Scenario 전체를 수동 테스트로 분류하는 것은 금지다.
34
-
35
- Scenario의 검증 항목을 분해하고, 각 항목별로 분류한다:
36
-
37
- | 분류 | 기준 | 산출물 |
38
- | --------------- | -------------------------------------------------------------------------- | ------------------------ |
39
- | **자동 테스트** | import하여 호출·실행·단언 가능 (순수 함수, 상태 변경, 분기 로직 등) | `.spec.ts` → TDD |
40
- | **LLM 검증** | 자동 테스트는 불가하지만 LLM이 코드 읽기·명령 실행·설정 확인으로 검증 가능 | `.verify.md` → 즉시 수행 |
41
- | **수동 검증** | 실제 하드웨어·물리적 UI 조작 등 LLM이 절대 수행할 수 없는 항목 | `.spec.md` → 문서화 |
42
-
43
- 하나의 Scenario에서 3종류가 모두 나올 수 있다. 각 분류에 해당하는 항목이 없으면 해당 파일은 생성하지 않는다.
44
-
45
- #### LLM 검증 문서 (.verify.md)
46
-
47
- 자동 테스트(`.spec.ts`)로는 검증할 수 없지만, LLM이 코드 읽기·명령 실행·설정 확인 등으로 직접 검증할 수 있는 항목을 다룬다.
48
- 콜백 등록 코드의 정확성, 설정 파일 값, 에러 핸들링 경로 존재 여부, 타입 정합성 등이 해당한다.
49
-
50
- `.verify.md`는 `.spec.ts`와 동급의 테스트 산출물이다. `.spec.ts`처럼 **검증 항목 명세**만 담고, 실행 결과는 파일에 기록하지 않는다 — 결과는 sd-check 등 검증 도구가 실행 시점에 산출한다. 본 문서에서 "테스트 실행"은 `.spec.ts` 실행과 `.verify.md` 검증을 모두 포함한다.
51
-
52
- **수행 절차:**
53
- 1. 검증 항목 중 "LLM 검증" 분류 항목을 `.verify.md`에 명세로 작성한다.
54
- 2. 각 항목을 순회하며 검증을 수행한다 — 코드를 읽고, 명령을 실행하고, 결과를 확인한다.
55
- 3. 문제 발견 시 코드를 수정한다. 통과 항목은 문서에 별도로 기록하지 않는다.
56
-
57
- ```markdown
58
- # {Scenario 제목} — LLM 검증
59
-
60
- ## 검증 항목
61
-
62
- - {항목}: {검증 방법 / 기대 결과}
63
- - {항목}: {검증 방법 / 기대 결과}
64
- ```
65
-
66
- #### 수동 테스트 문서 (.spec.md)
67
-
68
- 실제 하드웨어 연결, 물리적 UI 조작 등 LLM이 절대 수행할 수 없는 항목만 포함한다.
69
-
70
- ```markdown
71
- # {Scenario 제목} — 수동 검증
72
-
73
- ## 전제 조건
74
-
75
- - {테스트 전 필요한 상태/환경}
76
-
77
- ## 수행 절차
78
-
79
- 1. {사용자가 수행할 단계}
80
- 2. ...
81
-
82
- ## 기대 결과
83
-
84
- - {관찰되어야 하는 결과}
85
- ```
86
-
87
- ### 테스트 파일 네이밍 및 배치
88
-
89
- - **CRITICAL: Slice/Scenario 번호를 파일명에 사용하지 않는다** — `3.1-bootstrap.spec.ts` (X), `bootstrap.spec.ts` (O)
90
- - 파일명은 테스트 대상 모듈/클래스/함수 이름을 기반으로 한다
91
- - 기존 테스트 디렉토리 구조를 따른다 — 프로젝트에 `tests/` 디렉토리가 있으면 그 하위에 소스 구조를 미러링한다 (예: `packages/{pkg}/tests/{category}/{대상}.spec.ts`)
92
- - 기존 테스트가 없거나 테스트 구성 지침이 없으면, 같은 모노레포의 다른 패키지 테스트 구조를 참고한다. 그마저도 없으면 `.claude/skills/sd-tdd/references/test-setup.md`를 참고한다
93
- - Acceptance Test: `{대상}.acc.spec.ts`, Unit Test: `{대상}.spec.ts`
25
+ 설계 결정 변경 권한은 사용자에게만 있다. sd-tdd는 설계를 그대로 구현하는 역할이므로, 문서(요구명세·구현계획·설계 결정)를 사용자 결정 없이 독자적으로 변경하지 않는다.
94
26
 
95
27
  ## Step 1: Feature 문서 읽기 + 코드베이스 탐색
96
28
 
@@ -98,14 +30,13 @@ Scenario의 검증 항목을 분해하고, 각 항목별로 분류한다:
98
30
 
99
31
  Feature 문서 경로가 필수이다. 입력되지 않았거나 대화로 유추할 수 없다면 `/sd-plan`을 안내하고 종료한다.
100
32
 
101
- Feature 문서의 `## 참조 자료` 섹션 및 그 하위섹션을 반드시 함께 읽는다.
33
+ Feature 문서 및 WBS 문서의 `## 참조 자료` 섹션 및 그 하위섹션을 반드시 함께 읽는다.
102
34
 
103
35
  - wbs.md의 참조 자료 섹션도 모두 읽는다. 참조 자료의 구체적 정보(업무 규칙, 데이터 형식, 기술 제약 등)를 구현에 반영한다.
104
36
 
105
37
  ### 1-2. 기준 코드 탐색·확인
106
38
 
107
- **CRITICAL: 코드가 source of truth이다.** 문서가 아닌 실제 코드를 기준으로 파악한다.
108
- **CRITICAL: 탐색 없이 코드 작성으로 넘어가는 것은 위반이다.**
39
+ **CRITICAL: 코드가 source of truth이다.** 문서가 아닌 실제 코드를 기준으로 파악하며, 탐색 없이 코드 작성으로 넘어가지 않는다.
109
40
 
110
41
  #### (a) Feature 문서의 `## 기준 코드` 섹션 활용
111
42
 
@@ -114,8 +45,6 @@ Feature 문서의 `## 참조 자료` 섹션 및 그 하위섹션을 반드시
114
45
 
115
46
  #### (b) 섹션이 없거나 부실할 때 — 직접 탐색
116
47
 
117
- sd-plan Step 4 절차를 그대로 수행한다:
118
-
119
48
  - 코드베이스에서 이 Feature와 가장 유사한 기존 기능을 찾는다.
120
49
  - 탐색 대상: 관련 엔티티/모델, 기존 API 엔드포인트 및 패턴, 사용 중인 프레임워크·아키텍처, 기존 시스템 연동 방식, 성능/보안 제약, 관련 테스트 구조, 관련 의존성과 설정.
121
50
  - 발견한 항목을 `파일경로:라인번호` 인용과 함께 작업 메모로 정리한다. 인용 없는 항목은 신뢰하지 않는다.
@@ -135,22 +64,46 @@ sd-plan Step 4 절차를 그대로 수행한다:
135
64
 
136
65
  ### 1-3. 문서 정합성 확인
137
66
 
138
- 요구명세의 각 Scenario에서 참조하는 기능·메서드를 구현계획과 대조한다.
139
- `/sd-inner-clarify` 스킬을 호출하여 누락된 기능등 불명확한 부분을 명확화한다.
67
+ 요구명세의 각 Scenario에서 참조하는 기능·메서드를 구현계획과 대조한다. 다음과 같은 불명확한 부분이 있으면 `/sd-inner-clarify` 스킬을 호출하여 명확화한다:
140
68
 
141
- ## Step 2: Double Loop TDD
69
+ - Scenario가 참조하는 메서드/기능이 구현계획의 Slice에 등장하지 않음
70
+ - 요구명세의 용어와 구현계획의 용어가 서로 다름 (동일 개념을 다른 이름으로 지칭)
71
+ - Scenario의 단언 대상(에러 메시지·상태값·반환 형식 등)이 구현계획에 구체화되지 않음
142
72
 
143
- 테스트 작성 @.claude/references/sd-testing.md 읽고 따른다. (특히, mock 남발 금지)
73
+ ## Step 2: Double Loop TDD
144
74
 
145
75
  구현계획의 Slice 순서대로 진행한다. 각 Slice 내에서 Scenario를 하나씩 처리한다.
146
- 각 Scenario 시작 시 **테스트 분류 기준**에 따라 검증 항목을 분류한다.
147
76
  테스트 실행 명령은 CLAUDE.md를 참조한다.
148
77
 
78
+ ### 2-0. 테스트 파일 규칙
79
+
80
+ - 파일명은 테스트 대상 모듈/클래스/함수 이름을 기반으로 한다
81
+ - 기존 테스트 디렉토리 구조를 따른다 — 프로젝트에 `tests/` 디렉토리가 있으면 그 하위에 소스 구조를 미러링한다 (예: `packages/{pkg}/tests/{category}/{대상}.spec.ts`)
82
+ - 기존 테스트가 없거나 테스트 구성 지침이 없으면, 같은 모노레포의 다른 패키지 테스트 구조를 참고한다. 그마저도 없으면 `.claude/skills/sd-tdd/references/test-setup.md`를 참고한다
83
+ - Acceptance Test: `{대상}.acc.spec.ts`, Unit Test: `{대상}.spec.ts`
84
+
149
85
  ### 2-1. Acceptance Test 작성 (Red)
150
86
 
151
- **CRITICAL: 프로젝트에 이미 존재하는 기존 테스트부터 점검하고 선수정한다.**
87
+ #### Acceptance Test 제약 (CRITICAL)
88
+
89
+ Acceptance Test는 Feature의 **외부 계약**을 도메인 언어로 검증한다. 아래 제약을 모두 지킨다.
152
90
 
153
- #### 점검·선수정 절차
91
+ **작성 룰**
92
+
93
+ 1. **도메인 용어만 사용** — Scenario의 Given/When/Then에 등장하는 업무 용어·Public API만 참조. 내부 클래스·private 메서드·파일 경로 등장 금지.
94
+ 2. **Public 진입점으로만 호출** — 외부에서 실제 호출되는 방식 그대로(API 엔드포인트·서비스 공개 메서드·UI 이벤트 등). 내부 헬퍼 직접 호출 금지.
95
+ 3. **구현 세부 검증 금지** — 내부 상태 값, 메서드 호출 횟수, private 필드 스냅샷 등 단언 금지. "관찰 가능한 결과"(반환값·외부 영향·공개 상태)만 단언한다.
96
+
97
+ **생명주기 룰**
98
+
99
+ 4. **삭제·축소 금지** — Inner Loop에서 실패해도 Acceptance Test를 지우거나 단언을 약화시켜 통과시키는 것은 절대 금지(NEVER). 구현을 고치거나, 설계 장벽이면 `기술적 장벽 처리`에 따라 에스컬레이션한다.
100
+ 5. **리팩토링 불변** — Outer Loop Refactor에서 Acceptance Test는 수정 대상이 아니다. 수정이 필요하면 스펙 변경 신호이므로 중단하고 사용자에게 보고한다.
101
+
102
+ (스펙 자체가 변경된 경우에만 Feature 문서 갱신 후 Acceptance Test 수정 가능)
103
+
104
+ #### 기존 테스트 점검·선수정
105
+
106
+ **새 Acceptance Test를 작성하기 전, 프로젝트에 이미 존재하는 기존 테스트부터 점검하고 선수정한다.**
154
107
 
155
108
  1. 이번 Scenario의 대상 모듈/함수를 import하는 기존 테스트 파일을 검색한다.
156
109
  2. 각 테스트를 3분류로 판정한다:
@@ -160,6 +113,8 @@ sd-plan Step 4 절차를 그대로 수행한다:
160
113
  3. 판단이 모호하면 `/sd-inner-clarify`로 명확화한다.
161
114
  4. 선수정이 끝난 뒤 새 Acceptance Test 작성으로 진행한다.
162
115
 
116
+ #### Gherkin Scenario 변환
117
+
163
118
  Gherkin Scenario의 Given/When/Then을 프로젝트 테스트 프레임워크의 Acceptance Test로 변환한다.
164
119
 
165
120
  - Scenario 하나를 하나의 test 함수로 변환하되, Scenario 내 여러 When/Then이 있으면 하나의 test 함수 안에서 순차 검증한다(통합 수준).
@@ -177,6 +132,15 @@ Acceptance Test를 통과시키기 위해 **반드시 Unit Test를 먼저 작성
177
132
  - Acceptance Test가 통합 수준(Scenario 전체 흐름)이면, Unit Test는 각 개별 메서드/동작을 별도 test로 분리한다
178
133
  - **단일 메서드 호출이더라도 Inner Loop를 생략하지 않는다** — Acceptance Test에 없는 추가 케이스(경계값, 에러, 빈 입력 등)를 최소 1개 작성한다
179
134
 
135
+ #### Unit Test 허용 범위 (Acceptance와 비대칭)
136
+
137
+ Unit Test는 내부 설계를 검증한다. Acceptance와 달리 아래가 허용된다:
138
+
139
+ - 내부 클래스·private 메서드·내부 헬퍼 직접 import·호출 가능
140
+ - 구현 변경에 따라 **수정·삭제·재작성 자유** — 구현이 바뀌면 Unit Test도 따라 바뀐다
141
+ - 경계값·에러·빈 입력 등 구현 관점 케이스를 자유롭게 추가
142
+ - 내부 상태·호출 횟수 등 구현 세부 단언 가능 (단, Step 3-1 정리에서 "구현 결합 테스트"로 재검토 대상)
143
+
180
144
  #### 절차
181
145
 
182
146
  1. **Unit Test 작성 (Red)** — Acceptance Test와 별개의 도구 호출(Write/Edit)로 작성한다
@@ -185,7 +149,7 @@ Acceptance Test를 통과시키기 위해 **반드시 Unit Test를 먼저 작성
185
149
  - 방금 작성한 코드 범위에서 중복 제거, 하드코딩 제거(Fake It → 실제 구현), 네이밍 개선, Extract Variable/Method.
186
150
  - 모듈·아키텍처 수준 정리는 여기서 하지 않는다 — 그것은 `Outer Loop Refactor`의 영역이다.
187
151
 
188
- Acceptance Test가 성공할때 까지 반복한다.
152
+ Acceptance Test가 성공할 때까지 반복한다.
189
153
 
190
154
  ### 2-3. Outer Loop Refactor
191
155
 
@@ -220,12 +184,6 @@ Feature 전체의 테스트 코드를 "지속적 회귀 테스트로 남길 가
220
184
 
221
185
  - **중복 Unit Test 삭제** — `test`/`it` 단위로 판단한다. Acceptance Test와 검증 대상·입력값이 동일한 개별 테스트 케이스만 삭제한다(파일 단위 삭제 금지). Scenario 간 중복 포함
222
186
  - **구현 결합 테스트 전환/삭제** — 내부 상태 값, 메서드 호출 횟수 등 구현 세부사항을 검증하는 테스트
223
- - **TDD 중 쓴 mock 정리** — TDD Red 단계에서 의존성이 미구현이라 부득이 작성한 mock은, 실제 구현이 완성된 이 시점에 **전부 재점검**한다. 절차:
224
- 1. 테스트 파일의 모든 `vi.mock()`/`vi.spyOn()`/수동 mock 객체를 나열한다
225
- 2. 각 mock이 @.claude/references/sd-testing.md 의 화이트리스트(외부 네트워크·DB·하드웨어/OS·타이머)에 해당하는지 확인한다
226
- 3. 해당하지 않으면 실제 인스턴스로 교체한다. 호출 추적이 목적이었다면 `vi.spyOn()`으로만 남긴다
227
-
228
- 남겨두면 실제 코드 변경에 반응하지 않아 false positive를 양산하고, 리팩토링 시 한꺼번에 터진다.
229
187
  - **공통 setup 추출** — 중복되는 arrange 코드를 헬퍼/beforeEach로
230
188
  - **테스트 네이밍 개선** — 검증하는 동작을 문장처럼 읽히게
231
189
 
@@ -77,13 +77,44 @@ Feature는 독립적으로 설계·개발·검증할 수 있는 최소 **기능*
77
77
  - **나쁜 예:** Feature 1 "인증 토큰 발급(서버)" / Feature 2 "로그인 화면(클라이언트)" — 서버/클라이언트 분할 금지
78
78
  - **나쁜 예:** Feature 1 "도서 관리(관리자)" / Feature 2 "도서 조회(직원)" — 동일 도메인("도서")을 Actor(역할)로 분할 금지. "도서 관리" 단일 Feature에 관리자 화면 + 직원 화면을 함께 포함
79
79
 
80
+ ### Feature 분리 규칙
81
+
82
+ 하나의 Feature가 과도하게 클 때, **기능 범위 기반으로** 분리한다. 분리된 각 Feature는 여전히 end-to-end 수직 슬라이스(DB→서비스→UI)를 유지해야 한다.
83
+
84
+ **분리 판단 기준** — 아래 중 하나라도 해당하면 분리를 검토한다:
85
+
86
+ - 범위 항목이 대략 7개를 초과하는 경우
87
+ - 기본 동작과 확장 동작이 자연스럽게 구분되는 경우 (예: CRUD vs 통계/추천/일괄처리)
88
+ - 독립적으로 구현·검증 가능한 하위 단위로 나뉘는 경우
89
+
90
+ **분리 방법 — 기본→확장 패턴:**
91
+
92
+ 하나의 기능을 **기본 기능**(핵심 동작)과 **확장 기능**(부가 동작)으로 나눈다.
93
+
94
+ - 네이밍: `{기능명} — {범위 설명}` (예: "도서 관리 — 기본 CRUD", "도서 관리 — 추천/통계")
95
+ - 확장 Feature는 기본 Feature에 대한 의존성을 명시한다
96
+ - 기본 Feature만으로 해당 기능의 핵심 동작이 완성되어야 한다
97
+
98
+ **좋은 예:**
99
+
100
+ - Feature 1.1: 사용자 관리 — 기본 CRUD (등록, 수정, 삭제, 목록/검색)
101
+ - Feature 1.2: 사용자 관리 — 권한/역할 관리 (← 1.1에 의존)
102
+
103
+ **나쁜 예 (레이어 분리 — 금지):**
104
+
105
+ - Feature 1.1: 사용자 관리 — DB 스키마
106
+ - Feature 1.2: 사용자 관리 — API
107
+ - Feature 1.3: 사용자 관리 — UI
108
+
109
+ ### Feature 병합 규칙
110
+
80
111
  **CRITICAL: 기계적 판정 규칙 (예외 없음, 재량 금지)**
81
112
 
82
- 아래 중 **하나라도** 해당하면 무조건(MUST) 해당 Feature들을 단일 Feature로 병합한다. "기능 성격이 다르다", "CRUD와 조회는 다르다" 같은 자체 합리화는 금지(NEVER)한다.
113
+ 아래 중 **하나라도** 해당하면 무조건(MUST) 해당 Feature들을 단일 Feature로 병합한다.
83
114
 
84
115
  1. **Feature 이름에 Actor(역할)가 괄호/접미사로 포함된 경우** (예: "(관리자)", "(직원)", "(고객)", "관리자용", "직원용")
85
- 2. **동일 도메인 엔터티(예: "도서", "대출")에 대한 기능이 서로 다른 Feature에 나뉘어 등장하는 경우**
86
- 3. **두 Feature의 범위에 동일하거나 유사한 기능명**(예: "도서 목록 조회", "주문 목록 조회")**이 각각 등장하는 경우** (뷰/필터/권한 차이는 병합 사유를 면제하지 않는다)
116
+ 2. **동일 도메인 엔터티에 대해 아키텍처 레이어별로 Feature를 분할한 경우** (예: "도서 DB 스키마", "도서 API", "도서 UI")
117
+ 3. **두 Feature의 범위에 동일하거나 유사한 기능명이 각각 등장하는 경우** (예: "도서 목록 조회", "주문 목록 조회") 뷰/필터/권한 차이는 병합 사유를 면제하지 않는다
87
118
 
88
119
  그 외 규칙:
89
120
 
@@ -94,20 +125,30 @@ Feature는 독립적으로 설계·개발·검증할 수 있는 최소 **기능*
94
125
 
95
126
  ### Feature 작성 규칙
96
127
 
97
- - **의존성**: 이 Feature 범위(세부 기능)를 **하나씩** 검토하여, 다른 Feature와의 아래 두 종류 관계를 모두 판단한다.
98
- - **(1) 산출물 참조 관계 (코드 연결성)**: 이 Feature의 세부 기능이 다른 Feature가 만든 산출물(API, 컴포넌트, 데이터 구조, 서비스, 모듈 등)을 직접 사용한다면 그 Feature에 의존.
99
- - **(2) 결정 파급 관계 (CRITICAL, 자주 누락됨)**: 다른 Feature에서 내려지는 결정(명명 규칙, 공통 패턴, 레시피 스타일, 분기 구조, 공유 용어 등)이 확정되지 않으면 이 Feature의 **내용이 변할 수 있다면** 그 Feature에 의존.
100
- - 예시: Feature A가 "페이지/모달 분기 패턴"을 정립하고 Feature B 레시피가 그 분기를 **복붙**해서 쓴다면, A의 분기 구조가 바뀔 때 B 레시피도 변함 → B는 A에 의존
101
- - 예시: Feature A가 "레시피 작성 관용 규칙"을 정립하고 Feature B가 그 규칙을 따라야 한다면 → B는 A에 의존
102
- - 예시: Feature A에서 API 이름을 확정하고 Feature B가 그 이름을 문서·레시피에 인용한다면 → B는 A에 의존
103
- - "코드 간 import 없음"이 의존성 없음을 의미하지 **않는다**. 결정 파급은 signal 수준보다 **문서·규칙·명명 수준**에서 더 흔히 발생한다.
104
- - **판단 절차**: 세부 기능마다 "이것을 구현·기술하려면 다른 Feature가 먼저 완료되어야 하는가? 혹은 다른 Feature의 결정이 내 내용을 바꿀 수 있는가?"를 질문하고, 해당하는 Feature 번호를 명시한다.
105
- - **불확실하면 의존성 있음으로 판단한다** — 의존성 누락은 병렬 수행 시 블로킹·재작업을 유발하므로, 과소 설정보다 과대 설정이 안전하다.
106
- - 형식: Feature 번호만 쉼표로 나열 (예: `1.1, 2.3`) 또는 `없음`
107
- - (의존성에 따라 병렬 진행할 수 있으므로 **CRITICAL**)
128
+ Feature에는 다음 4가지를 반드시 포함한다.
129
+
108
130
  - **범위**: 해당 Feature의 세부 기능을 사용자 기능(user-facing function) 수준으로 **MUST 빠짐없이** 나열한다. (절대 누락되어선 안된다. 누락된것은 다음단계에서 제외사항으로 판단될 수 있다.)
109
131
  - **경계**: 이 Feature가 **하지 않는 것** 중 다른 Feature에서 다루거나 다룰 수 있는 것을 명시한다. 인접 Feature와의 경계가 모호한 경우 특히 중요하다. (프로젝트 전체에서 제외되는 항목은 문서 하단의 "제외 사항"에 기재한다.)
110
132
  - **근거**: 범위가 도출된 근거 혹은 출처를 명시한다. (요구사항, 사용자 답변, 첨부문서 등) 개발에 필요한 참조 파일/자료 경로가 있으면 확인 목적과 함께 기록한다.
133
+ - **의존성**: 다른 Feature와의 의존 관계를 기록한다. 형식: Feature 번호만 쉼표로 나열 (예: `1.1, 2.3`) 또는 `없음`. 의존성에 따라 병렬 진행할 수 있으므로 정확한 판단이 **CRITICAL**.
134
+
135
+ #### 의존성 판단 절차
136
+
137
+ 의존성은 아래 두 종류를 모두 판단한다:
138
+
139
+ - **(1) 산출물 참조 관계**: 이 Feature의 세부 기능이 다른 Feature가 만든 산출물(API, 컴포넌트, 데이터 구조, 서비스, 모듈 등)을 직접 사용하는 경우.
140
+ - **(2) 결정 파급 관계**: 다른 Feature에서 내려지는 결정(명명 규칙, 공통 패턴, 분기 구조, 공유 용어 등)이 확정되지 않으면 이 Feature의 내용이 변할 수 있는 경우.
141
+ - "코드 간 import 없음"이 의존성 없음을 의미하지 **않는다**. 결정 파급은 문서·규칙·명명 수준에서 더 흔히 발생한다.
142
+
143
+ **판단 절차 (후보 풀 → 제거 방식):**
144
+
145
+ 1. **후보 풀 전수 나열**: 다른 모든 Feature 번호를 후보로 나열한다.
146
+ 2. **제거 판단**: 각 후보에 대해 "이 Feature와 완전히 무관함을 증명할 수 있는가?" 산출물·결정·도메인 엔터티 중 하나라도 겹칠 가능성이 있으면 제거하지 않는다.
147
+ 3. **잔류 = 의존성**: 제거되지 않은 번호를 의존성에 기록한다.
148
+
149
+ **CRITICAL: 기본값은 "의존 있음". 판단이 머뭇거려지면 잔류시킨다.** 과소 설정(병렬 수행 시 블로킹·재작업)이 과대 설정(약간의 직렬화)보다 비용이 크다.
150
+
151
+ ---
111
152
 
112
153
  각 Feature의 세부 사항을 분류하고 `/sd-inner-clarify` 스킬을 호출하여 불명확한 부분을 명확화한다.
113
154
  - **나쁜 예:** "예약 기능 포함이 결정됨 → 예약 세부규칙(대기열 정책, 미대출 시 자동취소 기간)도 확정" — 기능 포함 여부와 세부 수치/규칙은 별개다. 세부사항은 별도로 분류·질문한다.
@@ -129,28 +170,25 @@ Feature를 작성하다가 "이건 시스템 동작상 당연히 필요하다"
129
170
 
130
171
  ## Step 4: 자가검증 (Self-Refine)
131
172
 
132
- 1차 산출된 WBS를 아래 6가지 관점으로 **MUST** 재검토하고, 필요 시 **분할·병합·순서조정·보완**을 수행하여 최종 WBS를 확정한다. 이 단계는 pass/fail 판정이 아니라 **정제(refine)**이며, 문제가 보이면 즉시 수정한다.
173
+ 1차 산출된 WBS를 아래 관점으로 **MUST** 재검토하고, 필요 시 **분할·병합·순서조정·보완**을 수행하여 최종 WBS를 확정한다.
133
174
 
134
175
  ### 검증 관점
135
176
 
136
- 1. **Feature 크기** — Feature 과도하게 크거나 여러 목적을 겸하면 **분할**한다.
137
- - 기준: 범위 항목이 지나치게 많거나(대략 7개 초과), 독립적으로 구현·검증 가능한 하위 단위로 자연스럽게 나뉘면 분할 후보.
138
- 2. **의존성에 의한 순서** 의존 대상이 번호에 있거나 같은 단계 내에 있으면 **번호/순서를 재정렬**한다.
139
- 3. **Feature 중복/누락/역추적** — 요구사항(Impact Mapping의 Deliverable) 대비 **양방향** 커버리지를 점검한다.
177
+ 1. **Feature 크기** — Step 3 "Feature 분리 규칙"의 판단 기준에 따라, 과도하게 Feature는 기본→확장 패턴으로 분할한다.
178
+ 2. **의존성 순서** 의존 대상이 번호에 있거나 같은 단계 내에 있으면 번호/순서를 재정렬한다.
179
+ 3. **중복/누락/역추적**요구사항(Impact Mapping의 Deliverable) 대비 양방향 커버리지를 점검한다.
140
180
  - 여러 Feature가 같은 세부 기능을 중복으로 갖고 있으면 하나로 통합하거나 경계를 명확화.
141
181
  - **정방향 (Deliverable → Feature):** 요구사항에 있는데 어떤 Feature에도 없는 항목은 추가하거나 "제외 사항"으로 사유와 함께 이동.
142
- - **역방향 (Feature → Deliverable):** 모든 Feature가 Impact Mapping의 Deliverable로 역추적 가능한지 확인한다. 역추적 불가한 Feature 있으면 Step 3 "Feature-Deliverable 역추적 강제 규칙"에 따라 `/sd-inner-clarify`로 질문 후 처리한다. 임의 유지 금지(NEVER).
143
- 4. **Feature 독립성 (단일 책임)** — 한 Feature의 이름과 실제 범위가 일치하는지 확인한다. 이름에 드러나지 않는 일을 몰래 수행하면 **분리**한다.
144
- 5. **명명/범위 일관성** — Feature 경계가 Epic 분류 기준(사용자 관점 기능 영역)과 정합하는지 확인한다. 다음 케이스는 end-to-end 기능 단위로 **병합**한다.
145
- - Feature가 아키텍처 레이어(DB/서비스/UI, 서버/클라이언트 등)로 쪼개져 있는 경우
146
- - **동일 도메인 기능이 Actor(역할) 기준으로 여러 Feature에 분할되어 있는 경우** (예: "도서 관리(관리자)" + "도서 조회(직원)"는 "도서 관리" 단일 Feature로 병합하여 관리자 화면 + 직원 화면을 함께 포함). 범위에 동일 기능(예: "도서 목록 조회")이 뷰/권한만 다르게 다른 Feature에 반복 등장하면 이 케이스이다.
182
+ - **역방향 (Feature → Deliverable):** 역추적 불가한 Feature Step 3 "Feature-Deliverable 역추적 강제 규칙"에 따라 처리한다.
183
+ 4. **Feature 독립성 (단일 책임)** — 한 Feature의 이름과 실제 범위가 일치하는지 확인한다. 이름에 드러나지 않는 일을 몰래 수행하면 분리한다.
184
+ 5. **병합 규칙 준수** Step 3 "Feature 병합 규칙"의 기계적 판정 규칙을 위반하는 Feature가 없는지 확인한다.
147
185
  6. **범위 항목의 검증 가능성** — 각 세부 기능이 완료 판정 가능한 구체적 표현인지 확인한다. "개선한다", "최적화한다" 같은 모호한 표현은 구체 동작으로 재작성한다.
148
186
 
149
187
  ### 의존성 매트릭스
150
188
 
151
- 위 2번 검증을 위해 의존성 매트릭스를 생성하고 다음을 확인한다:
189
+ 의존성 매트릭스를 생성하고 다음을 확인한다:
152
190
 
153
- 1. **누락 확인**: 각 Feature의 세부 기능이 (a) 다른 Feature의 산출물을 참조하거나 (b) 다른 Feature의 결정(명명·공통 패턴·레시피 스타일·분기 구조 등)에 의해 내용이 바뀔 수 있는데 의존성에 빠져 있지 않은지 크로스체크
191
+ 1. **누락 확인**: 각 Feature의 세부 기능이 다른 Feature의 산출물을 참조하거나 결정에 의해 내용이 바뀔 수 있는데 의존성에 빠져 있지 않은지 크로스체크
154
192
  2. **순환 확인**: A→B→A 같은 순환 의존성이 없는지 확인 (발견 시 Feature 분할로 해소)
155
193
  3. **1단계 존재 확인**: 의존성이 없는 Feature가 최소 1개 이상 존재하는지 확인
156
194
 
package/docs/assets.md CHANGED
@@ -18,7 +18,7 @@ claude/
18
18
 
19
19
  ## `claude/skills/`
20
20
 
21
- 다수의 스킬 디렉토리. `sd-*` 접두어 스킬은 postinstall로 소비 프로젝트에 배포되고, `my-apk-decompile/`·`playwright-cli/` 같이 `sd-*`로 시작하지 않는 스킬은 모노레포 로컬 전용이다. 각 스킬은 아래 파일을 포함한다:
21
+ 다수의 스킬 디렉토리. `sd-*` 접두어 스킬은 postinstall로 소비 프로젝트에 배포된다. `sd-*`로 시작하지 않는 스킬(예: `my-apk-decompile/`, `playwright-cli/`)은 모노레포 루트 `.claude/`에만 존재하고 배포 에셋에는 포함되지 않는 로컬 전용 스킬이다. 각 스킬은 아래 파일을 포함한다:
22
22
 
23
23
  | 파일 | 필수 | Description |
24
24
  | --------------- | ---- | ------------------------------------------------------------ |
@@ -30,8 +30,6 @@ claude/
30
30
 
31
31
  | 디렉토리 | 스킬 이름 | Description |
32
32
  | --------------------- | ------------------ | -------------------------------------------------- |
33
- | `my-apk-decompile/` | my-apk-decompile | APK 파일 디컴파일 및 소스코드 분석 |
34
- | `playwright-cli/` | playwright-cli | 브라우저 자동화 및 Playwright 테스트 |
35
33
  | `sd-check/` | sd-check | typecheck/lint/test 실행 및 에러 해결 |
36
34
  | `sd-claude-docs/` | sd-claude-docs | CLAUDE.md + usage 문서 동시 생성 |
37
35
  | `sd-commit/` | sd-commit | 전체 변경사항에 대한 단일 커밋 생성 |
@@ -39,6 +37,7 @@ claude/
39
37
  | `sd-deliverable/` | sd-deliverable | 매뉴얼/SIT 문서 생성 |
40
38
  | `sd-dev/` | sd-dev | 통합 개발 오케스트레이터 (요구명세 → TDD → 리뷰) |
41
39
  | `sd-doc-extract/` | sd-doc-extract | 문서 파일 텍스트/이미지 추출 (Python) |
40
+ | `sd-inner-clarify/` | sd-inner-clarify | (내부 전용) 명확성 분류·근거 탐색·명확화 질문 |
42
41
  | `sd-inner-debug/` | sd-inner-debug | (내부 전용) 근본 원인 분석(ACH) 로직 |
43
42
  | `sd-inner-review/` | sd-inner-review | (내부 전용) 코드 리뷰 분석 로직 |
44
43
  | `sd-issue/` | sd-issue | GitHub 이슈 생성 |
@@ -77,22 +76,15 @@ Claude Code 규칙 파일. 2개 파일.
77
76
 
78
77
  ## `claude/references/`
79
78
 
80
- 스킬과 규칙에서 참조하는 공유 문서. 규칙 파일에서 `Read tool로 읽으라`는 지시로 참조된다. 공유 문서(md파일)와 패키지별 사용 설명서 디렉토리.
79
+ 스킬과 규칙에서 참조하는 공유 문서. 규칙 파일에서 `Read tool로 읽으라`는 지시로 참조된다.
81
80
 
82
81
  ### 공유 문서
83
82
 
84
83
  | 파일 | Description |
85
84
  |------|-------------|
86
85
  | `sd-frontend-design.md` | 프론트엔드 UI 코드 작성 지침 |
87
- | `sd-simplysm14.md` | simplysm 패키지 문서 진입점 |
88
86
  | `sd-testing.md` | 테스트 작성 지침 |
89
87
 
90
- ### 패키지 문서 디렉토리
91
-
92
- | 디렉토리 | Description |
93
- |----------|-------------|
94
- | `sd-simplysm14/` | simplysm v14 패키지별 CLAUDE.md와 usage.md |
95
-
96
88
  ## 소스 오브 트루스
97
89
 
98
90
  개발 시 소스 오브 트루스는 모노레포 루트의 `.claude/` 디렉토리다. `packages/sd-claude/claude/`는 `prepack`(`sync.mjs`) 시 루트에서 복사되는 배포용 스냅샷이다.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/sd-claude",
3
- "version": "14.0.49",
3
+ "version": "14.0.51",
4
4
  "description": "심플리즘 패키지 - Claude Code 셋업",
5
5
  "author": "심플리즘",
6
6
  "license": "Apache-2.0",
@@ -1,110 +0,0 @@
1
- # sd-testing: 테스트 작성 규칙
2
-
3
- ## 모킹 원칙
4
-
5
- **CRITICAL: 모든 코드는 기본적으로 실제로 실행시킨다. mock은 최후의 수단이다.**
6
-
7
- ### 모킹 결정 플로우차트
8
-
9
- mock을 작성하기 **전에** 반드시 아래 순서로 판단한다:
10
-
11
- 1. **이 의존성을 실제로 실행하면 테스트가 물리적으로 돌아가는가?**
12
- - 돌아간다 → **모킹 금지. 실제 코드를 실행한다.**
13
- - 돌아가지 않는다 → 2번으로
14
- 2. **왜 물리적으로 불가능한가? 아래 화이트리스트에 해당하는가?**
15
- - 해당한다 → 모킹 허용 (최소 범위로)
16
- - 해당하지 않는다 → **모킹 금지. 테스트 설계를 재고한다.**
17
-
18
- ### 모킹 허용 화이트리스트
19
-
20
- 아래 항목**만** 모킹이 허용된다. 이 목록에 없으면 모킹하지 않는다:
21
-
22
- | 허용 대상 | 예시 | 이유 |
23
- |-----------|------|------|
24
- | 외부 네트워크 요청 | HTTP API 호출, WebSocket 서버 연결 | 외부 서버가 테스트 환경에 없음 |
25
- | 데이터베이스 연결 | DB 커넥션, 쿼리 실행 | DB 인스턴스가 테스트 환경에 없을 때만 (Docker로 띄울 수 있으면 실제 DB 사용) |
26
- | 하드웨어/OS 의존 | USB 장치, 파일시스템의 특수 권한, 네이티브 플러그인 | 물리 장치가 없음 |
27
- | 타이머/시간 | `Date.now()`, `setTimeout` | `vi.useFakeTimers()`로 제어 (이것은 mock이 아닌 테스트 유틸리티) |
28
-
29
- **다음은 모킹 대상이 아니다:**
30
- - 같은 패키지의 다른 모듈/클래스 → 실제 인스턴스를 생성해서 사용
31
- - 유틸리티 함수, 순수 함수 → 그냥 실행
32
- - 설정/config 객체 → 테스트용 실제 값을 만들어서 주입
33
- - 에러 핸들링 로직 → 실제 에러를 발생시켜서 테스트
34
- - 이벤트 핸들러/콜백 → 실제로 이벤트를 발생시켜서 테스트
35
-
36
- ### 모킹 세부 규칙
37
-
38
- - `vi.mock()` 하나가 모듈 전체를 대체하므로, 순수 함수까지 함께 가짜로 바뀐다. 혼합 모듈은 `importOriginal`로 실제 구현을 최대한 살린다.
39
- - **모킹이 실제 로직을 복제하면 모킹이 불필요하다는 증거다.** 모킹 코드가 원본 코드와 동일하거나 유사하면, 그 모킹을 삭제하고 실제 모듈을 쓴다.
40
- - **호출 여부만 확인하려면 `vi.mock()`이 아니라 `vi.spyOn()`을 쓴다.** mock은 구현을 가짜로 대체하고, spy는 실제 코드를 실행하면서 호출을 추적한다.
41
-
42
- ### Bad/Good 예시
43
-
44
- #### 예시 1: 같은 패키지의 유틸리티 함수
45
-
46
- ```typescript
47
- // ❌ BAD: 실행 가능한 유틸 함수를 모킹
48
- vi.mock("../utils/string-utils", () => ({
49
- formatName: vi.fn().mockReturnValue("formatted"),
50
- }));
51
-
52
- test("이름을 포맷한다", () => {
53
- const result = service.process("raw");
54
- expect(formatName).toHaveBeenCalledWith("raw"); // 구현 결합
55
- expect(result).toBe("formatted");
56
- });
57
- ```
58
-
59
- ```typescript
60
- // ✅ GOOD: 실제 유틸 함수를 실행
61
- import { formatName } from "../utils/string-utils";
62
-
63
- test("이름을 포맷한다", () => {
64
- const result = service.process("raw");
65
- expect(result).toBe("Raw"); // 실제 결과를 검증
66
- });
67
- ```
68
-
69
- #### 예시 2: 클래스 의존성
70
-
71
- ```typescript
72
- // ❌ BAD: 같은 패키지의 클래스를 통째로 모킹
73
- const mockParser = {
74
- parse: vi.fn().mockReturnValue({ type: "text", value: "hello" }),
75
- validate: vi.fn().mockReturnValue(true),
76
- };
77
-
78
- test("파서를 사용해 변환한다", () => {
79
- const converter = new Converter(mockParser as any);
80
- converter.convert("hello");
81
- expect(mockParser.parse).toHaveBeenCalled(); // 구현 결합
82
- });
83
- ```
84
-
85
- ```typescript
86
- // ✅ GOOD: 실제 파서 인스턴스를 사용
87
- test("파서를 사용해 변환한다", () => {
88
- const parser = new Parser();
89
- const converter = new Converter(parser);
90
- const result = converter.convert("hello");
91
- expect(result).toEqual({ type: "text", value: "hello" }); // 동작 결과를 검증
92
- });
93
- ```
94
-
95
- #### 예시 3: 외부 API (모킹이 정당한 경우)
96
-
97
- ```typescript
98
- // ✅ OK: 외부 HTTP 서버는 테스트 환경에 없으므로 모킹 허용
99
- vi.spyOn(httpClient, "get").mockResolvedValue({ status: 200, data: { id: 1 } });
100
-
101
- test("사용자 정보를 조회한다", async () => {
102
- const user = await userService.getUser(1);
103
- expect(user.id).toBe(1); // 반환값을 검증 (호출 여부가 아님)
104
- });
105
- ```
106
-
107
- ## 검증 원칙
108
-
109
- - **구현 결합 검증 금지.** mock으로 호출 횟수·인자를 검증하는 것은 구현 세부사항에 결합된다. 동작의 결과(반환값, 상태 변화, 사이드이펙트)를 검증한다.
110
- - 호출 여부 검증이 꼭 필요하면 spy를 쓰되, "몇 번 호출됐는가"보다 **"올바른 결과가 나왔는가"** 를 우선한다.