노트

스프링 전역 예외 처리

Global Exception Handling in Spring (@ControllerAdvice)

백엔드#spring · 연결된 개념 6개

쉽게 말하면

스프링 전역 예외 처리는 부서마다 제각각 사과문을 쓰는 대신, 고객 응대 창구 한 곳에서 같은 양식으로 답하게 하는 거예요. 어느 컨트롤러에서 문제가 나도 오류 응답 모양이 똑같아지죠.

비유가 깨지는 곳 모든 문제가 그 창구로 오진 않아요. 시큐리티 필터에서 난 401·403은 컨트롤러에 닿기 전이라 ControllerAdvice가 아니라 AuthenticationEntryPoint·AccessDeniedHandler가 맡아요.

@ExceptionHandler는 컨트롤러에서 던진 예외를 받아 응답으로 바꾸는 메서드이고, @ControllerAdvice는 그 처리기를 모든 컨트롤러에 공통으로 적용하는 클래스다. 예외를 응답 형식으로 바꾸는 코드를 한 곳에 모은다.

적용 범위

  • 컨트롤러 클래스 안의 @ExceptionHandler: 그 컨트롤러에서 난 예외만 처리한다
  • @ControllerAdvice 클래스 안의 @ExceptionHandler: 모든 컨트롤러에 적용된다. basePackages·assignableTypes로 범위를 좁힐 수 있다
  • @RestControllerAdvice = @ControllerAdvice + @ResponseBody. 반환값이 JSON 본문이 된다
@RestControllerAdvice
public class GlobalExceptionHandler {
 
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ProblemDetail> invalid(MethodArgumentNotValidException e) {
        ProblemDetail pd = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST);
        pd.setProperty("errors", e.getFieldErrors().stream()
            .map(f -> Map.of("field", f.getField(), "message", f.getDefaultMessage())).toList());
        return ResponseEntity.badRequest().body(pd);
    }
 
    @ExceptionHandler({OrderNotFoundException.class, NoSuchElementException.class})
    public ResponseEntity<ProblemDetail> notFound(RuntimeException e) {
        return ResponseEntity.status(404).body(ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, e.getMessage()));
    }
}
  • 하나의 메서드가 여러 예외 타입을 받을 수 있다
  • 스프링 6부터 RFC 9457(Problem Details for HTTP APIs) 형식의 ProblemDetail을 기본 지원해 오류 응답 구조를 표준화하기 쉽다(2026 기준)
  • 가장 구체적인 예외 타입의 처리기가 선택된다. 마지막 안전망으로 Exception 처리기를 두되, 내부 메시지·스택을 그대로 응답에 싣지 않는다

설계 포인트

  • 도메인 예외(domain exception: 주문 없음, 재고 부족)를 정의하고 상태 코드로 대응시키면 서비스 코드가 HTTP를 몰라도 된다
  • 예외를 잡아 로그만 남기고 200을 돌려주는 식으로 예외를 삼키지 않는다
  • 검증 실패(Bean Validation)와 인증·인가 실패는 다른 단계에서 난다. 시큐리티 필터에서 난 401·403은 ControllerAdvice까지 오지 않고 시큐리티의 진입점(AuthenticationEntryPoint)·접근 거부 처리기(AccessDeniedHandler)가 맡는다(시큐리티 필터체인)

예외를 어디서 잡고 어디서 던질지의 일반 원칙은 예외 처리 원칙, 요청 흐름에서의 위치는 Spring MVC 요청 흐름를 본다.

출처: Spring Framework 문서: Controller Advice · Error Responses(ProblemDetail)

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 스프링에서 외부 API 호출

    스프링 앱에서 다른 서버의 REST API를 부르는 도구는 여러 세대가 있다. 지금 새 코드라면 동기 호출은 RestClient, 리액티브·비동기는 WebClient, 인터페이스 선언 방식은 HTTP Interface나 OpenFeign이 흔한 선택이다(2026 기준).

  • 스프링 AOP

    AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)는 로깅·트랜잭션·보안·실행 시간 측정처럼 여러 클래스에 흩어져 반복되는 횡단 관심사(cross-cutting concern)를 한 곳에 모아, 비즈니스 코드를 건드리지 않고 메서드 호출 앞뒤에 끼워 넣는 방법이다. 스프링 AOP는 이를 프록시로 구현한다.

  • 의존성 주입 (DI)

    객체가 필요한 협력 객체를 직접 new로 만들지 않고, 바깥(스프링 컨테이너)에서 넣어 받는 방식. 무엇을 언제 만들고 어떻게 연결할지의 제어가 내 코드에서 컨테이너로 넘어가므로 제어의 역전(Inversion of Control, IoC)이라고도 부른다.

  • CORS

    브라우저가 다른 출처(scheme·host·port)로 보낸 요청의 응답을 읽어도 되는지, 서버가 Access-Control-Allow-* 헤더로 허락하는 규칙. 막는 주체가 브라우저라서 curl이나 서버끼리의 호출은 통과한다.

  • 스프링 설정 외부화와 프로파일

    스프링 부트는 설정값을 코드 밖(application.yml·환경변수·명령줄 인자 등)에 두고, 실행 환경에 따라 다른 값을 주입하게 해 준다. 같은 빌드 산출물을 개발·QA·운영에 그대로 쓰고 설정만 바꾸는 것이 목표다.

보기 옵션