diff --git a/.forge/runtime-role-matrix-live-20260714111146-v10-ta-001-attempt-1-run-67f0ca150a2e.md b/.forge/runtime-role-matrix-live-20260714111146-v10-ta-001-attempt-1-run-67f0ca150a2e.md deleted file mode 100644 index 5ba28d6..0000000 --- a/.forge/runtime-role-matrix-live-20260714111146-v10-ta-001-attempt-1-run-67f0ca150a2e.md +++ /dev/null @@ -1,3 +0,0 @@ -# runtime-role-matrix-live-20260714111146-v10-ta-001-attempt-1-run-67f0ca150a2e - -Forge 이슈 작업 브랜치 `forge/runtime-role-matrix-live-20260714111146-v10-ta-001-attempt-1-run-67f0ca150a2e`. diff --git a/docs/adr/ADR-001-spring-boundary-architecture.md b/docs/adr/ADR-001-spring-boundary-architecture.md deleted file mode 100644 index c8434aa..0000000 --- a/docs/adr/ADR-001-spring-boundary-architecture.md +++ /dev/null @@ -1,137 +0,0 @@ -# ADR-001: Spring 경계 아키텍처 및 오류 계약 - -## Context - -본 프로젝트(runtime-role-matrix-live)는 Spring Boot 기반의 REST API 서버로, 역할(Role) 기반 접근 제어 및 매트릭스 관리 기능을 제공한다. 현재 Controller, Service, Repository 계층 간 책임 분담이 명확하지 않고, 예외 처리 및 트랜잭션 경계가 일관되지 않아 유지보수성과 테스트 가능성이 저하되고 있다. - -### 현재 문제점 - -- Controller에서 비즈니스 로직 직접 수행 -- Service 계층의 트랜잭션 경계 불명확 -- 예외 처리 방식이 계층마다 상이 -- Repository 호출 시 구체적 예외가 Service까지 전파 - -## Decision - -### 1. 계층별 책임 정의 - -| 계층 | 책임 | -|------|------| -| **Controller** | HTTP 요청/응답 변환, 입력 검증, HTTP 상태 코드 결정, Service 호출 | -| **Service** | 비즈니스 로직 수행, 트랜잭션 경계 관리, 도메인 객체 조작, 예외 변환 | -| **Repository** | 데이터 접근 추상화, JPA Entity 관리, 쿼리 실행 | - -### 2. 오류 계약 (Error Contract) - -``` -Client Request - │ - ▼ -┌─────────────┐ -│ Controller │ ← @Valid, BindingResult 검증 -└──────┬──────┘ - │ BusinessException 또는 도메인 예외 - ▼ -┌─────────────┐ -│ Service │ ← 트랜잭션 경계, 예외 변환 -└──────┬──────┘ - │ DataAccessException (RuntimeException 래핑) - ▼ -┌─────────────┐ -│ Repository │ ← JPA/DB 접근 -└─────────────┘ -``` - -**예외 계층 구조:** - -| 예외 유형 | 발생 계층 | 처리 방식 | -|-----------|-----------|-----------| -| `MethodArgumentNotValidException` | Controller | 400 Bad Request, 필드 오류 목록 반환 | -| `BusinessException` | Service | 409 Conflict 또는 404 Not Found | -| `DataAccessException` | Repository | Service에서 `PersistenceException`으로 변환 → 500 Internal Server Error | -| `EntityNotFoundException` | Repository/Service | 404 Not Found | - -**표준 오류 응답 형식:** - -```json -{ - "timestamp": "2026-07-14T11:11:46Z", - "status": 400, - "error": "Bad Request", - "message": "Validation failed", - "path": "/api/v1/roles", - "details": [ - { "field": "name", "message": "must not be blank" } - ] -} -``` - -### 3. 트랜잭션 경계 - -| 작업 유형 | 트랜잭션 속성 | 전파 방식 | -|-----------|--------------|-----------| -| 조회 (Read) | `readOnly = true` | `REQUIRED` | -| 단일 생성/수정/삭제 | `readOnly = false` | `REQUIRED` | -| 다중 변경 (Batch) | `readOnly = false` | `REQUIRES_NEW` | - -**규칙:** -- Service 메서드가 트랜잭션 경계의 시작점 -- Controller에서 `@Transactional` 사용 금지 -- Repository는 항상 트랜잭션 내 실행 - -### 4. 의존성 방향 - -``` -Controller ──► Service ──► Repository - │ │ - └──► DTO/VO ◄──┘ -``` - -- Controller는 Service 인터페이스에만 의존 -- Service는 Repository 인터페이스에만 의존 -- Entity는 Repository → Service 방향으로만 이동 -- DTO/VO는 Controller ↔ Service 간 통신에 사용 - -## Alternatives - -### 대안 1: 모든 계층에서 예외 처리 - -| 장점 | 단점 | -|------|------| -| 세밀한 오류 제어 가능 | 예외 처리 코드 중복 | -| 계층별 맞춤 응답 가능 | 일관성 유지 어려움 | - -### 대안 2: @ControllerAdvice 단일화 - -| 장점 | 단점 | -|------|------| -| 예외 처리 중앙화 | 너무 많은 예외 유형 매핑 필요 | -| 응답 형식 일관성 | 디버깅 복잡도 증가 | - -### 선택: 계층별 예외 변환 + @ControllerAdvice - -Service 계층에서 도메인 예외로 변환하고, `@ControllerAdvice`에서 HTTP 응답으로 매핑하는 하이브리드 방식 채택. - -## Consequences - -### 긍정적 결과 - -- **단일 책임 원칙 준수**: 각 계층이 명확한 역할 수행 -- **테스트 용이성**: Mock 기반 단위 테스트 가능 -- **일관된 오류 응답**: API 소비자가 예측 가능한 에러 형식 수신 -- **트랜잭션 관리 용이**: Service 메서드 수준에서 트랜잭션 제어 - -### 부정적 결과 - -- **추가 코드 작성**: 예외 변환 클래스와 DTO 증가 -- **학습 곡선**: 개발자별 아키텍처 이해 필요 -- **트랜잭션 경계 설계 주의**: 잘못된 전파 설정 시 데이터 불일치 위험 - -### 추적 항목 - -| 항목 | 상태 | 비고 | -|------|------|------| -| 예외 계층 구조 구현 | Pending | BusinessException, PersistenceException 정의 | -| @ControllerAdvice 구성 | Pending | GlobalExceptionHandler | -| Service 트랜잭션 어노테이션 | Pending | @Transactional 적용 | -| 오류 응답 DTO 정의 | Pending | ErrorResponse, FieldErrorResponse |