Part3 - CH6. 스프링 기능 활용하기(2) Exception
Web Application에서 에러를 알리는 방법은 다음과 같다.
- 에러 페이지
- 4XX or 5XX error
- 클라이언트가 200외에 처리를 하지 못하는 경우, 200과 함께 error메세지 전달
전체 어플리케이션에 대해서 한번에 Exception을 처리하는 스프링의 방식은 다음과 같다.
@ControllerAdvice : Global 예외 처리 및 특정 package/Controller 예외처리 @ExceptionHandler : 특정 Controller의 예외 처리
실습환경 구성
일단, RestController와 값을 받을 DTO를 생성해준다.
public class User {
@NotEmpty
@Size(min = 1, max = 10)
private String name;
@Min(1)
@NotNull
private Integer age;
//getter,setter,toString 생략
}
@GetMapping("")
public User get(@RequestParam(required = false) String name, @RequestParam(required = false) Integer age) {
User user = new User();
user.setName(name);
user.setAge(age);
int a = 10 + age;
return user;
}
@PostMapping("")
public User post(@RequestBody @Valid User user) {
System.out.println(user);
return user;
}
@RequestParam(required = false) : 해당옵션을 주면 꼭 해당 파라미터가 들어오지 않아도 동작한다.
required = false 옵션을 통해 name이나 age에 null값이 들어오도록 유도한 후, exception에 대한 처리를 해 보자.
여기서 get메소드로 요청을 보내면 setAge에서 NPE가 발생한다.
그런데, 해당 요청에 대해 클라이언트는 어떤곳에서 어떤 에러가 발생했는지 구체적인 메세지를 받지 못한다.
ControllerAdvice
그러면 스프링부트에서 발생하는 예외를 처리하는 핸들러를 만들어보자.
@RestControllerAdvice
//@ControllerAdvice //view-resolver를 위한 advice
public class GlobalControllerAdvice {
@ExceptionHandler(value = Exception.class) //모든 예외를 잡는다.
public ResponseEntity exception(Exception e){
System.out.println(e.getClass().getName());
System.out.println(e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getClass().getSimpleName());
}
}
위와 같은 클래스를 만들어서 스프링부트에서 발생하는 모든예외에 대해서 클라이언트에게 ResponseEntity를 통한 에러메세지를 리턴해줄 수 있다.
그런데 내가 원하는 Exceptiopn에 대해서만 처리하고 싶을 수 있다. 그럴때는 다음과 같이 한다.
@ExceptionHandler(value = MethodArgumentNotValidException.class) //모든 예외를 잡는다.
public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
ExceptionHandler 어노테이션 value옵션에 특정 예외를 지정해주면 된다.
또한, 특정 패키지에서 발생하는 예외만 핸들링 하도록 아래와 같이 설정해줄수도 있다.
@RestControllerAdvice(basePackages = "")
또, 위의 핸들러를 특정 컨트롤러 패키지 안에 넣어주면 해당 컨트롤러에서만 동작한다. 그리고 전역으로 지정한 예외 핸들러와 컨트롤러 내에 동일한 예외를 처리하는 핸들러 둘다 있다면, 컨트롤러 내의 예외 핸들러에서 처리 된다.