Spring/Spring MVC

[Spring MVC] 6. Error Response

noahkim_ 2025. 4. 12. 14:59

1. Error Response

 

2. 핵심 개념 및 클래스 

개념 설명
ProblemDetail
RFC 9457에 정의된 문제 상세 형식을 표현하는 클래스.
표준 필드와 커스텀 필드를 포함할 수 있음.
ErrorResponse
HTTP 상태 코드, 헤더, RFC 9457 형식의 바디를 포함하는 오류 응답 계약.
Spring MVC의 모든 예외가 이를 구현함.
ErrorResponseException
ErrorResponse를 구현한 기본 클래스.
사용자 정의 예외에서 상속해 사용 가능.
ResponseEntityExceptionHandler
예외를 처리하고 ProblemDetail 형식으로 응답을 렌더링하는 데 사용할 수 있는 기본 클래스.
@ControllerAdvice와 함께 사용됨.

 

3. 에러 응답 렌더링 방식

  • @ExceptionHandler나 @RequestMapping 메서드에서 ProblemDetail 또는 ErrorResponse 타입의 객체를 반환하면, RFC 9457 규격에 맞는 JSON 에러 응답으로 자동 렌더링됩니다.

 

예제) @ExceptionHandler 구현

더보기
@ExceptionHandler(ResourceNotFoundException.class)
public ProblemDetail handleNotFound(ResourceNotFoundException ex, HttpServletRequest request) {
    ProblemDetail problemDetail = ProblemDetail.forStatus(HttpStatus.NOT_FOUND);
    problemDetail.setTitle("리소스를 찾을 수 없습니다");
    problemDetail.setDetail(ex.getMessage());
    return problemDetail;
}

 

필드

필드명 설명
status HTTP 상태 코드 설정
instance
오류가 발생한 요청의 URL 경로가 자동으로 설정됨 (명시하지 않으면 현재 경로로 설정됨)

 

예시) ProblemDetail 반환 (application/problem+json)

더보기
{
  "type": "about:blank",
  "title": "리소스를 찾을 수 없습니다",
  "status": 404,
  "detail": "해당 ID의 사용자를 찾을 수 없습니다",
  "instance": "/api/users/123"
}

 

컨텐츠 협상

  • 클라이언트가 요청 헤더에 Accept를 application/problem+json으로 요청하면, Spring은 이를 우선 사용해 RFC 9457 응답 형식으로 응답합니다.

 

4. 예외 응답 전역 처리 설정

  • @ControllerAdvice와 @ExceptionHandler로 전역 예외 처리 설정 가능

 

예제) HttpRequestMethodNotSupportedException 예외 처리

더보기
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ProblemDetail methodNotFound(HttpRequestMethodNotSupportedException ex) {
        ProblemDetail problemDetail = ProblemDetail.forStatus(NOT_FOUND);
        problemDetail.setTitle("지원하지 않는 HTTP 메서드 요청입니다.");
        problemDetail.setDetail(ex.getMessage());
        return problemDetail;
    }
}

 

5. 메시지 국제화

  • Spring MVC의 예외는 ErrorResponse 인터페이스 구현체입니다.
  • 각 예외 클래스는 국제화 처리를 위한 메시지 코드를 자동으로 보냅니다.

 

필드

필드명 의미 메시지 코드 형식
type 해당 문제의 식별자 (URL 형식) problemDetail.type.[예외 클래스명 (FQCN)]
title 간단한 문제 요약. problemDetail.title.[예외 클래스명 (FQCN)]
detail 해당 문제에 대한 구체적인 설명. problemDetail.[예외 클래스명 (FQCN)][접미사 (optional)]
  • 해당 값들은 MessageSource를 통해 메시지로 변환됩니다.

 

예제) 메시지 코드 패턴

더보기
problemDetail.type.org.springframework.web.HttpRequestMethodNotSupportedException=https://example.com/errors/method-not-supported
problemDetail.title.org.springframework.web.HttpRequestMethodNotSupportedException=요청 메서드가 지원되지 않습니다
problemDetail.org.springframework.web.HttpRequestMethodNotSupportedException=요청하신 메서드 {0}는 지원되지 않습니다. 지원되는 메서드는 {1}입니다.

 

예외 별 메시지 코드 인자

예외 클래스 메시지 코드 메시지 코드 인자
AsyncRequestTimeoutException 기본만 있음 없음
ConversionNotSupportedException 기본 {0} 속성명, {1} 속성값
HandlerMethodValidationException 기본
{0} 모든 validation 에러 목록
HttpMediaTypeNotAcceptableException 기본 + .parseError
{0} 지원되는 미디어 타입 리스트
HttpMediaTypeNotSupportedException 기본 + .parseError
{0} 지원되지 않는 타입, {1} 지원되는 타입 리스트
HttpMessageNotReadableException 기본 없음
HttpMessageNotWritableException 기본 없음
HttpRequestMethodNotSupportedException 기본
{0} 현재 요청 메서드, {1} 지원되는 메서드 리스트
MethodArgumentNotValidException 기본
{0} 글로벌 에러 목록, {1} 필드 에러 목록
MissingRequestHeaderException 기본 {0} 헤더 이름
MissingServletRequestParameterException 기본 {0} 파라미터 이름
MissingMatrixVariableException 기본 {0} 매트릭스 변수 이름
MissingPathVariableException 기본 {0} 경로 변수 이름
MissingRequestCookieException 기본 {0} 쿠키 이름
MissingServletRequestPartException 기본 {0} multipart 파트 이름
NoHandlerFoundException 기본 없음
NoResourceFoundException 기본 없음
TypeMismatchException 기본 {0} 속성명, {1} 값
UnsatisfiedServletRequestParameterException 기본 {0} 파라미터 조건 리스트

 

클라이언트 언어 결정

구분 설명 예시
클라이언트 설정 클라이언트가 선호하는 언어를 HTTP 헤더로 서버에 전달함
Accept-Language: ko-KR
Accept-Language: en-US
Accept-Language: fr-FR
LocaleResolver 스프링에서 클라이언트의 Locale을 결정하는 전략을 담당함
AcceptHeaderLocaleResolver
Accept-Language를 기반으로 Locale 자동 결정
LocaleChangeInterceptor 특정 파라미터 또는 헤더를 통해 Locale을 변경 가능하게 해주는 인터셉터
Accept-Language로 자동 변경 
쿼리 파라미터(?lang=ko)로 변경 
메시지 선택 방식 MessageSource가 현재 Locale에 맞는 파일을 선택하여 메시지를 제공
messages_{locale}.properties 
Locale이 ko_KR이면 messages_ko.properties 사용

 

예제

예제) HttpRequestMethodNotSupportedException 예외 처리

더보기

1. MessageSource 등록

@Bean
public MessageSource messageSource() {
    ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
    ms.setBasename("messages");
    ms.setDefaultEncoding("UTF-8");
    return ms;
}

 

2. LocaleResolver 등록

@Bean
public LocaleResolver localeResolver() {
    return new AcceptHeaderLocaleResolver();  // Accept-Language 헤더에 따라 Locale을 결정
}

 

3. LocaleChangeInterceptor 추가

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LocaleChangeInterceptor());
}

 

4. message.properties 파일 생성 및 정의

message_ko.properties

problemDetail.type.org.springframework.web.HttpRequestMethodNotSupportedException=https://example.com/errors/method-not-supported
problemDetail.title.org.springframework.web.HttpRequestMethodNotSupportedException=요청 메서드가 지원되지 않습니다
problemDetail.org.springframework.web.HttpRequestMethodNotSupportedException=요청하신 메서드 {0}는 지원되지 않습니다. 지원되는 메서드는 {1}입니다.

 

message_en.properties

problemDetail.type.org.springframework.web.HttpRequestMethodNotSupportedException=https://example.com/errors/method-not-supported
problemDetail.title.org.springframework.web.HttpRequestMethodNotSupportedException=HTTP method not supported
problemDetail.org.springframework.web.HttpRequestMethodNotSupportedException=The requested method {0} is not supported. Supported methods are {1}.

  

5. 전역 예외 처리 컨트롤러에 핸들러 등록

@RestControllerAdvice
public class GlobalExceptionHandler {

    private MessageSource messageSource;

    public GlobalExceptionHandler(MessageSource messageSource) {
        this.messageSource = messageSource;
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<ProblemDetail> methodNotFound(HttpRequestMethodNotSupportedException ex, Locale locale) {
        String type = messageSource.getMessage("problemDetail.type."+ex.getClass().getName(), null, locale);
        String title = messageSource.getMessage("problemDetail.title."+ex.getClass().getName(), null, locale);
        String detail = messageSource.getMessage("problemDetail."+ex.getClass().getName(), new Object[]{ex.getMethod(), toStringDelimiter(ex.getSupportedMethods())}, locale);

        ProblemDetail problemDetail = ProblemDetail.forStatus(METHOD_NOT_ALLOWED);
        problemDetail.setType(URI.create(type));
        problemDetail.setTitle(title);
        problemDetail.setDetail(detail);

        return new ResponseEntity<>(problemDetail, METHOD_NOT_ALLOWED);
    }

    private String toStringDelimiter(String[] supportedMethods) {
        return Arrays.stream(supportedMethods).collect(Collectors.joining(", "));
    }
}

 

6. 클라이언트 예외 처리

  • 클라이언트 측에서는 다음 예외를 활용해 에러 응답을 파싱할 수 있습니다:
클라이언트 예외 클래스 설명
WebClient WebClientResponseException HTTP 상태 코드가 4xx 또는 5xx인 응답을 받을 때 발생
RestTemplate RestClientResponseException RestTemplate 호출 중 4xx/5xx 오류가 발생했을 때 발생하는 최상위 예외 클래스

 

 

출처

'Spring > Spring MVC' 카테고리의 다른 글

[Tomcat] 1. Introduction  (0) 2025.04.14
[Spring MVC] 7. HTTP Caching  (0) 2025.04.14
[Spring MVC] 3. HTTP Message Conversion  (0) 2025.04.12
[Spring MVC] 2. Filters  (0) 2025.04.11
[Spring MVC] 4-3. Handler Methods: Controller Advice  (0) 2023.10.17