[스프링 - MVC] 예외처리 - @ControllerAdvice



@ControllerAdvice

개요

  • 이전 글에서 ExceptionHandlerExceptionResolver 를 활용해서 API 예외처리를 하는 방법에 대해 알아보았다.
  • 해당 글에서 작성한 컨트롤러 ( ExceptionHandlerController 클래스 )의 문제점은 정상코드와 예외처리 코드가 하나의 컨트롤러에 섞여있다는 것이다.
  • 따라서, 정상코드와 예외처리코드를 분리해보자.


목표

  • 정상로직를 수행하는 컨트롤러와 예외처리 컨트롤러를 분리한다.


예시 코드

이전에 작성한 코드를 그대로 사용하여 분리한다.


예외 처리 코드: ExControllerAdvice 클래스

package example.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestControllerAdvice
public class ExControllerAdvice {

  @ResponseStatus(HttpStatus.BAD_REQUEST)
  @ExceptionHandler(IllegalArgumentException.class)
  public ErrorItem illegalExHandle(IllegalArgumentException e) {
    return new ErrorItem("BAD", e.getMessage());
  }

  @ExceptionHandler(MyException.class)
  public ResponseEntity<ErrorItem> myExHandler(MyException e) {
    ErrorItem errorItem = new ErrorItem("MY-EX", e.getMessage());
    return new ResponseEntity<>(errorItem, HttpStatus.BAD_REQUEST);
  }

  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  @ExceptionHandler
  public ErrorItem runtimeExHandle(RuntimeException e) {
    return new ErrorItem("EX", "내부오류");
  }

}
  • @RestControllerAdvice
    • 해당 애너테이션을 활용하여 예외처리 전용 클래스를 작성하였다.
    • 요청 및 결과는 이전 게시글의 내용과 동일하게 작동한다.


예외 발생 컨트롤러: ExceptionHandlerController 클래스

package example.exception;

import ...

@RestController
public class ExceptionHandlerController {

    @GetMapping("/ex/{exName}")
    public String throwExceptions(@PathVariable String exName) {
        if (exName.equals("illegal")) {
            throw new IllegalArgumentException("IllegalArgument 예외!");
        }
        if (exName.equals("my")) {
            throw new MyException();
        }
        if (exName.equals("runtime")) {
            throw new RuntimeException();
        }

        return "ok";
    }

}
  • 예외 처리 코드가 삭제되었다.



@ControllerAdvice 상세설명

기본 원리

  • @ControllerAdvice 는 대상으로 지정한 여러 컨트롤러에 @ExceptionHandler , @InitBinder 기능을 부여해주는 역할을 한다.
  • 대상을 지정하지 않으면 모든 컨트롤러에 적용된다. (글로벌 적용)
  • @RestControllerAdvice@ControllerAdvice 와 같고, @ResponseBody 가 추가되어 있다.


대상 컨트롤러 지정 방법

// @RestController 에만 적용한다.
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}

// 해당 패키지의 모든 컨트롤러에 적용한다.
@ControllerAdvice("org.example.controllers") public class ExampleAdvice2 {}

// 부모클래스나 자식클래스의 컨트롤러에 적용한다.
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}




  • 본 게시글은 김영한님의 강의를 토대로 정리한 글입니다.
  • 더 자세한 내용을 알고 싶으신 분들이 계신다면, 해당 강의를 수강하시는 것을 추천드립니다.