diff --git a/.forge/role-ta-001-attempt-1-run-9fa6a6b8d5c6.md b/.forge/role-ta-001-attempt-1-run-9fa6a6b8d5c6.md deleted file mode 100644 index 41d89a8..0000000 --- a/.forge/role-ta-001-attempt-1-run-9fa6a6b8d5c6.md +++ /dev/null @@ -1,3 +0,0 @@ -# role-ta-001-attempt-1-run-9fa6a6b8d5c6 - -Forge 이슈 작업 브랜치 `forge/role-ta-001-attempt-1-run-9fa6a6b8d5c6`. diff --git a/docs/adr/ADR-001-controller-service-repository-boundaries.md b/docs/adr/ADR-001-controller-service-repository-boundaries.md deleted file mode 100644 index af42692..0000000 --- a/docs/adr/ADR-001-controller-service-repository-boundaries.md +++ /dev/null @@ -1,131 +0,0 @@ -# ADR-001: Controller-Service-Repository 경계 정의 - -## Context - -본 프로젝트(runtime-role-smoke-202607140500)는 Spring Boot 기반의 역할 관리 시스템이다. -다층 아키텍처에서 각 계층의 책임과 의존성 방향을 명확히 정의하여: -- 코드 유지보수성 향상 -- 단위 테스트 용이성 확보 -- 계층 간 결합도 최소화 - -를 목적으로 한다. - -## Decision - -### 1. Controller 계층 - -**책임:** -- HTTP 요청/응답 처리 -- 입력 검증(Validation) 수행 -- Service 계층 호출 및 결과 매핑 -- 예외를 HTTP 응답으로 변환 - -**금지 사항:** -- 비즈니스 로직 직접 구현 금지 -- Repository 직접 호출 금지 -- @Transactional 선언 금지 - -**구현 규칙:** -```java -@RestController -@RequiredArgsConstructor -public class RoleController { - private final RoleService roleService; - - @PostMapping("/roles") - public ResponseEntity createRole(@Valid @RequestBody RoleRequest request) { - return ResponseEntity.status(HttpStatus.CREATED) - .body(roleService.createRole(request)); - } -} -``` - -### 2. Service 계층 - -**책임:** -- 비즈니스 로직 수행 -- 트랜잭션 관리 -- 도메인 객체 조작 -- Repository 호출 및 결과 가공 - -**금지 사항:** -- HTTP 요청/응답 직접 처리 금지 -- @RequestBody, @RequestParam 등 HTTP 어노테이션 사용 금지 - -**구현 규칙:** -```java -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class RoleService { - private final RoleRepository roleRepository; - - @Transactional - public RoleResponse createRole(RoleRequest request) { - // 비즈니스 로직 - Role role = Role.create(request.getName(), request.getDescription()); - Role savedRole = roleRepository.save(role); - return RoleResponse.from(savedRole); - } -} -``` - -### 3. Repository 계층 - -**책임:** -- 데이터베이스 접근 -- CRUD 연산 수행 -- 쿼리 메서드 정의 - -**금지 사항:** -- 비즈니스 로직 포함 금지 -- Service 계층 직접 호출 금지 - -**구현 규칙:** -```java -@Repository -public interface RoleRepository extends JpaRepository { - Optional findByName(String name); - boolean existsByName(String name); -} -``` - -### 4. 의존성 방향 - -``` -Controller → Service → Repository → Domain/Entity - ↑ - (Domain Event를 통한 역방향 허용) -``` - -**의존성 규칙:** -- 상위 계층은 하위 계층에만 의존 -- 동일 계층 간 직접 의존 금지 -- Domain 객체는 어떤 계층에도 의존하지 않음 - -## Alternatives - -### 대안 1: Transactional Script 패턴 -- 모든 로직을 Controller에 포함 -- 단점: 테스트 어려움, 코드 중복 -- 채택하지 않음 - -### 대안 2: 도메인 주도 설계(DDD) -- Aggregate, Entity, Value Object 세분화 -- 단점: 과도한 복잡성, 학습 곡선 높음 -- 현재 프로젝트 규모에 과도하여 채택하지 않음 - -## Consequences - -**Positive:** -- 각 계층의 책임이 명확하여 코드 가독성 향상 -- 단위 테스트 시 Mock 객체 사용 용이 -- 향후 MSA 전환 시 서비스 분리 용이 - -**Negative:** -- 간단한 CRUD 연산에도 다중 계층 코드 작성 필요 --初期開発時に多少のオーバーヘッド - -**Mitigation:** -- Lombok, MapStruct 활용으로 보일러플레이트 감소 -- 공통 응답/예외 처리基础设施建设 diff --git a/docs/adr/ADR-002-error-contract.md b/docs/adr/ADR-002-error-contract.md deleted file mode 100644 index 7f5a43e..0000000 --- a/docs/adr/ADR-002-error-contract.md +++ /dev/null @@ -1,132 +0,0 @@ -# ADR-002: 오류 계약(Error Contract) 정의 - -## Context - -REST API에서 일관된 오류 응답 형식을 제공하여: -- 클라이언트가 오류를 명확히 이해 가능 -- API 버전 간 호환성 유지 -- 디버깅 및 모니터링 용이성 확보 - -를 목적으로 한다. - -## Decision - -### 1. 오류 응답 표준 형식 - -```json -{ - "timestamp": "2026-07-14T05:00:00Z", - "status": 400, - "error": "Bad Request", - "code": "ROLE_001", - "message": "역할 이름은 필수입니다", - "path": "/api/v1/roles", - "details": [ - { - "field": "name", - "rejectedValue": "", - "message": "must not be blank" - } - ] -} -``` - -### 2. 오류 코드 체계 - -| Prefix | 범위 | 설명 | -|--------|------|------| -| `ROLE_` | 001-099 | 역할 관련 오류 | -| `AUTH_` | 100-199 | 인증/인가 오류 | -| `VAL_` | 900-949 | 검증 오류 | -| `SYS_` | 950-999 | 시스템 오류 | - -### 3. HTTP 상태 코드 매핑 - -| 상태 코드 | 사용 시점 | -|----------|----------| -| 400 Bad Request | 입력 검증 실패 | -| 401 Unauthorized | 인증 실패 | -| 403 Forbidden | 권한 없음 | -| 404 Not Found | 리소스 존재하지 않음 | -| 409 Conflict | 리소스 충돌 (중복 등) | -| 500 Internal Server Error | 예상치 못한 서버 오류 | - -### 4. 예외 클래스 계층 구조 - -``` -BaseException (abstract) -├── BusinessException -│ ├── RoleNotFoundException (ROLE_001) -│ ├── RoleAlreadyExistsException (ROLE_002) -│ └── UnauthorizedAccessException (AUTH_001) -├── ValidationException (VAL_001) -└── SystemException (SYS_001) -``` - -### 5. 구현 클래스 - -```java -// BaseException.java -public abstract class BaseException extends RuntimeException { - private final String errorCode; - private final HttpStatus httpStatus; - - protected BaseException(String errorCode, HttpStatus httpStatus, String message) { - super(message); - this.errorCode = errorCode; - this.httpStatus = httpStatus; - } -} - -// ErrorResponse.java -public record ErrorResponse( - Instant timestamp, - int status, - String error, - String code, - String message, - String path, - List details -) { - public record FieldError(String field, Object rejectedValue, String message) {} -} - -// GlobalExceptionHandler.java -@RestControllerAdvice -public class GlobalExceptionHandler { - - @ExceptionHandler(BusinessException.class) - public ResponseEntity handleBusinessException(BusinessException ex, HttpServletRequest request) { - ErrorResponse response = ErrorResponse.of(ex, request.getRequestURI()); - return ResponseEntity.status(ex.getHttpStatus()).body(response); - } - - @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity handleValidationException(MethodArgumentNotValidException ex, HttpServletRequest request) { - ErrorResponse response = ErrorResponse.ofValidation(ex, request.getRequestURI()); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); - } -} -``` - -## Alternatives - -### 대안 1: RFC 7807 Problem Details -- `application/problem+json` Content-Type 사용 -- 단점: 클라이언트 라이브러리 지원 제한적 -- 채택하지 않음 (일반 JSON 응답 채택) - -### 대안 2: 단순 오류 메시지만 반환 -- 단점: 오류 코드 부재로 클라이언트 처리 어려움 -- 채택하지 않음 - -## Consequences - -**Positive:** -- 일관된 API 응답으로 클라이언트 개발 편의성 향상 -- 오류 코드 기반 로컬라이제이션 가능 -- 모니터링 시스템 연동 용이 - -**Negative:** -- 오류 응답 클래스 추가 작성 필요 -- 기존 예외 처리 코드 마이그레이션 필요 diff --git a/docs/adr/ADR-003-transaction-boundary.md b/docs/adr/ADR-003-transaction-boundary.md deleted file mode 100644 index ef40a37..0000000 --- a/docs/adr/ADR-003-transaction-boundary.md +++ /dev/null @@ -1,119 +0,0 @@ -# ADR-003: 트랜잭션 경계(Transaction Boundary) 정의 - -## Context - -Spring에서 트랜잭션 경계 설정 방식에 따라: -- 데이터 무결성 보장 -- 성능 최적화 -- 격리 수준(Isolation Level) 제어 - -를 적절히 balancing해야 한다. - -## Decision - -### 1. 트랜잭션 전파 정책 - -| 전파 유형 | 사용 시점 | -|----------|----------| -| `REQUIRED` (기본값) | 대부분의 Service 메서드 | -| `REQUIRES_NEW` | 독립적인 작업 단위 (로깅, 알림) | -| `NESTED` | 저장점(savepoint) 기반 부분 롤백 | -| `SUPPORTS` | 읽기 전용 조회 (트랜잭션 없으면 자동 읽기 전용) | - -### 2. 격리 수준(Isolation Level) - -```java -@Transactional(isolation = Isolation.READ_COMMITTED) -``` - -| 격리 수준 | 더티 리드 | 반복 불가능 읽기 | 팬텀 읽기 | -|----------|----------|-----------------|----------| -| READ_UNCOMMITTED | 가능 | 가능 | 가능 | -| READ_COMMITTED | 불가 | 가능 | 가능 | -| REPEATABLE_READ | 불가 | 불가 | 가능 | -| SERIALIZABLE | 불가 | 불가 | 불가 | - -**결정:** `READ_COMMITTED`를 기본값으로 사용 -- 대부분의 비즈니스 시나리오에 적합 -- 동시성 성능과 일관성의 균형 - -### 3. 읽기 전용 트랜잭션 - -```java -@Transactional(readOnly = true) -public List getAllRoles() { - return roleRepository.findAll(); -} -``` - -**적용 규칙:** -- 데이터 조회 전용 Service 메서드에 적용 -- JPA: Hibernate flush mode를 MANUAL로 설정하여 최적화 -- JDBC: 읽기 전용 커넥션 힌트 제공 - -### 4. 트랜잭션 경계 위치 - -``` -[Controller] - ↓ -[Service Layer] ← ★ 트랜잭션 경계 - ↓ -[Repository Layer] - ↓ -[Database] -``` - -**규칙:** -- 트랜잭션은 Service 계층에서 시작 -- Controller에서 @Transactional 사용 금지 -- Repository에서 @Transactional 사용 금지 - -### 5. 롤백 정책 - -```java -@Transactional(rollbackFor = Exception.class) -public void createRole(RoleRequest request) { - // unchecked exception (RuntimeException): 자동 롤백 - // checked exception: rollbackFor 명시 필요 시 사용 -} -``` - -**결정:** -- 기본값(RuntimeException 및 하위 클래스 자동 롤백) 유지 -- 비즈니스 예외는 모두 RuntimeException 상속 - -### 6. 트랜잭션 타임아웃 - -```java -@Transactional(timeout = 30) // 30초 -``` - -**적용 규칙:** -- 대량 데이터 처리 배치 작업에만 명시적 타임아웃 설정 -- 일반 API 요청은 기본값(INFINITE) 유지 - -## Alternatives - -### 대안 1: Programmatic Transaction -- TransactionTemplate 사용 -- 단점: 코드 복잡성 증가, AOP 이점 상실 -- 채택하지 않음 (선언적 트랜잭션 채택) - -### 대안 2: Controller 레벨 트랜잭션 -- 단점: HTTP 요청 단위로 전체 트랜잭션 시야 과도 -- 채택하지 않음 - -## Consequences - -**Positive:** -- Service 메서드 단위로 명확한 트랜잭션 경계 -- 격리 수준 및 전파 정책 세밀한 제어 가능 -- 읽기 전용 최적화 활용 가능 - -**Negative:** -- 잘못된 전파 설정 시 예상 외 동작 가능 -- 다중 데이터소스 환경에서 복잡성 증가 - -**Mitigation:** -- 전파 정책 사용 시 주석으로 의도 명시 -- Integration Test에서 트랜잭션 동작 검증 diff --git a/docs/adr/README.md b/docs/adr/README.md deleted file mode 100644 index 6a67737..0000000 --- a/docs/adr/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Architecture Decision Records - -본 디렉토리는 프로젝트의 주요 아키텍처 결정 사항을 문서화합니다. - -## ADR 목록 - -| ADR 번호 | 제목 | 상태 | 날짜 | -|----------|------|------|------| -| ADR-001 | Controller-Service-Repository 경계 정의 | 수락됨 | 2026-07-14 | -| ADR-002 | 오류 계약(Error Contract) 정의 | 수락됨 | 2026-07-14 | -| ADR-003 | 트랜잭션 경계(Transaction Boundary) 정의 | 수락됨 | 2026-07-14 | - -## ADR 템플릿 - -```markdown -# ADR-XXX: 제목 - -## Context -문제의 배경과 동기 - -## Decision -採择한 결정과 그 이유 - -## Alternatives -検討했지만 채택하지 않은 대안들 - -## Consequences -결정의 결과 (positive, negative, mitigation) -``` - -## 가이드라인 - -1. **새 ADR 생성 시:** `ADR-XXX` 형식으로 파일명 지정 -2. **상태 변경:** 수락됨(Accepted), 대체됨(Superseded), 폐기됨(Deprecated) -3. **검토 주기:** 분기별 기존 ADR 검토 및 업데이트