Skip to content

[REFACTOR] 공통 API 오류 응답 코드 체계 도입 #594

Description

@GiJungPark

Description

작업 배경

현재 API 오류 응답은 같은 ErrorResult(code, message) 구조를 사용하더라도 오류 경로에 따라 서비스 에러 코드의 정의 수준이 다릅니다.

  • DTO Bean Validation 실패와 잘못된 JSON 형식은 각각 COMMON-001, COMMON-002로 구분합니다.
  • PathVariable·RequestParam 검증, 필수 파라미터 누락, 타입 변환 실패, 업로드 용량 초과는 BAD_REQUESTcode로 사용합니다.
  • 분류되지 않은 DB 무결성 위반은 CONFLICTcode로 사용합니다.
  • 일반 인가 실패는 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 정리
  • MethodArgumentNotValidExceptionCOMMON-001로 처리
  • HttpMessageNotReadableExceptionCOMMON-002로 처리
  • ConstraintViolationExceptionHandlerMethodValidationExceptionCOMMON-003으로 처리
  • MissingServletRequestParameterExceptionCOMMON-004로 처리
  • MethodArgumentTypeMismatchExceptionCOMMON-005로 처리
  • MissingServletRequestPartExceptionCOMMON-006으로 처리
  • MissingRequestHeaderExceptionCOMMON-014로 처리
  • MaxUploadSizeExceededExceptionCOMMON-007로 처리
  • 매핑되지 않은 경로를 COMMON-008로 처리
  • HttpRequestMethodNotSupportedExceptionCOMMON-009로 처리
  • HttpMediaTypeNotSupportedExceptionCOMMON-010으로 처리
  • HttpMediaTypeNotAcceptableExceptionCOMMON-011로 처리
  • CustomAccessDeniedHandlerCOMMON-012 ErrorResult 본문을 반환하도록 변경
  • 별도 도메인 코드로 분류되지 않은 DataIntegrityViolationExceptionCOMMON-013으로 처리
  • 처리되지 않은 예외를 COMMON-999로 응답하는 최종 fallback 추가
  • BlockController를 포함해 Controller 파라미터 검증이 동일한 예외 계약을 사용하도록 정리
  • 동적 메시지와 공통 코드 조합 방식 공통화
  • 관련 단위·MockMvc·통합 테스트 추가
  • REST Docs 및 생성 OpenAPI named example 갱신
  • 공통 에러 코드 명명·사용·보안 기준 문서화
  • 기존 문서의 코드 형식 설명을 실제 기존 코드와 충돌하지 않도록 정정

제외 범위

  • 기존 도메인 비즈니스 오류 코드 변경
  • 기존 AUTH-* 인증 토큰 오류 코드 변경
  • DTO 필드나 PathVariable·RequestParam 각각의 개별 오류 코드 신설
  • ErrorResult 필드 추가 또는 필드별 오류 배열 추가
  • 응답에 traceId 추가
  • 업로드 용량 제한 설정값 변경
  • HTTP 400을 413 또는 422로 변경
  • /errorpermitAll로 변경하거나 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

  • COMMON-001~014, COMMON-999 정의
  • 요청 본문 검증·JSON 파싱 오류 처리
  • 경로·쿼리 파라미터 검증·누락·변환 오류 처리
  • 필수 요청 헤더 누락 오류 처리
  • multipart 누락·업로드 용량 오류 처리
  • 404·405·406·415 프로토콜 오류 처리
  • 일반 인가 실패 응답 본문 처리
  • DB 무결성 fallback 코드 처리
  • 예상하지 못한 500 fallback 처리
  • Controller 메서드 검증 방식 통일
  • 단위·MockMvc·통합 테스트 추가
  • REST Docs 및 OpenAPI named example 갱신
  • 공통 에러 코드 기준 문서화

Reference

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions