스프링은 /error 라는 경로로 기본 오류 페이지를 설정한다
따로 상태코드와 예외를 설정하지 않으면 기본 오류 페이지로 사용된다
모든 오류는 /error를 호출하게 된다.
그냥 static/error에 4xx.html과 5xx.html 파일을 만들어 놓자
간단명료
어떤 메서드를 5XX오류가 아니라 4XX오류로 처리해주고 싶을때
@ResponseStatus 을 사용하면 가능하다
@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "잘못된 요청 오류")
public class BadRequestException extends RuntimeException {
}
하지만 동적으로 오류처리해주고 싶을때는 어노테이션으론 불가능하다
이때는 ResponseStatusException 예외를 사용하면 된다
@GetMapping("/api/response-status-ex2")
public String responseStatusEx2() {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "error.bad", new
IllegalArgumentException());
}
error.bad를 통해 원하는 메시지를 보낼 수 있다
클라이언트가 값을 잘못보냈을때
typeMismatchException이 발생하는데
원래는 WAS까지 예외가 전달되서 500 오류가 발생해야 하지만,
위 예외는 클라이언트가 잘못했기 때문에
자동으로 400 Bad Request 예외를 던진다.
기본 오류 페이지는 단순히 오류 화면을 보여주면 끝이지만,
API는 각 오류 상황에 맞는 오류 응답 스펙을 정하고, JSON으로 데이터를 내려주어야 한다.
클라이언트는 정상 요청이든 오류 요청이든 JSON으로 변환되기를 기대한다.
이때 사용하는 어노테이션이 @ExceptionHandler이다.
해당 어노테이션을 사용하면 해당 컨트롤러에서 발생한 예외를 받아서 처리해줄 수 있다.
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandle(IllegalArgumentException e) {
log.error("[exceptionHandle] ex", e);
return new ErrorResult("BAD", e.getMessage());
}
위는 해당 컨트롤러에서 IllegalArgumentException이 터졌을때 호출되는 메서드이다.
@ResponseStatus 어노테이션을 이용해 400으로 보내줬다.
메서드의 파라미터를 IllegalArgumentException e로 설정했기 때문에
@ExceptionHandler의 인자는 생략해도 된다.
위 메서드는 IllegalArgumentException의 하위 자식 클래스를 모두 처리할 수 있다
실행결과
{
"code": "BAD",
"message": "잘못된 입력 값"
}
이때 @ExceptionHandler를 컨트롤러 안에 선언해놓으면
정상코드와 예외처리코드가 하나의 컨트롤러에 섞여있게 된다.
이때 @ControllerAdvice를 이용해서 예외 처리 코드를 따로 선언해놓을 수 있다.
@ControllerAdvice
public class ExControllerAdvice {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandle(IllegalArgumentException e) {
log.error("[exceptionHandle] ex", e);
return new ErrorResult("BAD", e.getMessage());
}
...
}
@ControllerAdvice는 대상으로 지정한 여러 컨트롤러에 @ExceptionHandler, @InitBinder 기능을 부여해주는 역할을 한다.
위처럼 대상을 지정하지 않으면 모든 컨트롤러에 적용된다.
대상은 RestController, 특정 패키지, 특정 컨트롤러 클래스에 지정 가능하다.
대상 컨트롤러 지정 방법
// RestController를 지정 @ControllerAdvice(annotations = RestController.class) public class ExampleAdvice1 {} // 특정 패키지에 지정 @ControllerAdvice("org.example.controllers") public class ExampleAdvice2 {} // 특정 컨트롤러 클래스 지정 @ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class}) public class ExampleAdvice3 {}
특정 패키지를 지정하면, 해당 패키지와 그 하위에 있는 컨트롤러가 대상이 된다
@ExceptionHandler 와 @ControllerAdvice 를 조합하면 예외를 깔끔하게 해결할 수 있다.
거기에 @ResponseStatus 까지 사용하면 원하는 상태코드 지정가능
위 3개 어노테이션을 자주 사용하자