Spring MVC에서의 예외처리

seongmin·2022년 10월 25일
0

Spring

목록 보기
23/38
post-thumbnail
post-custom-banner

@ExceptionHandler

@ExceptionHandler
    public ResponseEntity handleException(MethodArgumentNotValidException e) {
        // (1)
        final List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        // (2)
        return new ResponseEntity<>(fieldErrors, HttpStatus.BAD_REQUEST);
        
  • (1)

MethodArgumentNotValidException 객체에서 getBindingResult().getFieldErrors() 를 통해 발생한 에러 정보를 확인할 수 있다.

  • (2)

(1)에서 얻은 에러 정보를 ResponseEntity 를 통해 Response Body 로 전달한다.

[
    {
        "codes": [
            "Email.memberPostDto.email",
            "Email.email",
            "Email.java.lang.String",
            "Email"
        ],
        "arguments": [
            {
                "codes": [
                    "memberPostDto.email",
                    "email"
                ],
                "arguments": null,
                "defaultMessage": "email",
                "code": "email"
            },
            [],
            {
                "arguments": null,
                "defaultMessage": ".*",
                "codes": [
                    ".*"
                ]
            }
        ],
        "defaultMessage": "올바른 형식의 이메일 주소여야 합니다",
        "objectName": "memberPostDto",
        "field": "email",
        "rejectedValue": "hgd@",
        "bindingFailure": false,
        "code": "Email"
    }
]

위와 같이, 유효성 검사 실패에 대한 에러 메시지를 구체적으로 전송해주기 때문에 클라이언트 입장에서는 이제 어느 곳에 문제가 있는지를 구체적으로 알 수 있게 되었다.

하지만, 굳이 알 수 없는 모든 정보를 포함한 메세지를 받을 필요성은 없어보인다.

문제가 되는 정보만 받아볼 수 있으면 더 효율적이지 않을까?

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.List;

@Getter
@AllArgsConstructor
public class ErrorResponse {
		// (1)
    private List<FieldError> fieldErrors;

    @Getter
    @AllArgsConstructor
    public static class FieldError {
        private String field;
        private Object rejectedValue;
        private String reason;
    }
}

DTO 클래스의 유효성 검증 실패 시, 실패한 필드(멤버 변수)에 대한 Error 정보만 담아서 응답으로 전송하기 위한 ErrorResponse 클래스다.

(1) 과 같이 한 개 이상의 유효성 검증에 실패한 필드의 에러 정보를 담기 위해서 List 객체를 이용하며, 이 한개의 필드 에러 정보는 FieldError 라는 별도의 static class를 ErrorResponse 클래스의 멤버 클래스로 정의했다.

@ExceptionHandler
    public ResponseEntity handleException(MethodArgumentNotValidException e) {
				// (1)
        final List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();

				// (2)
        List<ErrorResponse.FieldError> errors =
                fieldErrors.stream()
                            .map(error -> new ErrorResponse.FieldError(
                                error.getField(),
                                error.getRejectedValue(),
                                error.getDefaultMessage()))
                            .collect(Collectors.toList());

        return new ResponseEntity<>(new ErrorResponse(errors), HttpStatus.BAD_REQUEST);

Controller에 위의 코드를 추가해준다.

(1) 에서는 List를 통째로 ResponseEntity 클래스에 실어서 전달했지만

(2) 에서는 필요한 정보들만 선택적으로 골라서 ErrorResponse.FieldError 클래스에 담아서 List로 변환 후, List<ErrorResponse.FieldError>ResponseEntity 클래스에 실어서 전달하고 있다.

@ExceptionHandler 단점

@ExceptionHandler 애너테이션으로 에러 처리를 하게되면 다음과 같은 문제점이 발생할 수 있다.

  • 각각의 Controller 클래스에서 @ExceptionHandler 애너테이션을 사용하여 Request Body에 대한 유효성 검증 실패에 대한 에러 처리를 해야되므로 각 Controller 클래스마다 코드 중복이 발생한다.

  • Controller에서 처리해야 되는 예외(Exception)가 유효성 검증 실패에 대한 예외(MethodArgumentNotValidException)만 있는것이 아니기 때문에 하나의 Controller 클래스 내에서 @ExceptionHandler 를 추가한 에러 처리 핸들러 메서드가 늘어난다.


정리

  • Controller 클래스 레벨에서 @ExceptionHandler 애너테이션을 사용하면 해당 Controller에서 발생하는 예외를 처리할 수 있다.

  • 필요한 Error 정보만 담을 수 있는 Error 전용 Response 객체를 사용하면 클라이언트에게 조금 더 친절한 에러 정보를 제공할 수 있다.

  • @ExceptionHandler 애너테이션 방식은 Controller마다 동일하게 발생하는 예외 처리에 대한 중복 코드가 발생할 수 있다.

  • @ExceptionHandler 애너테이션 방식은 다양한 유형의 예외를 처리하기에는 적절하지 않은 방식이다.

post-custom-banner

0개의 댓글