Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .claude/commands/feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
description: 기능 추가/개선을 TDD(Red→Green→Refactor)로 강제 수행
---

다음 요구사항으로 기능 작업을 **TDD로** 수행한다: **$ARGUMENTS**

루트 `CLAUDE.md`의 컨벤션과 6장 워크플로우를 준수한다. **테스트를 먼저 작성하지 않고 기능 코드를 작성하지 말 것.** 아래 순서를 반드시 지킨다.

1. **Plan**: 관련 도메인 패키지를 먼저 읽고 영향 범위를 파악한다. 변경/추가 파일, 엔드포인트, 새 `ErrorStatus`, DTO(`XxxReqDto`/`XxxResDto`)와 함께 **검증할 동작을 테스트 케이스 목록으로** 제시한다. 요구사항이 모호하면 **구현 전에 질문**한다.

2. **🔴 Red**: 위 케이스를 검증하는 테스트를 **먼저** 작성한다(`src/test/.../CLAUDE.md` 규약: 적절한 베이스 클래스, AssertJ, `@DisplayName` 한국어, given/when/then). 그 테스트를 `./gradlew test --tests "..."`로 실행해 **의도한 이유로 실패(또는 컴파일 실패)하는 것을 확인하고 결과를 보고**한다. 이 단계를 건너뛰면 안 된다.

3. **🟢 Green**: 그 테스트를 통과시킬 **최소 구현**을 기존 패턴(ApiResponse·WhiplashException·생성자 주입·패키지 바이 피처)대로 작성한다. 다시 테스트를 실행해 통과를 확인한다.

4. **🔵 Refactor**: 그린을 유지하며 중복·네이밍·구조를 정리하고, 변경 후 테스트를 재실행해 통과를 유지한다.

5. **Report**: 추가한 테스트, 변경 파일, 추가 엔드포인트, 새 `ErrorStatus`, 테스트 실행 결과(있는 그대로), 남은 TODO를 요약한다.

**환경 제약**: 통합/Testcontainers 테스트는 Docker가 필요하다. Docker가 없으면 순수 단위 테스트(`POJOTestSupport`) 수준에서라도 Red→Green을 지키고, 통합 테스트는 작성만 해 두되 "Docker 환경에서 실행 필요"로 명시한다. **테스트 자체를 생략하지 않는다.**

**예외**: 동작이 바뀌지 않는 순수 설정/문서/로그 문구 변경이면 그 사실을 한 줄로 밝히고 TDD 단계를 생략할 수 있다. 애매하면 TDD를 적용한다.

커밋·푸시·PR은 내가 명시적으로 요청하기 전까지 하지 않는다.
22 changes: 22 additions & 0 deletions .claude/commands/new-domain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
description: 새 도메인 모듈을 프로젝트 컨벤션에 맞춰 스캐폴딩
---

새 도메인 **$ARGUMENTS** 를 `com.example.whiplash.$ARGUMENTS` 패키지로 추가한다.

기존 도메인(예: `quiz`, `term`) 구조를 참고해 **패키지 바이 피처**로 레이어를 구성한다.

1. 먼저 유사한 기존 도메인 하나를 읽어 실제 패턴(어노테이션·생성자 주입·DTO 네이밍)을 확인한다.
2. 다음 골격을 생성한다(필요한 것만):
- `controller/` — `@RestController` + `@RequestMapping("/api/$ARGUMENTS")` + `@RequiredArgsConstructor` + `@Slf4j` + `@Tag`, 반환은 `ApiResponse<T>`
- `service/` — `@Service` + `@RequiredArgsConstructor`, 비즈니스 예외는 `WhiplashException(ErrorStatus.XXX)`
- `repository/` — JPA `JpaRepository` 또는 Mongo `MongoRepository`
- `entity/`(JPA) 또는 `document/`(Mongo) — `@Getter` + `@Builder` + protected 기본 생성자
- `dto/request`, `dto/response` — `XxxReqDto` / `XxxResDto`
3. 새 에러 케이스는 `apiPayload/ErrorStatus.java`에 도메인 주석 구역으로 추가한다.
4. `./gradlew compileJava`로 컴파일을 확인한다.
5. 생성한 파일과 엔드포인트, 추가한 `ErrorStatus`를 요약한다.

실제 비즈니스 로직은 빈 골격 + TODO로 두고, 내가 채울 부분을 명확히 표시한다.

**이후 이 도메인에 로직을 채울 때는 `/feature`의 TDD 루프(Red→Green→Refactor)를 따른다.** 스캐폴딩 단계에서는 골격만 만들고, 동작 구현은 실패 테스트부터 시작한다.
14 changes: 14 additions & 0 deletions .claude/commands/review-diff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
description: 현재 변경 diff를 프로젝트 컨벤션 기준으로 점검
---

현재 작업 트리의 변경사항(`git diff` 및 staged 포함)을 검토한다. $ARGUMENTS 가 있으면 그 범위에 집중한다.

루트 `CLAUDE.md` 기준으로 다음을 점검하고, 발견 시 파일:라인과 함께 보고한다.

- **컨벤션 위반**: `ApiResponse`/`WhiplashException` 우회, 필드 주입, DTO 네이밍, 컨트롤러 어노테이션 누락
- **절대 규칙 위반**: `.env`·시크릿 노출, 하드코딩된 설정값, 외부 API 미mock 테스트
- **버그 가능성**: NPE, 트랜잭션 경계, N+1, 누락된 검증(`@Valid`)
- **TDD 준수**: 동작이 바뀐 코드에 대응하는 테스트가 함께 있는지. 기능 코드만 추가되고 테스트가 없으면 **TDD 위반**으로 지적한다(설정/문서/로그 변경은 예외)

각 지적은 (1) 위치 (2) 문제 (3) 제안 수정 형태로 간결하게. 사소한 스타일은 묶어서 보고한다. 자동 수정은 하지 말고 보고만 한다.
35 changes: 35 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(./gradlew compileJava:*)",
"Bash(./gradlew test:*)",
"Bash(./gradlew build:*)",
"Bash(./gradlew bootJar:*)",
"Bash(./gradlew check:*)",
"Bash(git status:*)",
"Bash(git diff:*)",
"Bash(git log:*)",
"Bash(git branch:*)",
"Bash(git show:*)",
"Bash(rg:*)",
"Bash(grep:*)",
"Bash(find:*)",
"Bash(ls:*)",
"Bash(cat:*)",
"Bash(docker-compose ps:*)",
"Bash(docker compose ps:*)"
],
"ask": [
"Bash(git push:*)",
"Bash(git commit:*)",
"Bash(gh pr create:*)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Bash(git push --force:*)",
"Bash(./gradlew bootRun:*)"
]
}
}
Loading