[Project] RestControllerAdvice를 통한 exception 관리

bin1225·2024년 11월 24일
0

Project_Only

목록 보기
6/9
post-thumbnail

페이지 수정은 작성 후 24시간 안에만 가능하도록 하기 위해서,
작성 후 24시간이 경과하였는지 확인하는 메소드를 작성하였다.

만약 24시간이 경과하였다면 exception을 발생시켜 사용자에게 알린다.

여기서 발생한 예외를 처리하는 방법이 나뉜다.

Exception 처리 방식

Spring 기본 예외 처리 방식

Spring은 기본적으로 예외를 처리하는 BasicErrorController를 구현해두었다.

별도의 예외를 처리하지 않으면, WAS까지 error가 전달된다.
그리고 WAS는 다시 BasicErrorController로 다시 에러에 대한 처리를 요청한다.

Spring 기본 Error 처리 요청 흐름

WAS(톰캣) -> 필터 -> 서블릿(디스패처 서블릿) -> 인터셉터 -> 컨트롤러
-> 컨트롤러(예외발생) -> 인터셉터 -> 서블릿(디스패처 서블릿) -> 필터 -> WAS(톰캣)
-> WAS(톰캣) -> 필터 -> 서블릿(디스패처 서블릿) -> 인터셉터 -> 컨트롤러(BasicErrorController)

이 방식의 문제점은 Error에 대한 구체적인 정의를 할 수 없다는 것,
Error처리를 위해 한 번 더 요청을 해야한다는 점이다.

Spring Exception Resolver 동작 방식

  • ResponseStatus
  • ResponseStatusException
  • ExceptionHandler
  • ControllerAdvice, RestControllerAdvice

가장 좋은 처리 방식 -> @RestControllerAdvice

4가지 방법 중 가장 좋은 방식이다.
기본 Error처리의 단점을 보완하고, Error처리를 보다 체계적으로 할 수 있도록 기능을 제공한다.

@ExceptionHandler

@ExceptionHandler 어노테이션을 추가함으로써, 특정 error에 대한 처리를 할 수 있다.

  @ExceptionHandler(NoSuchElementFoundException.class)
  public ResponseEntity<String> handleNoSuchElementFoundException(NoSuchElementFoundException exception) {
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(exception.getMessage());
  }

Exception이 발생하면 해당 exception에 대한 정보를 설정하여 ResponseEntity에 담아 클라이언트에게 전달할 수 있다.

즉, ErrorCode와 message에 대한 정보를 직접 설정하여 전달함으로써 Error발생에 대한 구체적인 정보를 제공한다.

@RestControllerAdvice

각 Controller에서 예외를 처리한다면, 중복 코드가 발생한다.
이를 개선하기 위해 하나의 컨트롤러에 @RestControllerAdvice 어노테이션을 추가하여 여러 컨트롤러에 전역적으로 ExceptionHandler를 적용한다.

package com.project.only.error;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(RestApiException.class)
    public ResponseEntity<Object> handleCustomException(RestApiException e) {
        ErrorCode errorCode = e.getErrorCode();
        return handleExceptionInternal(errorCode);
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<Object> handleIllegalArgument(IllegalArgumentException e) {
        log.warn("handleIllegalArgument", e);
        ErrorCode errorCode = CommonErrorCode.INVALID_PARAMETER;
        return handleExceptionInternal(errorCode);
    }

    @ExceptionHandler({Exception.class})
    public ResponseEntity<Object> handleAllException(Exception ex) {
        log.warn("handleAllException", ex);
        ErrorCode errorCode = CommonErrorCode.INTERNAL_SERVER_ERROR;
        return handleExceptionInternal(errorCode);
    }

    private ResponseEntity<Object> handleExceptionInternal(ErrorCode errorCode) {
        return ResponseEntity.status(errorCode.getHttpStatus())
                .body(makeErrorResponse(errorCode));
    }

    private ErrorResponse makeErrorResponse(ErrorCode errorCode) {
        return ErrorResponse.builder()
                .code(errorCode.name())
                .message(errorCode.getMessage())
                .build();
    }


}

이런식으로 ControllerAdvice를 이용함으로써

  1. 하나의 클래스로 모든 컨트롤러에 대해 전역적으로 예외 처리가 가능해진다.
  2. 직접 정의한 에러 응답을 일관성있게 클라이언트에게 내려줄 수 있다.
  3. 별도의 try-catch문이 없어 코드의 가독성이 높아진다.

Reference

0개의 댓글