본문 바로가기
language/java

Spring @ControllerAdvice / @RestControllerAdvice 완벽 이해하기

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

Spring @ControllerAdvice / @RestControllerAdvice 완벽 이해하기

이전 글에서 설명한:

 
@ExceptionHandler
 

는 특정 예외를 처리하는 매우 강력한 기능입니다.

하지만 큰 프로젝트에서는 문제가 하나 있습니다.

예를 들어 Controller가 50개 있다고 가정해 봅시다.

UserController
OrderController
ProductController
PaymentController
...
 

모든 Controller에:

 
@ExceptionHandler(...)
 

를 작성하면?

중복 코드 폭발
 

이 발생합니다.

그래서 Spring이 제공하는 전역 예외 처리 기능이 바로:

@ControllerAdvice

입니다.

실무 Spring 프로젝트에서는 거의 반드시 사용한다고 봐도 됩니다.


1. @ControllerAdvice란?

ControllerAdvice

여러 Controller에 공통 적용되는 기능을 정의하는 컴포넌트

입니다.

대표적으로:

  • 전역 예외 처리
  • 공통 모델 데이터 추가
  • 바인딩 설정

등을 담당합니다.


2. 가장 많이 사용하는 목적

사실상 대부분:

전역 예외 처리
 

용도입니다.


3. 구조 비교

Controller마다 처리

 
@Controller
public class UserController {

    @ExceptionHandler(...)
}
 
 
@Controller
public class OrderController {

    @ExceptionHandler(...)
}
 

문제

중복
중복
중복
 

4. ControllerAdvice 적용

 
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(...)
}
 

결과

모든 Controller 공통 적용
 

5. 실행 흐름

HTTP 요청
 ↓
Controller
 ↓
예외 발생
 ↓
DispatcherServlet
 ↓
HandlerExceptionResolver
 ↓
@ControllerAdvice 탐색
 ↓
@ExceptionHandler 실행
 ↓
HTTP 응답 생성
 

6. 기본 예제

예외

 
public class UserNotFoundException
        extends RuntimeException {
}
 

Controller

 
@RestController
@RequestMapping("/users")
public class UserController {

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

        throw new UserNotFoundException();
    }
}
 

7. 전역 처리

 
@RestControllerAdvice
public class GlobalExceptionHandler {

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

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

8. 결과

 
GET /users/1
 

 
404 Not Found
 
 
"회원 없음"
 

반환.


9. @ControllerAdvice vs @RestControllerAdvice

면접 단골.


@ControllerAdvice

 
@ControllerAdvice
 

실제로는

 
@ControllerAdvice
+
@ResponseBody 없음
 

상태.


따라서

 
return "error";
 

이면

뷰 이름
 

으로 해석 가능.


10. @RestControllerAdvice

 
@RestControllerAdvice
 

실제로

 
@ControllerAdvice
+
@ResponseBody
 

입니다.


11. 그래서 REST API에서는?

거의 무조건

 
@RestControllerAdvice
 

사용.


12. 실무 ErrorResponse 구조

보통 문자열 안 씀.


DTO 생성

 
public record ErrorResponse(

    String code,
    String message

) {}
 

13. 응답

 
@RestControllerAdvice
public class GlobalExceptionHandler {

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

        return ResponseEntity
                .status(404)
                .body(
                    new ErrorResponse(
                        "USER_NOT_FOUND",
                        "회원이 존재하지 않습니다."
                    )
                );
    }
}
 

14. 결과

 
{
  "code":"USER_NOT_FOUND",
  "message":"회원이 존재하지 않습니다."
}
 

15. 여러 예외 처리 가능

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

16. 상위 예외 처리

예:

 
@ExceptionHandler(
    BusinessException.class
)
 

그러면

 
UserNotFoundException
 
 
OrderException
 
 
ProductException
 

전부 처리 가능.


17. 우선순위

매우 중요.

예:

 
@ExceptionHandler(
    UserNotFoundException.class
)
 
 
@ExceptionHandler(
    RuntimeException.class
)
 

둘 다 존재.


발생

 
throw new UserNotFoundException();
 

결과

 
UserNotFoundException
 

핸들러가 실행.


이유

더 구체적인 타입 우선
 

18. Exception.class 처리

보통 마지막 안전망.

 
@ExceptionHandler(
    Exception.class
)
 

역할

예상하지 못한 예외 처리
 

19. 로그는 어디서 남길까?

실무 핵심.


좋은 예

 
@RestControllerAdvice
public class GlobalExceptionHandler {

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

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

        ...
    }
}
 

20. 왜 여기서?

모든 예외가 여기 도착.


따라서

로그 1회
 

만 남길 수 있음.


21. Repository에서 로그?

 
catch(Exception e) {

    log.error(...);

    throw e;
}
 

Service에서도

 
log.error(...)
 

Advice에서도

 
log.error(...)
 

결과

같은 예외
3번 기록
 

22. 실무 패턴

Repository
 ↓
Service
 ↓
Controller
 ↓
ControllerAdvice

로그는 여기서만
 

23. Validation 예외 처리

실무 매우 중요.


DTO

 
public record UserRequest(

    @NotBlank
    String name

) {}
 

검증 실패

 
MethodArgumentNotValidException
 

발생.


24. 처리

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

    ...
}
 

25. 비즈니스 예외 + 시스템 예외 분리

실무 추천.


비즈니스 예외

 
@ExceptionHandler(
    BusinessException.class
)
 

400 계열


시스템 예외

 
@ExceptionHandler(
    Exception.class
)
 

500 계열


26. 추천 구조

 
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(
        BusinessException.class
    )
    public ResponseEntity<?> business(
            BusinessException e) {

        ...
    }

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

        ...
    }
}
 

27. 실무 전체 흐름

Controller
 ↓
Service
 ↓
Repository

예외 발생
 ↓
RuntimeException
 ↓
전파
 ↓
@ControllerAdvice
 ↓
ErrorResponse 생성
 ↓
로그 기록
 ↓
HTTP 응답
 

28. 자주 하는 실수

1. Controller마다 @ExceptionHandler 작성

중복 증가.


2. Exception.class만 처리

세부 예외 구분 불가.


3. 로그 중복 기록

Repository

Service

Advice

전부 기록.


4. 500만 반환

회원 없음
↓
500
 

잘못된 설계.


29. 면접 단골 질문

Q. @ControllerAdvice는 언제 사용하나요?

여러 Controller에 공통 적용되는
예외 처리 기능 구현 시
 

Q. @ControllerAdvice 와 @RestControllerAdvice 차이는?

@RestControllerAdvice

=
@ControllerAdvice
+
@ResponseBody
 

Q. 로그는 어디서 남기는 것이 좋은가?

최상위 예외 처리 지점

@ControllerAdvice
 

30. 가장 중요한 핵심 한 줄

@ControllerAdvice는
애플리케이션 전체의 예외 처리 정책을
한 곳에서 관리하기 위한 Spring의 전역 예외 처리 메커니즘이다.
 

다음 글은

Spring MVC 예외 처리 전체 흐름

(DispatcherServlet → HandlerAdapter → Controller → ExceptionResolver → ControllerAdvice)

를 다루는 것이 좋습니다.

이걸 이해하면 Spring 예외 처리가 "왜 그렇게 동작하는지" 내부 원리까지 완전히 연결됩니다.

반응형

댓글