본문 바로가기
Develop/Spring+JPA

[Spring] @ExceptionHandler로 API 예외 한번에 처리하기

by 연로그 2022. 4. 21.
반응형

목차

1. 서론

2. @ExceptionHandler

3. 상태 코드 바꾸기

4. Exception 한꺼번에 처리하기

5. 모든 Controller에 적용시키기


 


🙋‍♀️ 서론

 

API를 호출하다 보면 예외가 많이 발생된다.

controller에서 처리하려고 로직을 짜다보면 수많은 중복 코드가 발생한다.

Spring에서는 이를 편리하게 처리하기 위해 @ExceptionHandler라는 어노테이션을 지원해준다.

이번 글에서는 에러 처리 방법에 대해 공부해보겠다.

 

 


🚀 @ExceptionHandler

 

Controller에서 @ExceptionHandler가 붙은 메서드를 이용하면 특정 예외를 처리할 수 있다.

Controller 내부에서 어떠한 API가 호출되다가 IllegalArgumentException이 발생한다면 아래의 예제 코드가 실행된다.

@RestController
@RequestMapping("/test")
public class TestController {
    // ...
    
    @ExceptionHandler(IllegalArgumentException.class)
    public String handleException() {
        return "error!";
    }
}

 

참고로 Exception을 파라미터로 받으면 @ExceptionHandler 어노테이션에서 옵션으로 지정 안해줘도 된다.

@ExceptionHandler
public String handleException(IllegalArgumentException e) {
    return e.getMessage();
}

 

오류 페이지가 따로 존재하는 경우 ModelAndView를 이용할 수도 있다.

@ExceptionHandler
public ModelAndView handler(Exception e) {
    return new ModelAndView("error");
}

 

 


😲 상태 코드 바꾸기

 

https://http.cat/

 

위 예제들에서의 문제는 HTTP 상태 코드가 200이다.

200은 보통 OK, 정상적으로 동작했다는 의미로 사용한다.

(다양한 HTTP 상태 코드는 mozilla에 잘 설명되어 있다.)

 

이를 더 적절한 상태 코드를 전환하는 방법에 대해 서술해보겠다.

 

1. ResponseEntity 이용하기

@ExceptionHandler
public ResponseEntity<String> handleException(IllegalArgumentException e) {
    return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
    // return ResponseEntity.badRequest().body(e.getMessage()); // 위와 같은 코드
}

 

2. @ResponseStatus 이용하기

@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<String> handleException(IllegalArgumentException e) {
    return e.getMessage();
}

 


📑 Exception 한꺼번에 처리하기

 

Exception 종류를 하나하나 지정하면서 처리하기는 번거롭다.

다른 오류가 발생했더라도 반환하는 값이 같다던가 등 중복 로직이 발생할 수 있다.

그럴 때는 부모-자식 관계를 적절하게 이용하자.

 

아래 예제 코드를 살펴보자.

handleException1()이 handleException2()보다 구체적인 범위를 지정했다.

(IllegalArgumentException은 RuntimeException을 상속 받은 클래스이기 때문에)

따라서 IllegalArgumentException이 발생하게 된다면 handleException1()이 실행될 것이다.

 

Spring은 기본적으로 더 디테일한 것이 우선권을 가진다.

 

@RestController
@RequestMapping("/test")
public class TestController {
    // ...
    
    @ExceptionHandler(IllegalArgumentException.class)
    public String handleException1() {
        return "IllegalArgumentException!";
    }
    
    @ExceptionHandler(RuntimeException.class)
    public String handleException2() {
        return "Exception!";
    }
}

 

 


💥 모든 Controller에 적용시키기

 

위의 예제들은 TestController 내부에 ExceptionHandler를 적용해두었다.

이 경우 TestController에서 발생한 예외에 대해서만 처리가 되지 다른 컨트롤러까지 적용되지는 않는다.

 

만약 TestController 뿐만 아니라 다른 컨트롤러들에서도 적용시키고 싶다면?

@ControllerAdvice를 이용해보자. (또는 @RestControllerAdvice)

 

@RestControllerAdvice
public class ExceptionControllerAdvice {

    @ExceptionHandler(IllegalArgumentException.class)
    public String handleException() {
        return "IllegalArgumentException!";
    }
}

 

다양한 옵션으로 특정 위치의 예외만 처리할 수도 있다.

  • packages
  • assignableTypes
  • annotations

 

패키지 지정

@RestControllerAdvice("org.example.controllers")
public class ExceptionControllerAdvice {

    @ExceptionHandler
    public String handleException() {
        return "package!";
    }
}

 

클래스 지정

@RestControllerAdvice(assignableTypes = {TestController.class, HomeController.class})
public class ExceptionControllerAdvice {

    @ExceptionHandler
    public String handleException() {
        return "class!";
    }
}

 

어노테이션 지정

@RestControllerAdvice(annotations = RestController.class)
public class ExceptionControllerAdvice {

    @ExceptionHandler
    public String handleException() {
        return "annotation!";
    }
}

 


참고

반응형