본문 바로가기
Spring

[Spring] @(Rest)ControllerAdvice 활용

by doodoom 2023. 4. 13.

0. 이 글을 쓰게된 이유

미션을 진행하며 (Rest)ControllerAdvice를 사용하여 예외를 처리하였다. 원래 (Rest)ControllerAdvice가 예외를 처리할 때에만 사용하는줄 알았는데 활용할 수 있는 부분이 많은 기능이었다. 그 부분에 대해 알아보기 위해 이 글을 쓰게 되었다.

1. @(Rest)ControllerAdvice란?

Spring 공식 문서에 따르면 @(Rest)ControllerAdvice가 붙은 빈은 모든 @Controller이 붙은 빈에서 사용하는 @ExceptionHandler, @InitBinder, @ModelAttribute(글 쓰기 쉽게 세가지 기능이라고 부르겠다.)가 붙은 메소드들을 한번에 처리할 수 있도록 해주는 기능이다.

Controller에서 @ExceptionHandler, @InitBinder, @ModelAttribute를 활용해서 각각의 기능을 처리할 수 있다. 이렇게 controller 내에서 정의된 세가지 기능들은 해당 controller에서만 적용된다. 만약 전역적으로 세가지 기능을 사용하고 싶다면 (Rest)ControllerAdvice를 활용하면 된다.

2. @(Rest)ControllerAdvice에서 사용할 수 있는 기능

위에서 얘기했듯이 (Rest)ControllerAdvice에서는 @ExceptionHandler, @InitBinder, @ModelAttribute를 사용할 수 있다. 간단하게 각각의 기능들에 대해서 알아보자.

2.1 @ExceptionHandler

@ExceptionHandler 는 @(Rest)Controller 가 적용된 Bean에서 발생하는 예외를 잡아서 하나의 메서드에서 처리해주는 기능이다.
만약 어떠한 예외처리를 모든 Controller에 대해 전역적으로 사용하고싶다면 @ExceptionHandler를 @ControllerAdvice가 붙은 빈에서 사용하면 된다.
아래는 예시이다.

@RestControllerAdvice
public class ControllerAdvice {

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<ExceptionDto> handleIllegalArgumentException(final IllegalArgumentException exception) {
        final ExceptionDto exceptionDto = new ExceptionDto(exception.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(exceptionDto);
    }
}

2.2 @InitBinder

@InitBinder는 @(Rest)Controller 가 적용된 Bean에서 바인딩 또는 검증 설정을 변경하고 싶을때 사용된다.
만약 그 바인딩 설정을 모든 Controller에 대해 전역적으로 사용하고싶다면 @InitBinder를 @ControllerAdvice가 붙은 빈에서 사용하면 된다.
아래는 예시이다.

@RestControllerAdvice
public class ControllerAdvice {

    @InitBinder("RaceResult")
    public void initBind(final WebDataBinder webDataBinder) {
        webDataBinder.setDisallowedFields("id");
    }
}

2.3 @ModelAttribute

@ModelAttirbute는 @Controller 가 적용된 Bean에서 웹 뷰로 넘겨주는 데이터 폼인 model의 attribute를 설정해주도록 한다. @ModelAttribute는 메소드와 파라미터에 붙을 수 있다.
만약 그 Model 설정을 모든 Controller에 대해 전역적으로 사용하고싶다면 @ModelAttribute @ControllerAdvice가 붙은 빈에서 사용하면 된다.
아래는 예시이다.

@ControllerAdvice
public class ControllerAdvice {

    @ModelAttribute
    public void handleRequest(HttpServletRequest request, Model model) {
        String requestURI = request.getRequestURI();

        model.addAttribute("appVer", Const.AppVer);
        model.addAttribute("uri", requestURI);
    }
}

이렇게 되면 모든 view의 model에서 appVer, uri는 공통적으로 사용할 수 있게 된다.

3. 주의점

@ControllerAdvice를 사용하게되면 주의할 점이 있다.
만약 @Controller에서 @ExceptionHandler, @InitBinder, @ModelAttribute를 사용하고 @ControllerAdvice에서 @ExceptionHandler, @InitBinder, @ModelAttribute를 사용하면 뭐가 우선순위를 가질까?
DispatcherServlet에서 Exception을 처리할 때 Controller에 있는 ExceptionHandler가 먼저 우선 순위를 가진다. 그래서 Controller에 있는 @ExceptionHandler가 먼저 실행되어서 처리하게 된다.