Description
작업 배경
현재 API 오류 응답은 같은 ErrorResult(code, message) 구조를 사용하더라도 오류 경로에 따라 서비스 에러 코드의 정의 수준이 다릅니다.
- DTO Bean Validation 실패와 잘못된 JSON 형식은 각각
COMMON-001, COMMON-002로 구분합니다.
- PathVariable·RequestParam 검증, 필수 파라미터 누락, 타입 변환 실패, 업로드 용량 초과는
BAD_REQUEST를 code로 사용합니다.
- 분류되지 않은 DB 무결성 위반은
CONFLICT를 code로 사용합니다.
- 일반 인가 실패는 403 상태만 있고
ErrorResult 본문이 없습니다.
- 처리되지 않은 Spring MVC 예외와 예상하지 못한 예외는 ERROR 디스패치 과정에서
/error가 다시 인증 대상이 되어, 원래 404·405·406·415·500이어야 할 오류가 401 AUTH-001로 바뀔 수 있습니다.
BlockController는 메서드 검증 방식 차이로 @Positive 위반이 기존 ConstraintViolationException 처리에 포함되지 않을 수 있습니다.
HTTP 상태 이름은 클라이언트가 원인을 안정적으로 구분할 수 있는 서비스 에러 코드가 아닙니다. 도메인과 무관하게 여러 API에서 공통으로 발생할 수 있고 서버가 발생 조건을 식별할 수 있는 오류를 COMMON-* 코드로 정의합니다.
목표 상태
- 도메인과 무관하게 발생하는 예측 가능한 요청·프로토콜·인가·인프라 오류가 모두
COMMON-* 서비스 에러 코드로 응답됩니다.
- 예측하지 못한 서버 오류도 응답 구조가 깨지거나 인증 오류로 바뀌지 않고
COMMON-999로 응답됩니다.
- 모든 오류 응답은
ErrorResult(code, message) 구조를 유지합니다.
- HTTP 상태는 기존 계약을 우선 유지하며, 각 오류의 실제 의미에 맞는 상태를 보존합니다.
- 도메인 비즈니스 오류와 인증 토큰 오류는 기존 도메인 코드 및
AUTH-* 코드를 유지합니다.
- 서버 내부 예외 메시지, SQL, 제약조건 이름, 스택 트레이스는 응답에 노출하지 않습니다.
공통 에러 코드
| 코드 |
HTTP 상태 |
용도 |
COMMON-001 |
400 Bad Request |
@Valid @RequestBody DTO Bean Validation 실패 |
COMMON-002 |
400 Bad Request |
요청 본문 JSON 파싱 실패 |
COMMON-003 |
400 Bad Request |
PathVariable·RequestParam Bean Validation 제약 위반 |
COMMON-004 |
400 Bad Request |
필수 RequestParam 누락 |
COMMON-005 |
400 Bad Request |
PathVariable·RequestParam 타입 또는 Enum 변환 실패 |
COMMON-006 |
400 Bad Request |
필수 multipart RequestPart 누락 |
COMMON-007 |
400 Bad Request |
업로드 파일 또는 전체 요청 용량 초과 |
COMMON-008 |
404 Not Found |
매핑된 API 엔드포인트 없음 |
COMMON-009 |
405 Method Not Allowed |
엔드포인트가 지원하지 않는 HTTP 메서드 |
COMMON-010 |
415 Unsupported Media Type |
지원하지 않는 Content-Type |
COMMON-011 |
406 Not Acceptable |
지원하지 않는 Accept 타입 |
COMMON-012 |
403 Forbidden |
Spring Security의 일반 인가 실패 |
COMMON-013 |
409 Conflict |
별도 도메인 코드로 분류되지 않은 DB 무결성 위반 |
COMMON-014 |
400 Bad Request |
필수 RequestHeader 누락 |
COMMON-999 |
500 Internal Server Error |
별도 처리되지 않은 예상하지 못한 서버 오류 |
코드 적용 기준
- 어느 도메인의 API에서도 같은 의미로 발생할 수 있는 요청 처리·HTTP 프로토콜·프레임워크 오류는
COMMON-*를 사용합니다.
- 도메인 개념을 알아야 원인과 대응을 결정할 수 있는 오류는 기존 도메인 코드를 사용합니다.
- 존재하지 않는 작품은
NOVEL-001
- 컬렉션 소유권 위반은
COLLECTION-005
- 닉네임 중복은
USER-009
- 토큰 만료·유효하지 않은 토큰·잘못된 토큰 유형은 기존
AUTH-* 코드를 사용합니다.
- 같은 코드 안에서 구체적인 실패 원인만 달라지는 경우 코드는 고정하고 검증 애너테이션 메시지 등 안전한 메시지만 동적으로 조합합니다.
- 클라이언트의 대응이 같다면 필드·제약조건마다 코드를 추가하지 않고
message로 구분합니다.
COMMON-999의 메시지는 고정 문자열만 사용하고 원본 예외 내용은 로그에만 기록합니다.
작업 범위
- 공통 요청·프로토콜·인가·서버 오류를 표현하는
ICustomError 구현 enum 정리
MethodArgumentNotValidException을 COMMON-001로 처리
HttpMessageNotReadableException을 COMMON-002로 처리
ConstraintViolationException과 HandlerMethodValidationException을 COMMON-003으로 처리
MissingServletRequestParameterException을 COMMON-004로 처리
MethodArgumentTypeMismatchException을 COMMON-005로 처리
MissingServletRequestPartException을 COMMON-006으로 처리
MissingRequestHeaderException을 COMMON-014로 처리
MaxUploadSizeExceededException을 COMMON-007로 처리
- 매핑되지 않은 경로를
COMMON-008로 처리
HttpRequestMethodNotSupportedException을 COMMON-009로 처리
HttpMediaTypeNotSupportedException을 COMMON-010으로 처리
HttpMediaTypeNotAcceptableException을 COMMON-011로 처리
CustomAccessDeniedHandler가 COMMON-012 ErrorResult 본문을 반환하도록 변경
- 별도 도메인 코드로 분류되지 않은
DataIntegrityViolationException을 COMMON-013으로 처리
- 처리되지 않은 예외를
COMMON-999로 응답하는 최종 fallback 추가
BlockController를 포함해 Controller 파라미터 검증이 동일한 예외 계약을 사용하도록 정리
- 동적 메시지와 공통 코드 조합 방식 공통화
- 관련 단위·MockMvc·통합 테스트 추가
- REST Docs 및 생성 OpenAPI named example 갱신
- 공통 에러 코드 명명·사용·보안 기준 문서화
- 기존 문서의 코드 형식 설명을 실제 기존 코드와 충돌하지 않도록 정정
제외 범위
- 기존 도메인 비즈니스 오류 코드 변경
- 기존
AUTH-* 인증 토큰 오류 코드 변경
- DTO 필드나 PathVariable·RequestParam 각각의 개별 오류 코드 신설
ErrorResult 필드 추가 또는 필드별 오류 배열 추가
- 응답에 traceId 추가
- 업로드 용량 제한 설정값 변경
- HTTP 400을 413 또는 422로 변경
/error를 permitAll로 변경하거나 Security 인가 정책을 변경
- 기존 도메인 404·403 코드의 정보 노출 정책 변경
호환성 및 보안
- 기존에
BAD_REQUEST 또는 CONFLICT 문자열로 분기하는 클라이언트가 있다면 COMMON-* 전환이 필요합니다.
- 기존 HTTP 상태와 사용자에게 전달하던 안전한 검증 메시지는 유지합니다.
- DB 제약조건 이름과 root cause는 분류에만 사용하고 응답에는 노출하지 않습니다.
COMMON-999는 고정 메시지로만 응답하며 전체 예외는 서버 로그에 기록합니다.
- 도메인 리소스 미존재와 API 엔드포인트 미존재를 구분합니다.
완료 조건
- 위 표의 모든 공통 오류가 지정된 HTTP 상태와
COMMON-* 코드로 응답합니다.
BAD_REQUEST, CONFLICT 등 HTTP 상태 이름을 ErrorResult.code로 사용하는 경로가 남아 있지 않습니다.
- 일반 403 응답에
COMMON-012 코드와 메시지가 포함됩니다.
- 404·405·406·415와 예상하지 못한 500이 401
AUTH-001로 바뀌지 않습니다.
BlockController를 포함한 PathVariable·RequestParam 제약 위반이 일관되게 COMMON-003으로 응답합니다.
- DTO 검증 실패 시 구체적인 검증 메시지가 유지됩니다.
- 기존 도메인 예외와 인증 토큰 예외의 코드 및 HTTP 상태가 변경되지 않습니다.
COMMON-999 응답에 내부 예외 메시지·SQL·스택 트레이스가 포함되지 않습니다.
- 각 공통 오류를 실제 HTTP 요청으로 재현하는 테스트가 있습니다.
- 관련 예외 처리 테스트와 API 문서 테스트가 통과합니다.
- 생성 OpenAPI 명세에서 같은 HTTP 상태의 공통 오류가 서로 다른 named example로 확인됩니다.
./gradlew build -x test와 ./gradlew test가 통과합니다. 로컬 yml 또는 외부 인프라 부재로 실패하면 설정 파일을 만들지 않고 원인을 보고합니다.
To-Do
Reference
Description
작업 배경
현재 API 오류 응답은 같은
ErrorResult(code, message)구조를 사용하더라도 오류 경로에 따라 서비스 에러 코드의 정의 수준이 다릅니다.COMMON-001,COMMON-002로 구분합니다.BAD_REQUEST를code로 사용합니다.CONFLICT를code로 사용합니다.ErrorResult본문이 없습니다./error가 다시 인증 대상이 되어, 원래 404·405·406·415·500이어야 할 오류가 401AUTH-001로 바뀔 수 있습니다.BlockController는 메서드 검증 방식 차이로@Positive위반이 기존ConstraintViolationException처리에 포함되지 않을 수 있습니다.HTTP 상태 이름은 클라이언트가 원인을 안정적으로 구분할 수 있는 서비스 에러 코드가 아닙니다. 도메인과 무관하게 여러 API에서 공통으로 발생할 수 있고 서버가 발생 조건을 식별할 수 있는 오류를
COMMON-*코드로 정의합니다.목표 상태
COMMON-*서비스 에러 코드로 응답됩니다.COMMON-999로 응답됩니다.ErrorResult(code, message)구조를 유지합니다.AUTH-*코드를 유지합니다.공통 에러 코드
COMMON-001@Valid @RequestBodyDTO Bean Validation 실패COMMON-002COMMON-003COMMON-004COMMON-005COMMON-006COMMON-007COMMON-008COMMON-009COMMON-010COMMON-011COMMON-012COMMON-013COMMON-014COMMON-999코드 적용 기준
COMMON-*를 사용합니다.NOVEL-001COLLECTION-005USER-009AUTH-*코드를 사용합니다.message로 구분합니다.COMMON-999의 메시지는 고정 문자열만 사용하고 원본 예외 내용은 로그에만 기록합니다.작업 범위
ICustomError구현 enum 정리MethodArgumentNotValidException을COMMON-001로 처리HttpMessageNotReadableException을COMMON-002로 처리ConstraintViolationException과HandlerMethodValidationException을COMMON-003으로 처리MissingServletRequestParameterException을COMMON-004로 처리MethodArgumentTypeMismatchException을COMMON-005로 처리MissingServletRequestPartException을COMMON-006으로 처리MissingRequestHeaderException을COMMON-014로 처리MaxUploadSizeExceededException을COMMON-007로 처리COMMON-008로 처리HttpRequestMethodNotSupportedException을COMMON-009로 처리HttpMediaTypeNotSupportedException을COMMON-010으로 처리HttpMediaTypeNotAcceptableException을COMMON-011로 처리CustomAccessDeniedHandler가COMMON-012ErrorResult본문을 반환하도록 변경DataIntegrityViolationException을COMMON-013으로 처리COMMON-999로 응답하는 최종 fallback 추가BlockController를 포함해 Controller 파라미터 검증이 동일한 예외 계약을 사용하도록 정리제외 범위
AUTH-*인증 토큰 오류 코드 변경ErrorResult필드 추가 또는 필드별 오류 배열 추가/error를permitAll로 변경하거나 Security 인가 정책을 변경호환성 및 보안
BAD_REQUEST또는CONFLICT문자열로 분기하는 클라이언트가 있다면COMMON-*전환이 필요합니다.COMMON-999는 고정 메시지로만 응답하며 전체 예외는 서버 로그에 기록합니다.완료 조건
COMMON-*코드로 응답합니다.BAD_REQUEST,CONFLICT등 HTTP 상태 이름을ErrorResult.code로 사용하는 경로가 남아 있지 않습니다.COMMON-012코드와 메시지가 포함됩니다.AUTH-001로 바뀌지 않습니다.BlockController를 포함한 PathVariable·RequestParam 제약 위반이 일관되게COMMON-003으로 응답합니다.COMMON-999응답에 내부 예외 메시지·SQL·스택 트레이스가 포함되지 않습니다../gradlew build -x test와./gradlew test가 통과합니다. 로컬 yml 또는 외부 인프라 부재로 실패하면 설정 파일을 만들지 않고 원인을 보고합니다.To-Do
COMMON-001~014,COMMON-999정의Reference