Spring 7기 프로젝트/일정관리 프로젝트

[트러블슈팅] Spring의 글로벌 예외 처리와 커스텀 예외 클래스

JuNo_12 2025. 5. 13. 19:34

@RestControllerAdvice 란?

  1. 전역 예외 처리
    컨트롤러에서 발생하는 예외를 전역적으로(즉, 모든 컨트롤러에 대해 공통적으로) 처리할 수 있다.
    각 예외 유형마다 별도의 메서드를 만들어, 상황에 맞는 HTTP 상태 코드와 메시지를 반환할 수 있다.
  2. RestController와의 통합
    내부적으로 @ControllerAdvice와 @ResponseBody를 결합한 것과 같다.
    즉, 반환값이 자동으로 JSON, XML 등 REST 응답 형태로 변환
  3. 코드 중복 감소
    개별 컨트롤러마다 try-catch를 두지 않아도 되며, 예외 처리 로직을 한 곳에서 일관되게 관리할 수 있다.

정리 : REST API에서 발생할 수 있는 다양한 상황을 전역적으로, 효율적이고 일관되게 처리하기 위한 도구

 

 

@ExceptionHandler 란?

  1. 대상 예외 지정
    파라미터 또는 애너테이션의 값으로 처리하고 싶은 예외 클래스(예: IllegalArgumentException.class)를 지정합니다.
  2. 예외 발생 시 동작
    컨트롤러 내, 또는 @RestControllerAdvice(혹은 @ControllerAdvice) 클래스 내에 정의할 수 있으며, 지정한 예외가 발생하면 해당 메서드가 자동으로 실행됩니다.
  3. 맞춤형 응답 반환
    예외 상황에 따라 직접 메시지, 상태 코드, 또는 원하는 형식의 응답(JSON, 객체 등)을 반환할 수 있습니다.

정리 : @ExceptionHandler는 예외 상황별로 맞춤 응답을 만드는 데 사용하는 애너테이션 / 코드 가독성과 예외 처리의 일관성을 높일 수 있다.


1. GlobalExceptionHandler의 역할

  • @RestControllerAdvice를 사용하여 프로젝트 전역에서 발생할 수 있는 예외를 한 곳에서 처리할 수 있다.
  • @ExceptionHandler를 통해 각 예외 유형 별로 적절한 HTTP 상태 코드와 메시지를 반환할 수 있다.
  • 대표적인 처리 예시:
    • → 400 Bad Request InvalidPasswordException
    • → 404 Not Found ScheduleNotFoundException
    • 그 외 모든 Exception → 500 Internal Server Error
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(InvalidPasswordException.class)
    public ResponseEntity<String> handleInvalidPasswordException(InvalidPasswordException e) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
    }

    @ExceptionHandler(ScheduleNotFoundException.class)
    public ResponseEntity<String> handleNotFound(ScheduleNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
    }

    // 최종 방어막 (예기치 못한 오류 발생 시)
    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleOtherException(Exception ex) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("서버 오류가 발생했습니다: " + ex.getMessage());
    }

}
  • 모든 컨트롤러에서 중복되는 try-catch문이 필요 없어 유지보수성이 크게 향상된다.
  • 예외 별로 응답 형태와 메시지를 일관되게 구성할 수 있다.

 

@ExceptionHandler(Exception.class)

  • 이 애너테이션은 Exception 타입의 예외가 발생하면 해당 메서드가 실행되도록 설정
  • Exception.class는 모든 예외의 최상위 클래스이므로, 이 핸들러는 컨트롤러 내부에서 발생하는 예외 대부분을 처리할 수 있다.

2. 커스텀 예외 클래스

InvalidPasswordException

  • 비밀번호 불일치 등의 비즈니스 로직 예외 상황에 사용되는 커스텀 예외
  • RuntimeException 을 상속받아 필요할 때 자유롭게 throw할 수 있다. 
public class InvalidPasswordException extends RuntimeException {
    public InvalidPasswordException(String message) {
        super(message);
    }
}

 

ScheduleNotFoundException

  • 일정이 존재하지 않을 때(예: 특정 ID로 조회 시 결과가 없을 때) 사용되는 예외입니다.
  • 마찬가지로 RuntimeException 상속 후, 메시지를 입력받아 생성됩니다. 
public class ScheduleNotFoundException extends RuntimeException {

    public ScheduleNotFoundException(String message) {
        super(message);
    }
}

커스텀 예외 사용 이유

  • 예외 상황을 더 명확하게 구분하고, 상황별로 적절한 처리를 할 수 있습니다.
  • API 응답의 신뢰성이 높아지고, 디버깅 및 로그 분석이 쉬워집니다.

3. 활용 예시 흐름

  • 사용자가 잘못된 비밀번호로 일정을 수정/삭제 시도
     InvalidPasswordException  발생
    → GlobalExceptionHandler가 이 예외를 받아서 400 반환 
  • 존재하지 않는 일정(ID) 조회 시
    → ScheduleNotFoundException  발생
    → GlobalExceptionHandler가 404 반환 

 

4. 오늘의 깨달음

  • @RestControllerAdvice 하나만으로 전체 시스템의 예외 관리를 효율화할 수 있음을 경험
  • 커스텀 Exception 설계를 통해 비즈니스 로직의 명확한 예외 처리가 가능함을 확인