spring, ControllerAdvice와 RestControllerAdivce의 동시 적용의 문제와 해소
rest api와 view 렌더링 컨트롤러가 동시에 있을 때, 각각의 예외 어드바이스 적용이 안된다.
- 한 프로젝트에 rest api와 view 렌더링 컨트롤러 두 개가 있다. 가능하면 분리하면 좋을 테지만 일단은 한 프로젝트와 한 어플리케이션에 동작한다.
- 컨트롤러에 대한 ExceptionHandler를 @ControllerAdvice와 @RestControllerAdvice로 구현하였다.
-
그런데 문제가 발생했다. 모든 예외가 @RestControllerAdvice를 통해 동작하였다.
- rest와 view를 위한 각각의 어드바이스
@Slf4j
@ControllerAdvice
public class ViewExceptionHandler {
@ExceptionHandler
public void exException(Exception e, HttpServletResponse response) {
log.info("ViewExceptionHandler working! exception message : {}", e.getMessage());
response.setStatus(444);
HtmlRenderingUtil.staticViewWriter(response, "static/view/myerror.html"); // response.getWriter() html을 렌더링하는 로직.
}
}
@Slf4j
@RestControllerAdvice
public class RestExceptionHandler {
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler
public Map<String, Object> exException(Exception e) {
log.info("RestExceptionHandlerForTest working! exception message : {}", e.getMessage());
return Map.of("message", "api error 발생! for test!~!");
}
}
- RestController와 Controller
@RestController
@RequestMapping("/rest")
public class RestsController {
@GetMapping("/api")
public ResponseEntity<Map<String, String>> greeting(){
throw new RuntimeException("rest api 요청!");
}
}
@Slf4j
@Controller
@RequestMapping("/view")
public class ViewController {
@GetMapping("/page")
public String greeting(){
throw new RuntimeException("view page 요청!");
}
@GetMapping("/api")
public @ResponseBody ResponseEntity<Map<String, String>> api(){
throw new RuntimeException("view - api의 ResponseBody 요청!");
}
}
- ViewExceptionHandler가 동작하기를 바라는 엔드포인트
- /view/page
- /view/api
- RestExceptionHandler가 동작하기를 바라는 엔드포인트
- /rest/api
- 하지만 모든 예외가 RestExceptionHandler로 처리되었다.
해소 : ControllerAdvice만 사용한다.
- 다만 아래의 형태로 문제를 해결할 수 있었다.
@Order(Ordered.HIGHEST_PRECEDENCE) // Order가 없을 경우 ViewExceptionHandler만 동작하였다.
@ControllerAdvice(annotations = RestController.class)
public class RestExceptionHandler {
// 생략
}
- RestControllerAdvice와 ControllerAdvice가 왜 동시에 동작하는지는 확인하지 못했다. 다만 위의 형태를 사용하면 원하는 방향대로 동작함은 확인하였다.
- 이러한 문제가 발생하는 이유를 아시는 분 있으면 공유 부탁드립니다! 🤞
위의 현상을 재현하기 위한 프로젝트와 테스트 코드를 작성하였습니다.
- https://github.com/infoqoch/openstudy/tree/master/spring-exception-handler-scope-test