본문으로 건너뛰기
Advertisement

7.2 @RestControllerAdvice와 @ExceptionHandler

스프링에서는 개별 컨트롤러마다 예외를 처리하는 대신 애플리케이션 전역에서 발생하는 예외를 한 곳으로 모아 처리할 수 있는 강력한 기능인 @RestControllerAdvice(@ControllerAdvice)를 제공합니다.

전역 예외 처리기 작성

클래스 레벨에 @RestControllerAdvice를 선언하면 전역 예외 처리 담당 클래스가 됩니다. 내부 메서드에 @ExceptionHandler를 달아주면 특정 예외 타입이 발생했을 때 해당 메서드가 낚아채서 실행됩니다.

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

/**
* NullPointerException 전역 처리
*/
@ExceptionHandler(NullPointerException.class)
public ResponseEntity<ErrorResponse> handleNullPointerException(NullPointerException e) {
log.error("서버에서 Null 참조 발생", e);

ErrorResponse response = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"서버 내부에 문제가 발생했습니다."
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}

/**
* DB 관련 예외 처리 (Persistence Layer)
*/
@ExceptionHandler(DataAccessException.class)
public ResponseEntity<ErrorResponse> handleDataAccessException(DataAccessException e) {
log.error("데이터베이스 접근 오류", e);
ErrorResponse response = new ErrorResponse(500, "데이터베이스 연결에 문제가 발생했습니다.");
return ResponseEntity.internalServerError().body(response);
}

/**
* 잘못된 파라미터 타입 요청 처리 (예: 숫자가 필요한데 문자를 보냄)
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<ErrorResponse> handleTypeMismatch(MethodArgumentTypeMismatchException e) {
String message = String.format("파라미터 '%s'의 타입이 잘못되었습니다.", e.getName());
ErrorResponse response = new ErrorResponse(400, message);
return ResponseEntity.badRequest().body(response);
}

/**
* 커스텀 또는 모든 예외 처리 (Fallback)
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleAllException(Exception e) {
log.error("예상하지 못한 예외 발생", e);

ErrorResponse response = new ErrorResponse(500, "알 수 없는 시스템 오류입니다.");
return ResponseEntity.internalServerError().body(response);
}
}

유효성 검사(Validation) 예외 핸들링

Chapter 6에서 배운 Bean Validation 에러가 발생할 때 스프링은 MethodArgumentNotValidException을 뱉어냅니다. 이를 글로벌하게 핸들링하여 우리가 원하는 포맷(예: 에러가 발생한 필드명과 메시지를 조합)으로 반환할 수 있습니다.

import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;

// ... GlobalExceptionHandler 내부

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException e) {
StringBuilder errorMessage = new StringBuilder();
for (FieldError fieldError : e.getBindingResult().getFieldErrors()) {
errorMessage.append(fieldError.getField())
.append(" 필드는 ")
.append(fieldError.getDefaultMessage())
.append("; ");
}

ErrorResponse response = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
errorMessage.toString()
);
return ResponseEntity.badRequest().body(response);
}

@RestControllerAdvice를 활용하면 애플리케이션 곳곳의 예외 처리 로직을 한 클래스로 응집시켜 유지보수성과 일관성을 크게 높일 수 있습니다.

Advertisement