Spring 경계와 계약 아키텍처 정의 (role-ta-001)
This commit is contained in:
parent
a6cb360317
commit
ce8732abc8
1 changed files with 132 additions and 0 deletions
132
docs/adr/ADR-002-error-contract.md
Normal file
132
docs/adr/ADR-002-error-contract.md
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
# 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<FieldError> details
|
||||||
|
) {
|
||||||
|
public record FieldError(String field, Object rejectedValue, String message) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalExceptionHandler.java
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler(BusinessException.class)
|
||||||
|
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex, HttpServletRequest request) {
|
||||||
|
ErrorResponse response = ErrorResponse.of(ex, request.getRequestURI());
|
||||||
|
return ResponseEntity.status(ex.getHttpStatus()).body(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public ResponseEntity<ErrorResponse> 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:**
|
||||||
|
- 오류 응답 클래스 추가 작성 필요
|
||||||
|
- 기존 예외 처리 코드 마이그레이션 필요
|
||||||
Loading…
Add table
Add a link
Reference in a new issue