백엔드 기초 재정비/스프링 계층 구조 이해

GlobalExceptionHandler

namerong 2026. 3. 6. 00:37

1. GlobalExceptionHandler 흐름 정리

1. 예외 처리 방식의 문제점

Spring에서 예외를 별도로 처리하지 않으면 기본적으로 500 Internal Server Error가 발생한다.

예를 들어 서비스 로직에서 예외가 발생하면 다음과 같은 문제가 생길 수 있다.

  • Controller마다 try-catch 코드가 필요해진다.
  • API마다 에러 응답 형식이 달라질 수 있다.
  • 클라이언트가 에러를 해석하기 어려워진다.

예시

@GetMapping("/orders/{id}")
public Order getOrder(@PathVariable Long id) {
    try {
        return orderService.findById(id);
    } catch (Exception e) {
        throw new RuntimeException("주문 조회 실패");
    }
}

이 방식의 문제점은 다음과 같다.

  • Controller 코드가 불필요하게 복잡해진다.
  • 동일한 예외 처리 코드가 여러 Controller에서 반복된다.
  • API 에러 응답 구조가 일관되지 않다.

이러한 문제를 해결하기 위해 GlobalExceptionHandler를 사용한다.


2. GlobalExceptionHandler 개념

GlobalExceptionHandler는 애플리케이션 전체에서 발생하는 예외를 한 곳에서 공통적으로 처리하는 클래스이다.

Spring에서는 다음 어노테이션을 사용한다.

@RestControllerAdvice

이 어노테이션이 붙은 클래스는 다음과 같은 역할을 한다.

  • 모든 Controller에서 발생하는 예외를 전역적으로 감지한다.
  • 예외 처리 로직을 한 곳에서 관리할 수 있도록 한다.

3. 예외 발생 시 전체 흐름

Spring에서 요청 처리 중 예외가 발생했을 때의 흐름은 다음과 같다.

Client Request
      ↓
DispatcherServlet
      ↓
Controller
      ↓
Service
      ↓
Exception 발생
      ↓
GlobalExceptionHandler
      ↓
Error Response 생성
      ↓
Client Response

동작 과정은 다음과 같다.

  1. 클라이언트가 API 요청을 보낸다.
  2. DispatcherServlet이 요청을 받아 Controller를 호출한다.
  3. Controller에서 Service 로직을 실행한다.
  4. Service 계층에서 예외가 발생한다.
  5. Spring이 GlobalExceptionHandler를 탐색한다.
  6. 해당 예외를 처리하는 @ExceptionHandler 메서드가 실행된다.
  7. 에러 응답 객체를 생성한다.
  8. JSON 형태의 에러 응답을 클라이언트에게 반환한다.

4. GlobalExceptionHandler 기본 구조

예시

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<String> handleRuntimeException(RuntimeException e) {
        return ResponseEntity
                .status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(e.getMessage());
    }
}

 

구성 요소 설명

  • @RestControllerAdvice
    모든 Controller에서 발생하는 예외를 전역적으로 처리한다.
  • @ExceptionHandler
    특정 Exception을 처리하는 메서드를 지정한다.

예외가 발생하면 해당 메서드가 실행되어 응답을 생성한다.


5. 에러 응답 구조 직접 정의

실무에서는 단순히 문자열 메시지만 반환하지 않는다.
보통 에러 응답 객체(ErrorResponse)를 따로 정의하여 사용한다.

예시

public class ErrorResponse {

    private int status;
    private String message;
    private String code;
    private LocalDateTime timestamp;

}

 

필드 설명

필드 의미
status HTTP 상태 코드
message 에러 메시지
code 에러 코드
timestamp 에러 발생 시간

6. GlobalExceptionHandler에서 ErrorResponse 사용

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<ErrorResponse> handleRuntimeException(RuntimeException e) {

        ErrorResponse response = new ErrorResponse(
                500,
                e.getMessage(),
                "INTERNAL_SERVER_ERROR",
                LocalDateTime.now()
        );

        return ResponseEntity
                .status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(response);
    }
}

 

응답 예시

{
  "status": 500,
  "message": "재고 부족",
  "code": "INTERNAL_SERVER_ERROR",
  "timestamp": "2026-03-07T15:00:00"
}

이처럼 에러 응답 구조를 정의하면 모든 API의 에러 응답 형식을 동일하게 유지할 수 있다.


7. 실무에서 자주 사용하는 예외 처리

예외 종류에 따라 다른 HTTP 상태 코드를 반환할 수도 있다.

예시

IllegalArgumentException → 400 Bad Request
EntityNotFoundException → 404 Not Found
RuntimeException → 500 Internal Server Error

 

예시 코드

@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException e) {

    ErrorResponse response = new ErrorResponse(
            400,
            e.getMessage(),
            "BAD_REQUEST",
            LocalDateTime.now()
    );

    return ResponseEntity
            .status(HttpStatus.BAD_REQUEST)
            .body(response);
}

8. 전체 구조 정리

Spring 예외 처리 흐름은 다음과 같다.

Controller
   ↓
Service
   ↓
Exception 발생
   ↓
GlobalExceptionHandler
   ↓
ErrorResponse 생성
   ↓
ResponseEntity 반환
   ↓
Client

 

핵심 목적

  1. 예외 처리 로직 중앙화
  2. API 에러 응답 구조 통일
  3. Controller 코드 단순화

9. 최종 정리

GlobalExceptionHandler는 Spring 애플리케이션에서 발생하는 예외를 한 곳에서 공통적으로 처리하기 위한 기능이다. @RestControllerAdvice를 사용하면 모든 Controller에서 발생하는 예외를 전역적으로 감지할 수 있으며, @ExceptionHandler를 통해 특정 예외에 대한 처리 로직을 정의할 수 있다.

 

요청 처리 과정에서 Controller나 Service 계층에서 예외가 발생하면 Spring은 GlobalExceptionHandler를 통해 해당 예외를 처리하고, 정해진 형식의 에러 응답을 생성하여 클라이언트에게 반환한다.

 

또한 실무에서는 ErrorResponse와 같은 에러 응답 객체를 직접 정의하여 상태 코드, 메시지, 에러 코드, 발생 시간 등을 포함한 일관된 응답 구조를 사용한다. 이를 통해 예외 처리 로직을 중앙화하고 API의 에러 응답 형식을 통일할 수 있으며, 코드의 가독성과 유지보수성을 높일 수 있다.