Spring MVC 예외 처리 전체 흐름 완벽 이해하기
이전 글에서:
- @ExceptionHandler
- @ControllerAdvice
를 배웠습니다.
그런데 실무에서는 여기서 반드시 나오는 질문이 있습니다.
@ExceptionHandler는 누가 호출하는가?
@ControllerAdvice는 언제 개입하는가?
예외가 발생하면 Spring 내부에서 무슨 일이 일어나는가?
이 질문에 답할 수 있어야 Spring 예외 처리 구조를 진짜 이해한 것입니다.
이번 글에서는 Spring MVC 내부에서 예외가 처리되는 전체 흐름을 설명합니다.
대표 이미지



1. 전체 흐름 먼저 보기
Spring MVC에서 예외가 발생하면 아래 순서로 처리됩니다.
Client
↓
DispatcherServlet
↓
HandlerMapping
↓
HandlerAdapter
↓
Controller
↓
예외 발생
↓
HandlerExceptionResolver
↓
@ExceptionHandler
↓
@ControllerAdvice
↓
HTTP Response
2. DispatcherServlet이란?
Spring MVC의 핵심 진입점.
모든 요청은 먼저:
DispatcherServlet
으로 들어옵니다.
3. 예시 요청
GET /users/1
4. DispatcherServlet 동작
요청 수신
↓
어떤 Controller 호출할지 탐색
↓
Controller 실행
5. Controller 실행
@GetMapping("/{id}")
public User findUser(Long id) {
throw new UserNotFoundException();
}
6. 예외 발생
여기서 중요한 점.
throw new UserNotFoundException();
발생 시
Controller가 직접 처리하지 않으면
예외가 DispatcherServlet까지 올라갑니다.
7. DispatcherServlet은 catch를 가지고 있다
실제로 내부적으로는
try {
controller 실행
} catch(Exception e) {
예외 처리 로직
}
구조를 가집니다.
8. 그 다음 누구를 호출할까?
바로
HandlerExceptionResolver
입니다.
9. HandlerExceptionResolver란?
한 줄 정의
예외를 HTTP 응답으로 변환하는 전략 객체
10. Resolver 체인
Spring은 여러 Resolver를 순서대로 실행합니다.
대표적으로:
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver
11. 가장 중요한 Resolver
실무 기준 90% 이상.
ExceptionHandlerExceptionResolver
12. 역할
@ExceptionHandler 찾기
입니다.
13. 예시
@ExceptionHandler(
UserNotFoundException.class
)
public ResponseEntity<?> handle() {
}
14. 내부 흐름
예외 발생
↓
ExceptionHandlerExceptionResolver
↓
해당 예외 처리 메서드 검색
↓
메서드 실행
↓
응답 반환
15. Controller 내부 먼저 탐색
Spring은 우선
UserController
안에 있는
@ExceptionHandler
를 찾습니다.
16. 없으면?
그 다음
@ControllerAdvice
탐색.
17. ControllerAdvice 등장
@RestControllerAdvice
public class GlobalExceptionHandler {
}
18. 역할
전역 예외 처리
19. 실무 구조
Controller
↓
Service
↓
Repository
예외 발생
↓
ControllerAdvice
20. 예시
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(
UserNotFoundException.class
)
public ResponseEntity<?> handle() {
return ResponseEntity
.status(404)
.build();
}
}
21. Spring이 찾는 순서
매우 중요.
Controller 내부
↓
ControllerAdvice
↓
상위 예외
↓
Exception.class
22. 가장 구체적인 예외 우선
예:
@ExceptionHandler(
UserNotFoundException.class
)
@ExceptionHandler(
RuntimeException.class
)
동시 존재.
발생
throw new UserNotFoundException();
결과
UserNotFoundException 핸들러 실행
23. ResponseStatusExceptionResolver
두 번째 Resolver.
예외
@ResponseStatus(
HttpStatus.NOT_FOUND
)
public class UserNotFoundException
발생
throw new UserNotFoundException();
Spring이 자동으로
404 Not Found
반환.
24. DefaultHandlerExceptionResolver
Spring 기본 예외 처리기.
예:
HttpRequestMethodNotSupportedException
발생
POST만 가능한 API에 GET 호출
자동 응답
405 Method Not Allowed
25. Spring Boot에서는?
여기서도 처리 못하면
Spring Boot가 개입.
등장하는 것이
BasicErrorController
26. BasicErrorController
최후의 안전망.
예외 처리 실패 시
기본 응답 반환.
{
"timestamp":"...",
"status":500,
"error":"Internal Server Error"
}
27. 그래서 실무에서는?
대부분
@RestControllerAdvice
로 직접 처리.
28. 실제 실무 흐름
Controller
↓
Service
↓
Repository
SQLException
↓
DataAccessException
↓
BusinessException
↓
ControllerAdvice
↓
ErrorResponse
↓
JSON 반환
29. 로그는 어디서 남길까?
실무 기준.
@RestControllerAdvice
에서 한 번.
예:
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handle(
Exception e) {
log.error(
"시스템 오류",
e
);
...
}
30. 왜 한 번만?
예외를
Repository
↓
Service
↓
Controller
모두 log.error() 하면
같은 예외가 여러 번 출력됨.
31. 실무 최종 구조
HTTP 요청
↓
DispatcherServlet
↓
HandlerMapping
↓
HandlerAdapter
↓
Controller
↓
Exception 발생
↓
HandlerExceptionResolver
↓
ExceptionHandlerExceptionResolver
↓
@ControllerAdvice
↓
ErrorResponse 생성
↓
JSON 응답
32. 면접 단골 질문
Q. @ExceptionHandler는 누가 호출하나요?
DispatcherServlet
↓
HandlerExceptionResolver
↓
ExceptionHandlerExceptionResolver
이 호출.
Q. @ControllerAdvice는 언제 실행되나요?
Controller 내부에서 처리 못한 예외 발생 시
실행.
Q. Spring Boot 기본 에러 응답은 누가 만들까요?
BasicErrorController
Q. 실무에서 로그는 어디서 남기나요?
@ControllerAdvice
최상위 처리 지점.
33. 가장 중요한 핵심 한 줄
Spring MVC 예외 처리는
DispatcherServlet이 예외를 잡고,
HandlerExceptionResolver가 적절한 @ExceptionHandler 또는 @ControllerAdvice를 찾아 HTTP 응답으로 변환하는 구조이다.
이제 예외 처리 카테고리는 완전히 끝났고, 다음부터는 원래 목차대로
7. 동시성 & 멀티스레드
의 첫 번째 글인
프로세스(Process) vs 스레드(Thread)
로 넘어가면 됩니다. 이것부터 JVM의 Stack, Heap, synchronized, CAS, Atomic, ThreadPool, Virtual Thread까지 모두 연결되기 시작합니다.
'language > java' 카테고리의 다른 글
| Thread와 Runnable 완벽 이해하기 (0) | 2026.05.29 |
|---|---|
| 프로세스(Process) vs 스레드(Thread) 완벽 이해하기 (0) | 2026.05.29 |
| Spring @ControllerAdvice / @RestControllerAdvice 완벽 이해하기 (0) | 2026.05.29 |
| Spring @ExceptionHandler 완벽 이해하기 (0) | 2026.05.29 |
| Java/Spring 실무 예외 처리 패턴 완벽 이해하기 (0) | 2026.05.29 |
댓글