예외 처리

HUSII·2023년 5월 9일
0

스프링 MVC

목록 보기
5/5

오류 페이지

스프링은 /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 예외 처리

기본 오류 페이지는 단순히 오류 화면을 보여주면 끝이지만,
API는 각 오류 상황에 맞는 오류 응답 스펙을 정하고, JSON으로 데이터를 내려주어야 한다.

클라이언트는 정상 요청이든 오류 요청이든 JSON으로 변환되기를 기대한다.

이때 사용하는 어노테이션이 @ExceptionHandler이다.

@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개 어노테이션을 자주 사용하자

profile
공부하다가 생긴 궁금한 것들을 정리하는 공간

0개의 댓글