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)보다 우선한다는 것입니다.
로컬 핸들러는 해당 컨트롤러에서 발생한 예외만 처리하며 특수한 케이스에 사용합니다. 전역 핸들러는 모든 컨트롤러의 예외를 한 곳에서 처리하며 코드 중복 제거와 에러 응답 일관성을 보장합니다.
예외 처리 흐름을 단계별로 따라가 보세요.
HTTP Request
클라이언트가 API 요청을 전송합니다.
DispatcherServlet
Spring의 프론트 컨트롤러가 요청을 수신합니다.
Controller 메서드 실행
매핑된 컨트롤러 메서드가 실행됩니다.
예외 발생!
컨트롤러 로직에서 예외가 발생합니다.
로컬 @ExceptionHandler 탐색
해당 컨트롤러 내부에서 매칭되는 핸들러를 찾습니다.
전역 @ControllerAdvice 탐색
로컬 핸들러가 없으면 @ControllerAdvice 클래스에서 탐색합니다.
에러 응답 반환
매칭된 핸들러가 표준 에러 응답을 생성하여 반환합니다.
단계 1: HTTP Request
GET /api/users/999
3. 에러 응답 표준화 — 실무 패턴
꼬리질문: "에러 응답을 표준화하려면 어떻게 해야 하나요?"
실무에서 @ControllerAdvice의 가장 중요한 역할은 에러 응답을 표준화하는 것입니다. 클라이언트가 어떤 API를 호출하든 동일한 형식의 에러 응답을 받을 수 있어야 합니다.
ResponseEntityExceptionHandler를 상속하면 Spring이 기본 제공하는 예외(MethodArgumentNotValid, HttpRequestMethodNotSupported 등)도 프로젝트의 표준 에러 형식으로 통일할 수 있습니다.
예외 유형별 에러 응답을 확인해 보세요.
예외 정보
UserNotFoundException
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 적용 범위
V1ExceptionHandler
@Order(1) — 우선순위 높음
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가 잡지 못함
참고 자료
- Spring Framework - @ControllerAdvice Javadoc — @ControllerAdvice 공식 API 문서
- Spring MVC - Controller Advice — Spring MVC 공식 레퍼런스 문서
- Baeldung - Error Handling for REST with Spring — REST API 에러 처리 실무 패턴 가이드
- RFC 7807 - Problem Details for HTTP APIs — HTTP API 에러 응답 표준 스펙
의견을 들려주세요
서비스 개선에 큰 도움이 됩니다. 익명으로 자유롭게 남겨주세요.