Skip to content
Open
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
39 changes: 39 additions & 0 deletions .claude/commands/prd-writer-cmd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# PRD + AC 작성

$ARGUMENTS 경로의 요구사항 파일을 분석하여 PRD + Acceptance Criteria를 작성합니다.

## 사용법
```
/prd-writer mission/round2.md
```

## 실행 절차

### 1단계: 스킬 로드
```
Skill tool 호출: skill="prd-writer"
```
- PRD 작성 가이드라인과 템플릿을 로드합니다.

### 2단계: 요구사항 파일 읽기
- 지정된 파일($ARGUMENTS)을 읽습니다.
- 파일이 없으면 사용자에게 확인합니다.

### 3단계: 브레인스토밍 진행
스킬의 워크플로우를 따릅니다:

1. **CAPTURE**: Feature 목록 추출
2. **CLARIFY**: 모호한 부분 AskUserQuestion으로 확인
3. **STRUCTURE**: PRD + AC 형식으로 구조화
4. **VALIDATE**: 완전성 체크

### 4단계: 결과물 저장
- `.claude/PLAN.md`에 저장
- 사용자에게 요약 제시

## 출력물
- `.claude/PLAN.md`: 완성된 PRD + AC 문서

## 참조
- 템플릿: `.claude/skills/prd-writer/templates/prd-template.md`
- 예시: `.claude/skills/prd-writer/examples/user-feature.md`
38 changes: 38 additions & 0 deletions .claude/rules/core/design-priciples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Design Principles

## 핵심 원칙
- SRP (Single Responsibility Principle) 무조건 준수
- OCP (Open/Closed Principle) 준수
- 현재 요구사항에 집중하되, 구조는 유연하게
- 파라미터가 6개 이하면 그대로 유지, 7개 이상이면 Command 객체로 그루핑

## 입력값 검증 전략

### 2단계 검증 (관심사 분리)
| 단계 | 계층 | 검증 대상 | 예시 |
|------|------|----------|------|
| 1단계 | Request DTO | 입력값 형식 | 필수값, 타입, 범위 |
| 2단계 | Domain Entity | 도메인 불변식 | 비즈니스 규칙 |

### Request DTO 검증 (입력값 형식)
- Bean Validation 사용 허용: `@NotNull`, `@NotBlank`, `@NotEmpty`, `@Min`, `@Max`, `@Positive`, `@Size` 등
- 목적: "값이 존재하는가? 기본 형식에 맞는가?"
- Controller에서 `@Valid` 사용

### Domain Entity 검증 (도메인 불변식)
- 도메인 객체가 자기 불변식을 스스로 검증
- 목적: "비즈니스 규칙에 맞는가?"
- 정적 팩토리 메서드(`create`)에서 검증
- cross-field 검증(필드 간 교차 검증)은 도메인에서 처리

## 중복 체크 전략
- 비즈니스 중복 체크는 Service에서 exists 쿼리로 명시적 수행
- DB unique constraint는 스키마 레벨에서 반드시 설정 (최종 방어선)
- DataIntegrityViolationException은 글로벌 예외 핸들러에서 공통 처리
- Service에서 try-catch로 DataIntegrityViolationException을 잡지 않는다

## 계층 간 데이터 전달
- Controller → Service: 원시값 파라미터로 전달 (dto.toEntity 방식 사용하지 않음)
- Service → Controller: Result/Info 객체 사용
- DTO가 Domain Entity를 직접 알지 않는다
- 암호화 등 외부 의존성이 필요한 변환은 Service에서 수행
50 changes: 50 additions & 0 deletions .claude/rules/core/dto-patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# DTO Patterns

## Record 사용 원칙
- 모든 DTO는 Java record로 작성 (불변성 보장)
- Request/Response는 버전별 DTO 컨테이너 내부에 정의
- Info는 application 계층에 독립 record로 정의

## DTO 컨테이너 구조
```java
public class UserV1Dto {
public record SignUpRequest(
String loginId,
String password,
String name,
LocalDate birthDate,
String email
) {}

public record UserResponse(
String loginId,
String name,
LocalDate birthDate,
String email
) {
public static UserResponse from(UserInfo info) {
return new UserResponse(
info.loginId(),
info.maskedName(),
info.birthDate(),
info.email()
);
}
}
}
```

## 데이터 흐름
```
Request → Controller (원시값 추출) → Service → Domain → Info → Response
```

## Request DTO 검증
- Bean Validation 사용 허용: `@NotNull`, `@NotBlank`, `@NotEmpty`, `@Min`, `@Max`, `@Positive`, `@Size` 등
- 입력값 형식 검증 담당 (도메인 불변식 검증과 관심사 분리)
- Controller에서 `@Valid` 사용

## 금지 사항
- DTO가 Domain Entity를 직접 import 금지
- dto.toEntity() 패턴 금지 (Controller에서 원시값 전달)
- 비즈니스 규칙 검증을 DTO에서 수행 금지 (도메인 역할)
48 changes: 48 additions & 0 deletions .claude/rules/core/exception-patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Exception Patterns

## 예외 종류별 사용

### IllegalArgumentException
- 도메인 불변식 검증 실패 시 사용
- User.create(), validate{Field}() 등에서 throw
- 글로벌 핸들러가 400 BAD_REQUEST로 변환

```java
private static void validateLoginId(String loginId) {
if (loginId == null || loginId.isBlank()) {
throw new IllegalArgumentException("로그인 ID는 필수입니다");
}
}
```

### CoreException
- 비즈니스 로직 오류 시 사용
- Service 계층에서 throw
- ErrorType에 따라 HTTP 상태코드 결정

```java
// 중복 체크
if (userRepository.existsByLoginId(loginId)) {
throw new CoreException(ErrorType.CONFLICT, "이미 사용 중인 로그인 ID입니다");
}

// 조회 실패
User user = userRepository.findByLoginId(loginId)
.orElseThrow(() -> new CoreException(ErrorType.NOT_FOUND, "회원을 찾을 수 없습니다"));

// 인증 실패
throw new CoreException(ErrorType.UNAUTHORIZED, "비밀번호가 일치하지 않습니다");
```

## ErrorType 선택 기준
| ErrorType | HTTP Status | 사용 상황 |
|-----------|-------------|-----------|
| BAD_REQUEST | 400 | 입력값 검증 실패 |
| UNAUTHORIZED | 401 | 인증 실패, 헤더 누락 |
| NOT_FOUND | 404 | 리소스 없음 |
| CONFLICT | 409 | 중복 리소스 |
| INTERNAL_ERROR | 500 | 예상치 못한 오류 |

## 금지 사항
- Controller/Service에서 try-catch로 예외 잡기 금지
- DataIntegrityViolationException 직접 처리 금지 (글로벌 핸들러가 처리)
39 changes: 39 additions & 0 deletions .claude/rules/core/layer-patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Layer Patterns

## 패키지 구조
```
com.loopers/
├── interfaces/api/{domain}/ # REST API
├── application/{domain}/ # Facade, Info
├── domain/{domain}/ # Entity, Service, Repository 인터페이스
└── infrastructure/{domain}/ # Repository 구현, 외부 어댑터
```

## 계층별 역할

### Controller (interfaces)
- HTTP 요청/응답 변환만 담당
- Request에서 원시값 추출하여 Facade에 전달
- try-catch 금지 (글로벌 핸들러가 처리)
- `@Valid`로 Request DTO 입력값 형식 검증

### Facade (application)
- Service 호출 오케스트레이션
- Domain Entity → Info 변환
- 트랜잭션 경계 아님 (Service에서 관리)

### Service (domain)
- 비즈니스 로직 담당
- 중복 체크는 exists 쿼리로 명시적 수행
- `@Transactional(readOnly = true)` 기본, 쓰기만 `@Transactional`

### Repository (domain → infrastructure)
- 인터페이스는 domain 패키지에 정의
- 구현체는 infrastructure 패키지에 배치

## 의존성 방향
```
interfaces → application → domain ← infrastructure
```
- Domain은 Infrastructure를 알지 않음
- 외부 의존성은 인터페이스로 추상화 (예: PasswordEncoder)
43 changes: 43 additions & 0 deletions .claude/rules/core/naming-conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Naming Conventions

## 클래스 네이밍

### API 계층 (interfaces/api/)
- Controller: `{Domain}V{version}Controller` (예: `UserV1Controller`)
- API Spec: `{Domain}ApiV{version}Spec` (예: `UserApiV1Spec`)
- DTO Container: `{Domain}V{version}Dto` (예: `UserV1Dto`)
- Request DTO: `{Action}Request` (내부 record, 예: `SignUpRequest`)
- Response DTO: `{Domain}Response` (내부 record, 예: `UserResponse`)

### Application 계층 (application/)
- Facade: `{Domain}Facade` (예: `UserFacade`)
- Info: `{Domain}Info` (예: `UserInfo`)

### Domain 계층 (domain/)
- Entity: `{Domain}` (예: `User`)
- Service: `{Domain}Service` (예: `UserService`)
- Repository: `{Domain}Repository` (예: `UserRepository`)
- 의존성 인터페이스: `{Concept}Encoder`, `{Concept}Validator` (예: `PasswordEncoder`)

### Infrastructure 계층 (infrastructure/)
- Repository 구현: `{Domain}RepositoryImpl` (예: `UserRepositoryImpl`)
- JPA Repository: `{Domain}JpaRepository` (예: `UserJpaRepository`)
- 인터페이스 구현: `{Prefix}{Concept}` (예: `BcryptPasswordEncoder`)

## 메서드 네이밍

### Repository 메서드
- 저장: `save(entity)`
- 단일 조회: `findBy{Field}(value)` → `Optional<T>` 반환
- 존재 여부: `existsBy{Field}(value)` → `boolean` 반환

### DTO 변환 메서드
- 팩토리 메서드: `from(source)` (예: `UserResponse.from(UserInfo)`)

### 도메인 엔티티 메서드
- 정적 팩토리: `create(...)` (생성자 대신 사용)
- 비즈니스 메서드: 동사로 시작 (예: `changePassword`, `delete`, `restore`)

### 검증 메서드
- private 검증: `validate{Field}(value)` (예: `validateLoginId`)
- 정적 검증 유틸: `{Validator}.validate(...)` (예: `PasswordValidator.validate`)
9 changes: 9 additions & 0 deletions .claude/rules/core/never-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Never Do

- 실제 동작하지 않는 코드, 불필요한 Mock 데이터를 이용한 구현 금지
- null-safety 하지 않은 코드 작성 금지 (Optional 활용)
- System.out.println 코드 남기지 않기
- 테스트를 임의로 삭제하거나 @Disabled 처리 금지
- 요청하지 않은 기능을 선제적으로 구현하지 않기
- 과도한 미래 예측 기반 설계 금지 (YAGNI 준수)
- Service 간 직접 호출 금지 (순환 참조 방지)
26 changes: 26 additions & 0 deletions .claude/rules/core/project-conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Project Conventions (기존 코드 패턴)

## API Response
- 성공: ApiResponse.success(data)
- 실패: ApiResponse.fail(errorType)

## Exception
- throw new CoreException(ErrorType.NOT_FOUND)
- throw new CoreException(ErrorType.BAD_REQUEST, "Custom message")

## ErrorType 종류
- INTERNAL_ERROR (500), BAD_REQUEST (400), NOT_FOUND (404), CONFLICT (409)

## Feature 생성 순서
1. Controller (interfaces/api/) — *ApiSpec 인터페이스 + *Controller 구현
2. Facade (application/) — orchestration + *Info records
3. Service (domain/) — 비즈니스 로직
4. Repository (domain/) — 인터페이스 정의
5. Repository Impl (infrastructure/) — JPA 구현

## 주의사항
- open-in-view disabled (Controller에서 lazy loading 금지)
- Timezone: Asia/Seoul
- Soft Delete: deletedAt 필드 사용, hard delete 금지
- Hibernate DDL은 production에서 none (스키마 변경은 migration으로만)
- QueryDSL Q-class 경로: build/generated/sources/annotationProcessor
21 changes: 21 additions & 0 deletions .claude/rules/core/recommendation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Recommendation

## 코드 품질
- 재사용 가능한 객체 설계
- 기존 코드 패턴 분석 후 일관성 유지
- Java 21 모던 문법 적극 활용 (Records, Pattern Matching, Text Blocks, Sealed Class)

## 테스트
- 실제 API를 호출해 확인하는 E2E 테스트 코드 작성
- 도메인 검증은 단위 테스트로 작성 (HTTP 컨텍스트 불필요)
- Testcontainers 활용하여 실제 DB 환경에서 통합 테스트

## 문서화
- 개발 완료된 API는 `http/*.http` 파일에 분류해 작성
- 성능 최적화에 대한 대안 및 제안 제시

## 우선순위
1. 실제 동작하는 해결책만 고려
2. null-safety, thread-safety 고려
3. 테스트 가능한 구조로 설계
4. 기존 코드 패턴 분석 후 일관성 유지
39 changes: 39 additions & 0 deletions .claude/rules/core/workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Workflow

## 대원칙 - 증강 코딩
- 방향성 및 주요 의사 결정은 개발자에게 제안만 하고, 최종 승인된 사항을 기반으로 작업 수행
- AI가 임의판단하지 않고, 제안 후 개발자 승인을 받은 뒤 수행
- 요청하지 않은 기능 구현, 테스트 삭제, 반복적 동작 시 즉시 중단하고 보고

## 구현 워크플로우 (하향식)

PRD + AC가 명세 역할을 하므로, **AC ↔ 테스트 1:1 매핑 검증**이 핵심

### 구현 순서 (하향식)

```
Controller → Facade → Service → Repository → Entity
```

1. **Controller + DTO** - API 인터페이스 정의
2. **Facade + Info** - 오케스트레이션
3. **Service** - 비즈니스 로직
4. **Repository** - 인터페이스 + 구현체
5. **Entity** - 도메인 모델

### 테스트 작성

- 구현과 테스트를 **동시에** 작성
- 모든 테스트는 3A 원칙: Arrange - Act - Assert
- AC와 테스트 1:1 매핑 필수

| 계층 | 테스트 유형 |
|------|-----------|
| Entity | 단위 테스트 |
| Service | 통합 테스트 |
| Controller | E2E 테스트 |

### Refactor Phase
- 불필요한 코드 제거, 객체지향적 구조 개선
- unused import 제거
- 모든 테스트 케이스가 통과해야 함
Loading