Spring

Spring @ControllerAdvice에 대해서 설명해주세요

@ControllerAdvice가 예외를 잡는 원리부터 에러 응답 표준화, 다중 Advice 우선순위, 적용 범위 제한까지 — 인터랙티브 시각화로 완전 정복합니다.

2026년 3월 18일 · 약 10분 읽기

Q. "Spring의 @ControllerAdvice가 무엇인지, 예외 처리 흐름과 함께 설명해주세요."

예상 꼬리질문

답변 가이드

"@ControllerAdvice는 모든 컨트롤러에 걸친 전역 예외 처리, 모델 속성, 데이터 바인딩을 한 클래스에 집중시키는 Spring의 특수 컴포넌트입니다."

"예외 발생 시 로컬(@ExceptionHandler in Controller) → 전역(@ControllerAdvice) 순서로 탐색합니다. 로컬 핸들러가 더 우선순위가 높습니다."

"실무에서는 @RestControllerAdvice + 표준 ErrorResponse로 API 에러 응답을 일관되게 관리합니다. ResponseEntityExceptionHandler를 상속하면 Spring 기본 예외도 표준 형식으로 통일할 수 있습니다."

Spring으로 REST API를 개발하다 보면 예외 처리 코드가 모든 컨트롤러에 반복되는 경험을 하게 됩니다. @ControllerAdvice는 이 문제를 해결하기 위해 등장한 Spring의 전역 예외 처리 메커니즘입니다.

각 컨트롤러마다 try-catch로 예외를 처리하면 코드 중복은 물론, 에러 응답 형식이 컨트롤러마다 달라지는 문제가 생깁니다. @ControllerAdvice는 예외 처리 로직을 한 곳에 집중시켜 이 문제를 해결합니다.


1. ControllerAdvice란 무엇인가

꼬리질문: "@ControllerAdvice는 어떤 역할을 하나요?"

@ControllerAdvice는 Spring Framework에서 제공하는 특수한 컴포넌트입니다. 이름 그대로 모든 컨트롤러에 대한 "조언(Advice)"을 제공합니다. AOP의 개념을 빌려온 이름이지만, 실제로는 세 가지 기능을 담당합니다.

@RestControllerAdvice는 @ControllerAdvice + @ResponseBody의 합성 애노테이션입니다. REST API 서버에서는 거의 항상 @RestControllerAdvice를 사용합니다.

@ControllerAdvice의 세 가지 역할을 직접 확인해 보세요.

@ExceptionHandler전역 예외 처리

모든 컨트롤러에서 발생하는 예외를 한 곳에서 처리합니다. 각 컨트롤러마다 try-catch를 작성할 필요 없이, 예외 타입별로 핸들러를 정의하면 됩니다. 실무에서 가장 많이 사용되는 기능입니다.

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handle(
            BusinessException ex) {
        return ResponseEntity
            .status(ex.getHttpStatus())
            .body(ErrorResponse.of(
                ex.getErrorCode(),
                ex.getMessage()));
    }
}

실무 팁: REST API 프로젝트에서는 @RestControllerAdvice + @ExceptionHandler조합만으로도 대부분의 요구사항을 충족할 수 있습니다. @ModelAttribute와 @InitBinder는 주로 뷰 기반(Thymeleaf) 프로젝트에서 활용됩니다.

2. 예외 처리 흐름 — 어떻게 동작하는가

꼬리질문: "예외가 발생했을 때 Spring이 핸들러를 탐색하는 순서를 설명해주세요"

컨트롤러에서 예외가 발생하면 Spring은 정해진 순서에 따라 예외를 처리할 핸들러를 탐색합니다. 핵심은 로컬(@ExceptionHandler)이 전역(@ControllerAdvice)보다 우선한다는 것입니다.

로컬 핸들러는 해당 컨트롤러에서 발생한 예외만 처리하며 특수한 케이스에 사용합니다. 전역 핸들러는 모든 컨트롤러의 예외를 한 곳에서 처리하며 코드 중복 제거와 에러 응답 일관성을 보장합니다.

예외 처리 흐름을 단계별로 따라가 보세요.

1 / 7
1

HTTP Request

클라이언트가 API 요청을 전송합니다.

2

DispatcherServlet

Spring의 프론트 컨트롤러가 요청을 수신합니다.

3

Controller 메서드 실행

매핑된 컨트롤러 메서드가 실행됩니다.

4

예외 발생!

컨트롤러 로직에서 예외가 발생합니다.

5

로컬 @ExceptionHandler 탐색

해당 컨트롤러 내부에서 매칭되는 핸들러를 찾습니다.

6

전역 @ControllerAdvice 탐색

로컬 핸들러가 없으면 @ControllerAdvice 클래스에서 탐색합니다.

7

에러 응답 반환

매칭된 핸들러가 표준 에러 응답을 생성하여 반환합니다.

단계 1: HTTP Request

GET /api/users/999

진행률14%

3. 에러 응답 표준화 — 실무 패턴

꼬리질문: "에러 응답을 표준화하려면 어떻게 해야 하나요?"

실무에서 @ControllerAdvice의 가장 중요한 역할은 에러 응답을 표준화하는 것입니다. 클라이언트가 어떤 API를 호출하든 동일한 형식의 에러 응답을 받을 수 있어야 합니다.

ResponseEntityExceptionHandler를 상속하면 Spring이 기본 제공하는 예외(MethodArgumentNotValid, HttpRequestMethodNotSupported 등)도 프로젝트의 표준 에러 형식으로 통일할 수 있습니다.

예외 유형별 에러 응답을 확인해 보세요.

예외 정보

Exception 클래스

UserNotFoundException

HTTP Status

404 Not Found

에러 코드

USER_NOT_FOUND

메시지

사용자를 찾을 수 없습니다.

JSON 응답

{
  "code": "USER_NOT_FOUND",
  "message": "사용자를 찾을 수 없습니다.",
  "timestamp": "2026-03-18T12:00:00",
  "details": []
}

@ExceptionHandler 코드

@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handle(
        UserNotFoundException ex) {
    return ResponseEntity
        .status(HttpStatus.NOT_FOUND)
        .body(ErrorResponse.of(
            "USER_NOT_FOUND",
            ex.getMessage()));
}

4. 다중 Advice 우선순위와 적용 범위

꼬리질문: "다중 ControllerAdvice가 있을 때 우선순위는 어떻게 되나요?"

프로젝트가 커지면 하나의 @ControllerAdvice로는 부족할 수 있습니다. 도메인별, API 버전별로 Advice를 분리하고 싶을 때 @Order적용 범위 제한을 활용합니다.

세 가지 방법을 사용할 수 있습니다: basePackages(패키지 기반 제한), assignableTypes(컨트롤러 타입 기반 제한), annotations(특정 애노테이션이 붙은 컨트롤러만 대상).

범위 제한 방식을 비교해 보세요.

@ControllerAdvice(basePackages = "com.example.api.v1")

특정 패키지에 속한 컨트롤러에만 Advice를 적용합니다.

패키지 구조 — Advice 적용 범위

com.example/
├─ api/
├─ v1/
└─ UserControllerV1ExceptionHandler
└─ OrderControllerV1ExceptionHandler
├─ v2/
└─ UserControllerGlobal
├─ admin/
└─ AdminControllerGlobal
#1

V1ExceptionHandler

@Order(1) — 우선순위 높음

#2

GlobalExceptionHandler

@Order(2) — 우선순위 낮음

핵심: @Order 값이 낮을수록 우선순위가 높습니다. 같은 예외를 처리하는 핸들러가 여러 Advice에 있으면, Order가 낮은 것이 먼저 실행됩니다. 특정 Advice가 매칭되면 전역 Advice는 실행되지 않습니다.

5. 테스트와 주의사항

꼬리질문: "@ControllerAdvice를 테스트하는 방법과 주의사항은 무엇인가요?"

@ControllerAdvice는 @WebMvcTest와 함께 쉽게 테스트할 수 있습니다. @WebMvcTest는 자동으로 @ControllerAdvice 빈을 로드하므로, MockMvc로 예외 상황을 만들고 에러 응답을 검증하면 됩니다.

주의사항으로 Filter 예외는 미처리됩니다. @ControllerAdvice는 DispatcherServlet 이후에서만 동작하므로, Spring Security Filter에서 발생한 예외는 처리하지 못합니다. @Async 메서드에서 발생한 예외도 잡지 못하며, AsyncUncaughtExceptionHandler 설정이 필요합니다.

@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void 존재하지_않는_사용자_조회시_404_반환() throws Exception {
        mockMvc.perform(get("/api/users/999"))
            .andExpect(status().isNotFound())
            .andExpect(jsonPath("$.code").value("USER_NOT_FOUND"));
    }
}

면접 체크리스트

이 항목들을 자신 있게 설명할 수 있다면 @ControllerAdvice 질문은 준비 완료입니다.

  • - @ControllerAdvice: 전역 예외 처리, 모델 속성, 데이터 바인딩을 한 곳에 집중
  • - 우선순위: 로컬 → 전역 순서로 예외 핸들러가 탐색됨
  • - @RestControllerAdvice: @ControllerAdvice + @ResponseBody의 합성 — REST API의 표준
  • - ResponseEntityExceptionHandler: 상속 시 Spring 기본 예외도 표준 형식으로 처리
  • - 주의사항: Filter 예외, @Async 예외는 @ControllerAdvice가 잡지 못함

참고 자료


의견을 들려주세요

서비스 개선에 큰 도움이 됩니다. 익명으로 자유롭게 남겨주세요.

0 / 1000
이전@Controller vs @RestController
다음Filter vs Interceptor vs AOP (준비 중)