본문 바로가기
language/java

Spring @ExceptionHandler 완벽 이해하기

by 죄니안죄니 2026. 5. 29.
반응형

Spring @ExceptionHandler 완벽 이해하기

이전 글에서 설명한 실무 예외 처리 패턴의 핵심이:

예외 발생
↓
전파
↓
한 곳에서 처리
↓
응답 반환
 

이라고 했습니다.

그런데 Spring은 어떻게 특정 예외를 잡아서 HTTP 응답으로 변환할까요?

그 역할을 하는 것이 바로:

@ExceptionHandler

입니다.

실제로 Spring MVC 내부에서는:

Controller
↓
예외 발생
↓
DispatcherServlet
↓
ExceptionResolver
↓
@ExceptionHandler
↓
HTTP 응답 생성
 

흐름으로 동작합니다.


1. @ExceptionHandler란?

ExceptionHandler 는:

특정 예외 발생 시 실행할 메서드를 지정하는 어노테이션

입니다.


2. 가장 기본 예제

 
@Controller
public class UserController {

    @GetMapping("/users/{id}")
    public User findUser(
            @PathVariable Long id) {

        throw new UserNotFoundException();
    }

    @ExceptionHandler(
            UserNotFoundException.class
    )
    public ResponseEntity<String> handle() {

        return ResponseEntity
                .status(404)
                .body("회원 없음");
    }
}
 

3. 실행 흐름

사용자 요청

 
GET /users/1
 

Controller 실행

 
throw new UserNotFoundException();
 

Spring이 catch

 
@ExceptionHandler(
    UserNotFoundException.class
)
 

실행

404 응답 반환


4. 내부적으로 누가 찾을까?

매우 중요.

실제로는:

DispatcherServlet 이 처리합니다.


5. DispatcherServlet 흐름

HTTP 요청
 ↓
DispatcherServlet
 ↓
Controller 호출
 ↓
예외 발생
 ↓
ExceptionResolver 조회
 ↓
@ExceptionHandler 실행
 ↓
Response 반환
 

6. ExceptionResolver란?

HandlerExceptionResolver 는:

예외를 HTTP 응답으로 변환하는 Spring 컴포넌트

입니다.


7. Spring 기본 Resolver

대표적으로:

ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver
 

존재.


8. 가장 중요한 Resolver

ExceptionHandlerExceptionResolver
 

이 Resolver가

 
@ExceptionHandler
 

를 찾아서 실행합니다.


9. 예외 객체 받을 수 있음

예:

 
@ExceptionHandler(
    UserNotFoundException.class
)
public ResponseEntity<?> handle(
        UserNotFoundException e) {

    return ResponseEntity
            .status(404)
            .body(e.getMessage());
}
 

10. 메시지 사용 가능

예외

 
throw new UserNotFoundException(
        "회원 없음"
);
 

응답

 
{
  "message":"회원 없음"
}
 

11. 여러 예외 처리 가능

예:

 
@ExceptionHandler({
    UserNotFoundException.class,
    ProductNotFoundException.class
})
 

12. 상위 예외 처리도 가능

예:

 
@ExceptionHandler(
    BusinessException.class
)
 

그러면

 
UserNotFoundException
 
 
OrderException
 

전부 처리 가능.


13. 우선순위

매우 중요.

예:

 
UserNotFoundException
 

 
BusinessException
 

 
RuntimeException
 

Spring은

가장 구체적인 예외
 

를 먼저 찾음.


14. 예시

 
@ExceptionHandler(
    UserNotFoundException.class
)
 

있고

 
@ExceptionHandler(
    RuntimeException.class
)
 

도 있으면


 
UserNotFoundException
 

발생 시

첫 번째 실행.


15. ResponseEntity 활용

실무 표준.

 
@ExceptionHandler(
    UserNotFoundException.class
)
public ResponseEntity<?> handle() {

    return ResponseEntity
            .status(404)
            .body("회원 없음");
}
 

16. ErrorResponse DTO 사용

실무에서는 문자열 거의 안 씀.


예:

 
public record ErrorResponse(

    String code,
    String message

) {}
 

17. 응답

 
@ExceptionHandler(
    UserNotFoundException.class
)
public ResponseEntity<?> handle() {

    return ResponseEntity
            .status(404)
            .body(
                new ErrorResponse(
                    "USER_NOT_FOUND",
                    "회원 없음"
                )
            );
}
 

18. 결과

 
{
  "code":"USER_NOT_FOUND",
  "message":"회원 없음"
}
 

19. @ResponseStatus와 함께 사용 가능

예:

 
@ResponseStatus(
    HttpStatus.NOT_FOUND
)
 

 
@ExceptionHandler(
    UserNotFoundException.class
)
@ResponseStatus(
    HttpStatus.NOT_FOUND
)
public ErrorResponse handle() {

    return ...
}
 

20. Controller 내부에 두는 문제

초기에는 많이 씀.


예:

 
@Controller
public class UserController {

    @ExceptionHandler(...)
}
 

21. 문제점

Controller마다 중복.


UserController
OrderController
ProductController
 

전부 작성 필요.


22. 그래서 등장

다음 단계가:

 
@ControllerAdvice
 

23. @ExceptionHandler + @ControllerAdvice

실무 표준.


구조

Controller
 ↓
예외 발생
 ↓
ControllerAdvice
 ↓
@ExceptionHandler
 ↓
응답
 

24. 예시

 
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(
        UserNotFoundException.class
    )
    public ResponseEntity<?> handle() {

        ...
    }
}
 

25. 장점

모든 Controller 공통 처리.


26. 로그도 여기서 남김

실무에서는 보통

 
log.error(...)
 

 
@ControllerAdvice
 

에서 수행.


27. 예시

 
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handle(
        Exception e) {

    log.error("시스템 오류", e);

    ...
}
 

28. Spring Boot 기본 예외 처리

만약

 
@ExceptionHandler
 

없으면?


Spring Boot 기본 응답

 
{
  "timestamp":"...",
  "status":500,
  "error":"Internal Server Error"
}
 

반환.


29. 왜 직접 구현할까?

서비스마다

 
{
  "code":"USER_NOT_FOUND",
  "message":"..."
}
 

형태 통일 필요.


30. 실무에서 자주 하는 실수

1)

 
@ExceptionHandler(Exception.class)
 

만 사용

모든 예외 구분 불가.


2)

Controller마다 작성

중복 폭발.


3)

로그를 여기저기서 남김

중복 로그.


4)

HTTP 상태 코드 안 맞춤

회원 없음
↓
500 반환
 

잘못된 설계.


31. 실무 추천 구조

Controller
 ↓
Service
 ↓
Repository

예외 발생
 ↓
Custom Exception
 ↓
@ControllerAdvice
 ↓
@ExceptionHandler
 ↓
ErrorResponse
 ↓
HTTP 응답
 

32. 핵심 흐름 요약

예외 발생
 ↓
DispatcherServlet
 ↓
ExceptionResolver
 ↓
@ExceptionHandler 탐색
 ↓
응답 생성
 

33. 가장 중요한 핵심 한 줄

@ExceptionHandler는
"예외를 HTTP 응답으로 변환하는 Spring MVC의 공식 예외 처리 메커니즘"이다.
 

34. 면접 단골 질문

Q. @ExceptionHandler는 누가 호출하나요?

DispatcherServlet
 ↓
ExceptionResolver
 

이 호출.


Q. Controller 안에 둘 수 있나요?

가능

하지만 실무는
@ControllerAdvice 사용
 

Q. 여러 @ExceptionHandler가 있으면?

가장 구체적인 예외 타입 우선
 

다음 글은 Spring @ControllerAdvice / @RestControllerAdvice 입니다.

여기서 DispatcherServlet → HandlerExceptionResolver → @ExceptionHandler → @ControllerAdvice 전체 그림이 완성되고, 실무 표준 예외 처리 구조를 이해하게 됩니다.

반응형

댓글