스프링 부트의 예외처리

goose_bumps·2025년 5월 30일

SpringBoot

목록 보기
7/9

자바를 공부했다면 예외처리에 대해서는 기본적으로 알고 있을 것이다. 보통 예외가 발생을 하면 예외를 try-catch 블럭을 사용하여 처리 동작을 수행하거나 throw를 통해 전가를 하는 방식을 택할 것이다.

https://velog.io/@nosibi/%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC

웹 애플리케이션에서는 외부(클라이언트)에서 들어오는 요청에 담긴 데이터를 처리하는 경우가 많은데, 예외를 정상 처리하기보다는 대부분 클라이언트에게 어떤 문제가 발생했는지 전달한다.

이러한 처리 방식을 스프링 부트에서 어떻게 적용할 수 있는지 알아보자.

우선, 스프링 부트에서 예외를 처리하는 방법은 크게 2가지이다.

  • 글로벌 예외 처리 : @(Rest)ControllerAdvice@ExceptionHanlder를 사용한 모든 컨트롤러 예외 처리
  • 컨트롤러 예외 처리 : @ExceptionHanlder를 사용한 특정 컨트롤러 예외만 처리

정의만 봐서는 무슨 뜻인지 전혀 이해가 가지 않을 것이다.

예를 들어, A,B,C라는 3개의 컨트롤러가 있고 글로벌 예외 처리 클래스 내에 RuntimeException를 처리하는 메서드를 만들었다고 가정해보자.
저 3개의 컨트롤러 중 어떤 컨트롤러든 RuntimeException이 발생했다면 해당 메서드가 호출되는 것이다. 이것이 바로 글로벌 예외 처리이다.

이와 다르게 컨트롤러 예외 처리는 컨트롤러 내부에 예외를 처리하는 메서드를 만든다. 위의 예시에서 A 컨트롤러 내부에 RuntimeException를 처리하는 메서드가 있다면 이 메서드는 A 컨트롤러에서 RuntimeException이 발생했을 경우에만 호출된다.

설명만으로는 잘 이해가 가지 않을 수 있으니 뒤에서 차근차근 예시 코드를 통해 알아가보자.

1. 글로벌 예외 처리

그림과 같이 처리하려는 예외 종류에 해당하는 예외가 발생한 컨트롤러에 대해 전역으로 처리하는 방식이다.

매우 간단한 예제를 통해 이해해보자.

우선 RuntimeException을 발생시키는 컨트롤러를 만들어보겠다.

@RestController
@RequestMapping("/exception")
public class ExceptionController {

    @GetMapping
    public void getRuntimeException(){
        throw new RuntimeException();
    }
}

이해를 위해 만든 컨트롤러일 뿐 그 외에 기능은 없다.

이제 글로벌 예외 처리를 할 수 있는 ExceptionHandler라는 클래스를 만들어보자.

@RestControllerAdvice
public class ExceptionHandler {

    @org.springframework.web.bind.annotation.ExceptionHandler(value = RuntimeException.class)
    public ResponseEntity<String> handlerException(RuntimeException e){
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
    }
}

이 클래스에 대해 설명하자면 먼저 @RestControllerAdvice라는 에너테이션이 클래스에 추가되어 있는데, @RestController에서 발생하는 예외를 한 곳에서 관리하고 처리할 수 있게 하는 기능을 수행한다.

즉, 컨트롤러에서 발생한 예외는 이 클래스에서 처리한다는 의미이다.
이제 클래스 안에 메서드를 보면 @ExceptionHandler라는 에너테이션이 추가되어 있는데 여기서 예외를 정한다.
value값으로 RuntimeException.class를 지정했으니 RuntimeException이 발생하면 이 메서드에서 처리한다는 것이다.

애플리케이션을 실행한 후 Swagger로 확인해보면

정상적으로 예외 처리하여 클라이언트에게 응답 메시지를 보낸 것을 확인할 수 있다.

2. 컨트롤러 예외 처리

이번에는 예외를 처리하는 클래스를 따로 만들지 않고 컨트롤러 내부에서 처리하도록 로직을 바꿔보겠다.

@RestController
@RequestMapping("/exception")
public class ExceptionController {

    @GetMapping
    public void getRuntimeException(){
        throw new RuntimeException();
    }

    @org.springframework.web.bind.annotation.ExceptionHandler(value = RuntimeException.class)
    public ResponseEntity<String> handlerException(RuntimeException e){
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
    }
}

단순히 컨트롤러 클래스 내부에 @ExceptionHandler 메서드를 추가하면 된다.

단, 이렇게 설계를 할 경우 다른 컨트롤러에서 발생한 RuntimeException은 처리를 못하고 오직 해당 컨트롤러인 ExceptionController에서 발생한 RuntimeException만 처리하게 된다.

예외 처리하는 이 두 방식은 목적에 따라 선택해서 사용하면 되는데, 모든 컨트롤러에서 공통된 예외를 처리할 경우 글로벌 예외 처리를, 컨트롤러별로 다르게 처리해야 하는 도메인 예외인 경우 컨트롤러 예외 처리를 사용하면 된다.

0개의 댓글