장쫄깃 기술블로그

[Spring Boot] @RestControllerAdvice 란 본문

Spring Framework/Spring Boot

[Spring Boot] @RestControllerAdvice 란

장쫄깃 2022. 9. 25. 20:49
728x90


들어가며


자사 서드파티 API를 개발하는 업무를 담당했을 때, 처음에는 모든 예외처리를 try-catch로 처리하였다. 그렇다 보니 불필요한 중복 코드들이 많아지고 가독성도 떨어졌다. 또, 코드가 점점 복잡해져 생산성도 떨어졌다. 확실한 건 중복되는 코드들이 너무 많았다. 이러한 문제를 해결하기 위해 고민하고 찾아본 결과 @ControllerAdvice, @RestControllerAdvice를 발견했다.

 

 

@ControllerAdvice 란


/**
 * Specialization of {@link Component @Component} for classes that declare
 * {@link ExceptionHandler @ExceptionHandler}, {@link InitBinder @InitBinder}, or
 * {@link ModelAttribute @ModelAttribute} methods to be shared across
 * multiple {@code @Controller} classes.
 *
 * <p>Classes annotated with {@code @ControllerAdvice} can be declared explicitly
 * as Spring beans or auto-detected via classpath scanning. All such beans are
 * sorted based on {@link org.springframework.core.Ordered Ordered} semantics or
 * {@link org.springframework.core.annotation.Order @Order} /
 * {@link javax.annotation.Priority @Priority} declarations, with {@code Ordered}
 * semantics taking precedence over {@code @Order} / {@code @Priority} declarations.
 * {@code @ControllerAdvice} beans are then applied in that order at runtime.
 * Note, however, that {@code @ControllerAdvice} beans that implement
 * {@link org.springframework.core.PriorityOrdered PriorityOrdered} are <em>not</em>
 * given priority over {@code @ControllerAdvice} beans that implement {@code Ordered}.
 * In addition, {@code Ordered} is not honored for scoped {@code @ControllerAdvice}
 * beans &mdash; for example if such a bean has been configured as a request-scoped
 * or session-scoped bean.  For handling exceptions, an {@code @ExceptionHandler}
 * will be picked on the first advice with a matching exception handler method. For
 * model attributes and data binding initialization, {@code @ModelAttribute} and
 * {@code @InitBinder} methods will follow {@code @ControllerAdvice} order.
 *
 * <p>Note: For {@code @ExceptionHandler} methods, a root exception match will be
 * preferred to just matching a cause of the current exception, among the handler
 * methods of a particular advice bean. However, a cause match on a higher-priority
 * advice will still be preferred over any match (whether root or cause level)
 * on a lower-priority advice bean. As a consequence, please declare your primary
 * root exception mappings on a prioritized advice bean with a corresponding order.
 *
 * <p>By default, the methods in an {@code @ControllerAdvice} apply globally to
 * all controllers. Use selectors such as {@link #annotations},
 * {@link #basePackageClasses}, and {@link #basePackages} (or its alias
 * {@link #value}) to define a more narrow subset of targeted controllers.
 * If multiple selectors are declared, boolean {@code OR} logic is applied, meaning
 * selected controllers should match at least one selector. Note that selector checks
 * are performed at runtime, so adding many selectors may negatively impact
 * performance and add complexity.
 *
 * @author Rossen Stoyanchev
 * @author Brian Clozel
 * @author Sam Brannen
 * @since 3.2
 * @see org.springframework.stereotype.Controller
 * @see RestControllerAdvice
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {

 

@ControllerAdvice@ExceptionHandler, @ModelAttribute, @InitBinder가 적용된 메소드들에 AOP를 적용하여 Controller단에 적용하기 위해 고안된 어노테이션이다. 클래스에 선언되며, 모든 @Controller에 대한 전역적으로 발생할 수 있는 예외를 잡아서 처리할 수 있다.

 

 

@RestControllerAdvice 란


/**
 * A convenience annotation that is itself annotated with
 * {@link ControllerAdvice @ControllerAdvice}
 * and {@link ResponseBody @ResponseBody}.
 *
 * <p>Types that carry this annotation are treated as controller advice where
 * {@link ExceptionHandler @ExceptionHandler} methods assume
 * {@link ResponseBody @ResponseBody} semantics by default.
 *
 * <p><b>NOTE:</b> {@code @RestControllerAdvice} is processed if an appropriate
 * {@code HandlerMapping}-{@code HandlerAdapter} pair is configured such as the
 * {@code RequestMappingHandlerMapping}-{@code RequestMappingHandlerAdapter} pair
 * which are the default in the MVC Java config and the MVC namespace.
 *
 * @author Rossen Stoyanchev
 * @author Sam Brannen
 * @since 4.3
 * @see RestController
 * @see ControllerAdvice
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {

@RestControllerAdvice@ControllerAdvice와 @ResponseBody를 합친 어노테이션이다. @ControllerAdvice의 역할을 수행하고, @ResponseBody를 통해 객체를 리턴할 수 있다.

 

따라서 단순히 예외 처리를 하고 싶다면 @ControllerAdvice를, 응답으로 객체를 리턴해야 한다면 @RestControllerAdvice를 적용하면 된다.

 

위 두 어노테이션 모두 적용 범위를 클래스나 패키지 단위로 제한할 수 있으며, 아래와 같이 사용하면 된다.

@RestControllerAdvice(basePackageClasses = TestExceptionController.class)
public class ApiExceptionAdvice {
	// ...
}
@RestControllerAdvice(basePackages = "com.jdh.restControllerAdvice.controller")
public class ApiExceptionAdvice {
	// ...
}

 

 

@ExceptionHandler 란


@ExceptionHandler 어노테이션을 메소드에 선언하고 특정 예외 클래스를 지정해주면 해당 예외가 발생했을 때 메소드에 정의한 로직으로 처리할 수 있다. @ControllerAdvice 또는 @RestControllerAdvice에 정의된 메소드가 아닌 일반 컨트롤러 단에 존재하는 메소드에 선언할 경우, 해당 Controller에만 적용된다.

 

@Service 등의 빈에서는 적용되지 않는다.

 

 

정리하며


이번 게시글에선 @ControllerAdvice, @RestControllerAdvice에 대해 알아보았다. 다음 게시글에선 해당 어노테이션을 사용하여 예외 처리하는 방법에 대해서 알아보겠다.

 

 


참고

https://javachoi.tistory.com/253

https://velog.io/@banjjoknim/RestControllerAdvice

728x90