목차
1. 서론
2. @ExceptionHandler
3. 상태 코드 바꾸기
4. Exception 한꺼번에 처리하기
5. 모든 Controller에 적용시키기
🙋♀️ 서론
API를 호출하다 보면 예외가 많이 발생된다.
controller에서 처리하려고 로직을 짜다보면 수많은 중복 코드가 발생한다.
Spring에서는 이를 편리하게 처리하기 위해 @ExceptionHandler라는 어노테이션을 지원해준다.
이번 글에서는 에러 처리 방법에 대해 공부해보겠다.
🚀 @ExceptionHandler
Controller에서 @ExceptionHandler가 붙은 메서드를 이용하면 특정 예외를 처리할 수 있다.
Controller 내부에서 어떠한 API가 호출되다가 IllegalArgumentException이 발생한다면 아래의 예제 코드가 실행된다.
@RestController
@RequestMapping("/test")
public class TestController {
// ...
@ExceptionHandler(IllegalArgumentException.class)
public String handleException() {
return "error!";
}
}
참고로 Exception을 파라미터로 받으면 @ExceptionHandler 어노테이션에서 옵션으로 지정 안해줘도 된다.
@ExceptionHandler
public String handleException(IllegalArgumentException e) {
return e.getMessage();
}
오류 페이지가 따로 존재하는 경우 ModelAndView를 이용할 수도 있다.
@ExceptionHandler
public ModelAndView handler(Exception e) {
return new ModelAndView("error");
}
😲 상태 코드 바꾸기
위 예제들에서의 문제는 HTTP 상태 코드가 200이다.
200은 보통 OK, 정상적으로 동작했다는 의미로 사용한다.
(다양한 HTTP 상태 코드는 mozilla에 잘 설명되어 있다.)
이를 더 적절한 상태 코드를 전환하는 방법에 대해 서술해보겠다.
1. ResponseEntity 이용하기
@ExceptionHandler
public ResponseEntity<String> handleException(IllegalArgumentException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
// return ResponseEntity.badRequest().body(e.getMessage()); // 위와 같은 코드
}
2. @ResponseStatus 이용하기
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<String> handleException(IllegalArgumentException e) {
return e.getMessage();
}
📑 Exception 한꺼번에 처리하기
Exception 종류를 하나하나 지정하면서 처리하기는 번거롭다.
다른 오류가 발생했더라도 반환하는 값이 같다던가 등 중복 로직이 발생할 수 있다.
그럴 때는 부모-자식 관계를 적절하게 이용하자.
아래 예제 코드를 살펴보자.
handleException1()이 handleException2()보다 구체적인 범위를 지정했다.
(IllegalArgumentException은 RuntimeException을 상속 받은 클래스이기 때문에)
따라서 IllegalArgumentException이 발생하게 된다면 handleException1()이 실행될 것이다.
Spring은 기본적으로 더 디테일한 것이 우선권을 가진다.
@RestController
@RequestMapping("/test")
public class TestController {
// ...
@ExceptionHandler(IllegalArgumentException.class)
public String handleException1() {
return "IllegalArgumentException!";
}
@ExceptionHandler(RuntimeException.class)
public String handleException2() {
return "Exception!";
}
}
💥 모든 Controller에 적용시키기
위의 예제들은 TestController 내부에 ExceptionHandler를 적용해두었다.
이 경우 TestController에서 발생한 예외에 대해서만 처리가 되지 다른 컨트롤러까지 적용되지는 않는다.
만약 TestController 뿐만 아니라 다른 컨트롤러들에서도 적용시키고 싶다면?
@ControllerAdvice를 이용해보자. (또는 @RestControllerAdvice)
@RestControllerAdvice
public class ExceptionControllerAdvice {
@ExceptionHandler(IllegalArgumentException.class)
public String handleException() {
return "IllegalArgumentException!";
}
}
다양한 옵션으로 특정 위치의 예외만 처리할 수도 있다.
- packages
- assignableTypes
- annotations
패키지 지정
@RestControllerAdvice("org.example.controllers")
public class ExceptionControllerAdvice {
@ExceptionHandler
public String handleException() {
return "package!";
}
}
클래스 지정
@RestControllerAdvice(assignableTypes = {TestController.class, HomeController.class})
public class ExceptionControllerAdvice {
@ExceptionHandler
public String handleException() {
return "class!";
}
}
어노테이션 지정
@RestControllerAdvice(annotations = RestController.class)
public class ExceptionControllerAdvice {
@ExceptionHandler
public String handleException() {
return "annotation!";
}
}
참고
'Develop > Spring+JPA' 카테고리의 다른 글
[Spring] @RequestBody가 빈 생성자가 필요한 이유 (hint. ObjectMapper) (1) | 2022.05.01 |
---|---|
[Spring] 생성자 주입 vs 필드 주입 vs 수정자 주입 (0) | 2022.04.28 |
[Spring Security] 초간단 로그인 만들기 (2) | 2022.03.07 |
[Spring Security] 스프링 시큐리티 간단 적용기 (1) | 2022.02.07 |
[Spring] NestedServletException - Name for argument type [java.lang.Long] not available 에러 (0) | 2022.01.28 |