Web Application에서 에러를 알리는 방법은 다음과 같다.

  1. 에러 페이지
  2. 4XX or 5XX error
  3. 클라이언트가 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 = "")

또, 위의 핸들러를 특정 컨트롤러 패키지 안에 넣어주면 해당 컨트롤러에서만 동작한다. 그리고 전역으로 지정한 예외 핸들러와 컨트롤러 내에 동일한 예외를 처리하는 핸들러 둘다 있다면, 컨트롤러 내의 예외 핸들러에서 처리 된다.