예외처리 - Spring 에서 예외 처리 종류

박영준·2023년 7월 6일
0

Spring

목록 보기
36/58

1. 정의

1) HandlerExceptionResolver

  • HandlerExceptionResolver 인터페이스는 발생한 Exception을 catch하고 HTTP 상태나 응답 메세지 등을 설정한다.

    • WAS 입장에서도 해당 요청이 정상적인 응답인 것으로 인식되어, WAS의 에러 전달이 진행되지 않는다.
  • HandlerExceptionResolver 는 적합한 예외 처리를 위해 HandlerExceptionResolver 구현체들을 빈으로 등록해서 관리한다.

    • DefaultErrorAttributes, ExceptionHandlerExceptionResolver, ResponseStatusExceptionResolver, DefaultHandlerExceptionResolver

      • DefaultErrorAttributes : 직접 예외를 처리하지 않고 속성만 관리하므로 나머지 3가지와 성격이 다르다.

      • 나머지 3가지 : 직접 예외를 처리하는 ExceptionResolver (예외 처리기)

        • HandlerExceptionResolverComposite로 모아서 관리한다

2. 예외 처리 방법

Spring 은 다음 도구들을 사용해서, ExceptionResolver를 동작시켜서 에러를 처리한다.

1) @ResponseStatus

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class NoSuchElementFoundException extends RuntimeException {
  ...
}

/* 응답 결과
{
    "timestamp": "2021-12-31T03:35:44.675+00:00",
    "status": 404,
    "error": "Not Found",
    "path": "/product/5000"
}
*/
  • HTTP Status 와 Response 를 제공

  • ResponseStatusExceptionResolver 가 지정해준 상태로 에러 응답이 내려가도록 처리

(1) 사용 조건

  1. Exception 클래스 자체

  2. 메소드에 @ExceptionHandler 와 함께

  3. 클래스에 @RestControllerAdvic e 와 함께

(2) 단점

  1. 에러 응답의 내용(Message) 수정 X
    (단, DefaultErrorAttributes를 수정하면 가능하긴 함)

  2. 예외 클래스와 강하게 결합돼서, 같은 예외는 같은 상태와 에러 메세지를 반환함

  3. 별도의 응답 상태가 필요하다면, 예외 클래스를 추가해야 됨

  4. WAS까지 예외가 전달되고, WAS의 에러 요청 전달이 진행됨

  5. 외부에서 정의한 Exception 클래스에는 @ResponseStatus 를 붙여줄 수 없음

2) ResponseStatusException

@GetMapping("/product/{id}")
public ResponseEntity<Product> getProduct(@PathVariable String id) {
    try {
        return ResponseEntity.ok(productService.getProduct(id));
    } catch (NoSuchElementFoundException e) {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Item Not Found");
    }
}
  • 코드를 외부 라이브러리에서 정의한 경우, 개발자는 해당 코드를 수정할 수 없으므로 @ResponseStatus 를 붙여줄 수 없다.

    • 이를 해결하기 위해 등장한 것이 ResponseStatusException
    • 손쉽게 에러를 반환 가능
  • 생성자로 HTTP Status 와 String 만을 받는다.

  • HttpStatus와 함께 선택적으로 reason과 cause를 추가 가능

  • 언체크 예외을 상속받고 있어, 명시적으로 에러를 처리해주지 않아도 됨

  • ResponseStatusExceptionResolver 클래스가 ResponseStatusException 또는 @ResponseStatus 이 붙은 예외를 찾아서 처리

(1) 장단점

장점

  1. 기본적인 예외 처리를 빠르게 적용할 수 있으므로, 손쉽게 프로토타이핑 가능

  2. HttpStatus를 직접 설정하여, 예외 클래스와의 결합도 ↓

  3. 불필요하게 많은 별도의 예외 클래스를 만들지 않아도 됨

  4. 프로그래밍 방식으로 예외를 직접 생성하므로, 예외를 더욱 잘 제어 O
    (비슷한 유형의 예외를 별도로 처리 가능. 응답마다 다른 상태 코드를 세팅 가능)

단점

  1. 직접 예외 처리를 프로그래밍하므로, 일관된 예외 처리 X

  2. 예외 처리 코드의 중복

  3. Spring 내부의 예외를 처리 어려움

  4. 예외가 WAS까지 전달되고, WAS의 에러 요청 전달이 진행됨

이런 한계점으로 인해,
API 에러 처리를 위해서는 @ExceptionHandler를 사용하는 방식이 多 사용됨

3) @ExceptionHandler

@RestController
@RequiredArgsConstructor
public class ProductController {

  private final ProductService productService;
  
  @GetMapping("/product/{id}")
  public Response getProduct(@PathVariable String id){
    return productService.getProduct(id);
  }

  // 에러 처리	
  @ExceptionHandler(NoSuchElementFoundException.class)
  public ResponseEntity<String> handleNoSuchElementFoundException(NoSuchElementFoundException exception) {
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(exception.getMessage());
  }
}
  • 매우 유연하게 에러를 처리할 수 있는 방법

  • @ExceptionHandler에 의해 발생한 예외는 ExceptionHandlerExceptionResolver에 의해 처리됨

  • Exception 클래스들을 속성으로 받아 처리할 예외를 지정 가능
    ExceptionHandler 어노테이션에 예외 클래스를 지정하지 않는다면, 파라미터에 설정된 에러 클래스를 처리

  • @ResponseStatus와도 결합 가능

    • 만약 ResponseEntity 에서도 status를 지정하고 @ResponseStatus도 있다면, ResponseEntity 가 우선순위를 갖는다

주의!
'@ExceptionHandler 에 등록된 예외 클래스'와 '파라미터로 받는 예외 클래스'가 동일해야 한다는 것이다.
만약 값이 다르다면, Spring 은 컴파일 시점에 에러를 내지 않다가 런타임 시점에 에러를 발생시킨다.

(1) 사용 조건

  1. 컨트롤러의 메소드

  2. @ControllerAdvice 나 @RestControllerAdvice 가 있는 클래스의 메소드

(2) 장단점

장점

  1. @ResponseStatus와 달리, 에러 응답(payload)을 자유롭게 다룰 수 있다는 점에서 유연하다.

  2. ExceptionHandler 의 파라미터로 HttpServletRequest나 WebRequest 등...을 얻을 수 있으며
    반환 타입으로는 ResponseEntity, String, void 등... 자유롭게 활용 가능

단점

  1. @ExceptionHandler 에 등록된 예외 클래스와 파라미터로 받는 예와 클래스가 동일해야함
    만약 값이 다르다면, Spring 은 컴파일 시점에 에러를 내지 않다가 런타임 시점에 에러를 발생시킨다.

  2. @ExceptionHandler는 컨트롤러에 구현하므로, 특정 컨트롤러에서만 발생하는 예외만 처리된다.
    → 컨트롤러에 에러 처리 코드가 섞이면서, 에러 처리 코드가 중복될 가능성↑
    → 그래서 Spring 은 전역적으로 예외를 처리할 수 있는 좋은 기술을 제공해줌

4) @ControllerAdvice 와 @RestControllerAdvice

구현 방법에 대해서는 아래 링크를 참고하자.
참고: @RestControllerAdvice 예외 처리 구현하기

  • 이 어노테이션을 사용하면, 전역적으로 @ExceptionHandler 를 적용할 수 있다

    • 여러 컨트롤러에 대해 전역적으로 ExceptionHandler 를 적용해준다.
  • 두 어노테이션의 차이는 @Controller 와 RestController 의 차이와 같다.

    • @ResponseBody가 붙어 있어 응답을 Json으로 내려준다는 것

(1) 장단점

장점

  1. 하나의 클래스로, 모든 컨트롤러에 대해 전역적으로 예외 처리 가능

  2. 직접 정의한 에러 응답을 일관성있게 클라이언트에게 내려줄 수 있음

  3. 별도의 try-catch문이 없어, 코드의 가독성↑

단점

  1. 여러 ControllerAdvice가 있을 때 @Order 어노테이션으로 순서를 지정하지 않는다면, Spring은 ControllerAdvice를 임의의 순서로 처리할 수 있음
    → 일관된 예외 응답을 위해서는 이러한 점에 주의

  2. 한 프로젝트당 하나의 ControllerAdvice만 관리하는 것이 좋다.

  3. 여러 ControllerAdvice 가 필요하다면, basePackages나 annotations 등을 지정해야 함

  4. 직접 구현한 Exception 클래스들은 한 공간에서 관리

profile
개발자로 거듭나기!

0개의 댓글