실제 서비스를 운영하다 보면 에러는 언제든 발생할 수 있습니다. 이때 중요한 건 단순히 에러가 발생하는 것이 아니라, 에러를 어떻게 안정적으로 처리하고 사용자에게 의미 있는 메시지를 전달할 것인가입니다.
Spring Boot에서는 @ExceptionHandler와 @ControllerAdvice를 이용해 전역 예외 처리(글로벌 에러 핸들링)을 우아하게 구현할 수 있습니다. 예제 중심으로 그 개념과 활용법을 정리해 보겠습니다.
1. 예외 처리를 왜 분리해야 하는가?
- 컨트롤러 로직과 예외 로직의 분리로 가독성 향상
- API 응답을 일관된 형식(JSON)으로 유지
- 에러 로깅을 한 곳에서 관리 가능
2. @ExceptionHandler 기본 사용법
특정 컨트롤러 내에서 발생한 예외를 처리하고 싶을 때는 해당 컨트롤러 클래스 안에 @ExceptionHandler를 정의할 수 있습니다.
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id)
.orElseThrow(() -> new UserNotFoundException("사용자를 찾을 수 없습니다."));
}
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String> handleUserNotFound(UserNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}
}
3. @ControllerAdvice로 전역 예외 처리하기
프로젝트 전체에서 발생하는 공통 예외를 처리하고 싶다면 @ControllerAdvice를 활용하세요.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("USER_NOT_FOUND", ex.getMessage()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
String errorMessage = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.joining(", "));
return ResponseEntity.badRequest()
.body(new ErrorResponse("VALIDATION_ERROR", errorMessage));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse("INTERNAL_ERROR", "서버 오류가 발생했습니다."));
}
}
※ ErrorResponse 클래스 예시
public class ErrorResponse {
private String code;
private String message;
// 생성자, getter/setter 생략
}
4. 예외 커스터마이징 실무 팁
- 비즈니스 예외: 도메인 중심의 예외(UserNotFoundException 등)를 정의해 구체적인 오류 표현
- 공통 응답 포맷: 모든 오류를 같은 구조로 내려줘야 API 소비자가 해석하기 쉬움
- 로깅 전략:
log.error()는 꼭 남겨두세요. 서버 모니터링 시 매우 유용합니다.
5. 실무에서 많이 사용하는 예외 핸들러
| 예외 클래스 | 처리 목적 |
|---|---|
| IllegalArgumentException | 잘못된 파라미터 |
| MethodArgumentNotValidException | Validation 실패 |
| HttpRequestMethodNotSupportedException | 지원하지 않는 HTTP 메서드 |
| MissingServletRequestParameterException | 필수 파라미터 누락 |
| Exception | 기타 모든 예외 (fallback) |
6. 결론
Spring에서의 예외 처리는 단순히 에러를 잡는 것 이상입니다. 서비스 품질, 디버깅 효율성, 사용자 경험 모두에 직결되는 요소이기 때문에, 처음부터 제대로 설계하고 구성하는 것이 중요합니다.
@ExceptionHandler와 @ControllerAdvice를 잘 활용하면 예외 처리의 일관성과 유지보수성이 크게 향상됩니다.
'개발 > JAVA' 카테고리의 다른 글
| [JAVA] RESTful API 설계 원칙과 적용 방법 – 실전 사례 기반 가이드 (0) | 2025.10.07 |
|---|---|
| [JAVA] Spring Boot에서 국제화(i18n) 적용하기 – 다국어 지원 가이드 (0) | 2025.10.07 |
| [JAVA] Spring Validation: @Valid, @NotNull 등 유효성 검사 완전 정복 (0) | 2025.10.04 |
| [JAVA] Lombok으로 코드 간결하게 작성하기: 실무 개발자가 말하는 장단점과 주의점 (0) | 2025.10.03 |
| [JAVA] DTO와 Entity 분리하기: 왜, 어떻게, 어디까지? (0) | 2025.10.02 |
