
ControllerAdvice 없이 Spring Boot 예외 처리를 컨트롤러마다 try/catch로 작성하면 처음에는 명확해 보이지만, API가 늘어날수록 같은 오류 응답 형식이 여러 곳에 흩어집니다.
핵심은 예외를 없애는 것이 아니라 예외가 API 응답으로 바뀌는 지점을 한곳에 모으는 것입니다. 이 글은 Spring Framework 공식 문서를 기준으로 @ControllerAdvice와 @ExceptionHandler 사용 기준을 설명합니다.

Spring Boot 예외 처리를 컨트롤러에서 치우는 이유
컨트롤러의 주된 역할은 요청을 받아 애플리케이션 흐름으로 넘기고 응답을 돌려주는 것입니다. 모든 메서드에 try/catch가 들어가면 정상 흐름보다 오류 변환 코드가 더 눈에 띄게 됩니다.
@GetMapping("/users/{id}")
public UserResponse findUser(@PathVariable Long id) {
User user = userService.findById(id);
return UserResponse.from(user);
}이처럼 컨트롤러는 정상 흐름을 읽기 쉽게 두고, 사용자를 찾지 못한 예외가 404 응답으로 바뀌는 규칙은 별도 advice로 옮기는 편이 관리하기 쉽습니다.
@ControllerAdvice와 @ExceptionHandler의 역할
@ExceptionHandler는 특정 예외를 어떤 응답으로 바꿀지 선언합니다. @ControllerAdvice는 이 규칙을 여러 컨트롤러에 공통 적용할 수 있게 해 줍니다.
@RestControllerAdvice
public class ApiExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handle(UserNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("USER_NOT_FOUND", ex.getMessage()));
}
}이 구조에서는 UserNotFoundException이 어디서 발생했는지보다, API 사용자에게 어떤 오류 계약으로 보여 줄지가 더 분명해집니다.
ErrorResponse는 왜 따로 설계해야 할까
오류 응답은 단순 메시지 문자열보다 계약에 가깝습니다. 프론트엔드, 모바일 앱, 외부 API 사용자는 어떤 필드가 오는지 알아야 안정적으로 처리할 수 있습니다.
- `code`: 프로그램이 분기할 수 있는 안정적인 오류 코드
- `message`: 사용자나 개발자가 읽을 수 있는 설명
- `details`: validation field error처럼 추가 정보가 필요한 경우
- `traceId`: 운영 로그와 연결해야 할 때 선택적으로 사용
validation error는 일반 예외와 다르게 본다
@Valid 실패는 도메인 예외와 성격이 다릅니다. 보통 하나의 오류가 아니라 여러 필드의 오류가 동시에 발생할 수 있기 때문입니다.
public record FieldErrorResponse(
String field,
String reason
) {}이 경우에는 `email은 형식이 잘못됐다`, `password는 길이가 짧다`처럼 필드별 목록을 내려주는 구조가 더 실용적입니다.
실무에서 상태 코드를 고르는 기준
- 요청 형식이나 필드 검증 실패는 보통 400 계열로 본다
- 인증이 없으면 401, 권한이 없으면 403을 구분한다
- 없는 리소스는 404로 표현한다
- 비즈니스 규칙 충돌은 409를 검토한다
- 서버가 예상하지 못한 오류는 내부 메시지를 숨기고 500으로 처리한다
정리
ControllerAdvice를 이해할 때는 명령어나 패턴 이름만 외우기보다 그 도구가 해결하려는 경계를 먼저 보는 편이 좋습니다. 오늘 글의 기준은 화면과 데이터, 실행과 import, 정상 흐름과 오류 흐름, 정합성과 동시성, 개인 브랜치와 공유 브랜치를 나눠 보는 것입니다.
함께 보면 좋은 내부 글은 Spring Boot 의존성 주입은 왜 필요할까, Spring Boot profile은 언제 나눠야 할까, 자바 record는 class와 무엇이 다를까입니다. 외부 기준은 Spring Framework Docs – Exceptions, Spring Framework Docs – @ControllerAdvice, Spring Boot Docs – Error Handling를 확인했습니다.