Merge pull request 'TA 역할 Spring 경계 smoke' (#3) from forge/runtime-role-matrix-live-20260714111146-v10-ta-001-attempt-1-run-67f0ca150a2e into main
This commit is contained in:
commit
727124f2e9
2 changed files with 140 additions and 0 deletions
|
|
@ -0,0 +1,3 @@
|
||||||
|
# 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`.
|
||||||
137
docs/adr/ADR-001-spring-boundary-architecture.md
Normal file
137
docs/adr/ADR-001-spring-boundary-architecture.md
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
# 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 |
|
||||||
Loading…
Add table
Add a link
Reference in a new issue