2025년 7월 6일
조회 : 36|2분 읽기
Spring 전역 예외 처리기
@RestControllerAdvice를 활용한 전역 예외 처리 실전 적용기
이번 글에서는 Spring REST API 개발 과정에서 자주 마주치는 예외 상황을 @RestControllerAdvice를 통해 어떻게 일관되고 깔끔하게 처리할 수 있는지에 대해 설명드리겠습니다.
💥 왜 전역 예외 처리가 필요할까요?
API를 개발하다 보면 다양한 이유로 예외가 발생합니다. 예를 들어 다음과 같은 경우가 있습니다:
- 사용자가 필수 값을 누락했을 때
- JSON의 필드 타입이 잘못되었을 때
- 서버 내부 로직에서 오류가 발생했을 때
이러한 예외들을 각각의 컨트롤러에서 일일이 try-catch로 처리하게 되면 다음과 같은 문제들이 발생합니다:
- ❌ 코드 중복: 비슷한 예외 처리 코드가 여러 곳에 반복됩니다.
- ❌ 유지보수 어려움: 예외 처리 정책이 바뀌면 모든 컨트롤러 코드를 수정해야 합니다.
- ❌ 일관성 부족: 응답 형식이 달라 프론트엔드에서 처리하기 어려워집니다.
@RestControllerAdvice를 활용하면 이러한 문제를 해결하고, 예외 처리 로직을 하나의 클래스로 통합하여 보다 일관성 있고 유지보수하기 쉬운 구조를 만들 수 있습니다.
🧱 기본 사용법: 예외를 한 곳에서 처리하기
@RestControllerAdvice는 @ControllerAdvice와 @ResponseBody를 결합한 애노테이션입니다.
REST API 환경에서 예외 발생 시 JSON 형태의 응답을 반환하는 데 사용됩니다.
REST API 환경에서 예외 발생 시 JSON 형태의 응답을 반환하는 데 사용됩니다.
다음은 가장 기본적인 예제입니다:
java1@RestControllerAdvice 2public class GlobalExceptionHandler { 3 4 @ExceptionHandler(IllegalArgumentException.class) 5 public ResponseEntity<String> handleIllegalArgument(IllegalArgumentException e) { 6 return ResponseEntity 7 .badRequest() 8 .body("잘못된 입력입니다: " + e.getMessage()); 9 } 10 11 @ExceptionHandler(RuntimeException.class) 12 public ResponseEntity<String> handleRuntime(RuntimeException e) { 13 return ResponseEntity 14 .status(HttpStatus.INTERNAL_SERVER_ERROR) 15 .body("서버 오류가 발생했습니다."); 16 } 17}
🔄 예외 처리 흐름 다이어그램
아래 다이어그램은 예외가 발생했을 때 @RestControllerAdvice가 어떻게 작동하는지를 보여줍니다:
🎯 특정 클래스나 패키지에만 적용하고 싶을 때
기본적으로 @RestControllerAdvice는 전체 컨트롤러에 적용되지만,
필요한 경우 특정 패키지나 특정 클래스에만 적용할 수 있습니다.
이를 위해 basePackages, basePackageClasses, assignableTypes 속성을 사용할 수 있습니다.
필요한 경우 특정 패키지나 특정 클래스에만 적용할 수 있습니다.
이를 위해 basePackages, basePackageClasses, assignableTypes 속성을 사용할 수 있습니다.
📦 basePackages: 특정 패키지에만 적용
java1@RestControllerAdvice(basePackages = "com.example.api.user") 2public class UserExceptionHandler { 3 // user 패키지 하위 컨트롤러에만 적용됩니다. 4}
🧬 basePackageClasses: 특정 클래스가 속한 패키지에만 적용
java1@RestControllerAdvice(basePackageClasses = SomeController.class) 2public class SomeControllerAdvice { 3 // SomeController가 속한 패키지 내 컨트롤러에만 적용됩니다. 4}
🔠 assignableTypes: 특정 클래스에만 적용
java1@RestControllerAdvice(assignableTypes = {UserController.class}) 2public class UserControllerAdvice { 3 // UserController에서 발생한 예외에만 적용됩니다. 4}
🛠 실무에서 유용한 예외 처리 팁
✅ 커스텀 예외 정의하기
비즈니스 도메인에 맞는 예외를 직접 정의하여 명확한 책임 분리를 할 수 있습니다.
java1public class UserNotFoundException extends RuntimeException { 2 public UserNotFoundException(String message) { 3 super(message); 4 } 5}
java1@ExceptionHandler(UserNotFoundException.class) 2public ResponseEntity<String> handleUserNotFound(UserNotFoundException ex) { 3 return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage()); 4}
✅ 일관된 에러 응답 포맷 설계하기
API 응답을 아래와 같은 형태로 통일하면 프론트엔드에서도 안정적으로 파싱할 수 있습니다.
json1{ 2 "code": "INVALID_ARGUMENT", 3 "message": "입력값이 유효하지 않습니다", 4 "errors": [ 5 { 6 "field": "name", 7 "reason": "공백일 수 없습니다" 8 } 9 ], 10 "timestamp": "2025-07-06T20:03:00", 11 "path": "/api/v1/user" 12}
🧾 Spring에서 자주 마주치는 기본 예외들
- MethodArgumentNotValidException: @Valid 실패 시 발생합니다.
- BindException: @ModelAttribute 바인딩 실패 시 발생합니다.
- ConstraintViolationException: @RequestParam, @PathVariable 검증 실패 시 발생합니다.
- HttpMessageNotReadableException: JSON 파싱 오류 시 발생합니다.
✅ 마무리하며
@RestControllerAdvice는 Spring REST API 개발에서 예외 처리의 복잡성을 줄이고
일관된 사용자 경험을 제공하기 위한 강력한 도구입니다.
일관된 사용자 경험을 제공하기 위한 강력한 도구입니다.