본문 바로가기

Back-End/Spring-MVC

스프링 글로벌 API 예외 처리와 @Valid 검증 예외 처리

@ControllerAdvice와 @ExceptionHandler란?

스프링은 @ControllerAdvice와 @ExceptionHandler를 통해 특정 컨트롤러 또는 전역적으로 발생하는 예외를 처리할 수 있도록 지원한다. 이 접근법을 사용하면 코드 중복 없이 일관된 에러 응답을 클라이언트에게 반환할 수 있다.


글로벌 API 예외 처리

공통적으로 API 예외를 처리하는 예제

1) 공통 응답 처리를 위한 응답 객체 생성

공통 응답 처리를 위한 응답 객체 생성

2) InvalidPasswordException 정의

3) GlobalExceptionHandler 클래스

  • @ExceptionHandler(InvalidPasswordException.class)
    • @ExceptionHandler는 지정된 예외가 발생했을 때 자동으로 해당 메서드가 호출되도록 설정하는 스프링의 어노테이션
    • 여기서는 InvalidPasswordException이 발생했을 때 이 메서드가 실행된다.
  • public ResponseEntity<ErrorResponse> handlerInvalidPasswordException(InvalidPasswordException e)
    • 반환 타입은 ResponseEntity<ErrorResponse>로, HTTP 응답과 함께 사용자 정의 에러 객체(ErrorResponse)를 반환한다.
    • InvalidPasswordException 객체를 매개변수로 받아 예외 메시지와 추가 정보를 처리한다.
  • ErrorResponse errorResponse = new ErrorResponse(e.getMessage(), HttpStatus.UNAUTHORIZED.value())
    1. e.getMessage() : 예외 객체에서 전달된 메시지를 가져온다.
    2. HttpStatus.UNAUTHORIZED.value() : HTTP 상태 코드 401을 숫자값으로 반환한다.
    3. ErrorResponse : 예외 메시지와 상태 코드를 포함하는 응답 객체를 생성한다.
  • return new ResponseEntity<>(errorResponse, HttpStatus.UNAUTHORIZED);
    1. ResponseEntity : HTTP 응답 객체로, 본문(body)과 상태 코드(status)를 포함한다.
    2. errorResponse : 반환할 응답 객체로, 사용자 정의 메시지와 상태 코드가 포함되어 있다.
    3. HttpStatus.UNAUTHORIZED: HTTP상태 코드 401을 설정한다.

4) 오류 응답 데이터

포스트맨으로 응답 데이터를 받음.

작동 흐름

  1. 예외 발생 : 비밀번호가 일치하지 않아 InvalidPasswordException이 발생
  2. 예외 핸들러 실행 : @ExceptionHandler가 예외를 가로채고, 해당 메서드가 호출된다.
  3. ErrorResponse 생성 : 클라이언트에게 반환할 에러 메시지와 상태 코드가 포함된 응답 객체를 생성한다.
  4. 로그 기록 : 예외 메시지를 로그에 기록하여 디버깅 활용.
  5. HTTP 응답 반환 : 상태 코드 401과 함께 에러 응답 객체를 클라이언트로 반환
[클라이언트] 
   ↓ 요청
[컨트롤러 메서드]
   ↓
[예외 발생] (InvalidPasswordException)
   ↓
[GlobalExceptionHandler]
   ↓ @ExceptionHandler 매핑
[예외 처리 핸들러 실행]
   ↓
[ErrorResponse 객체 생성]
   ↓
[HTTP 응답 생성 (ResponseEntity)]
   ↓
[HTTP 응답 반환 (401)]
   ↓
[클라이언트로 에러 메시지 반환]

@Valid 검증 실패 예외 처리

@Valid는 DTO 클래스의 필드에 제약 조건을 설정하고, 이를 기반으로 입력값 검증을 수행하는데 사용된다. 검증 실패시 MethodArgumentNotValidException이 발생한다.

공통적으로 @Valid 검증 실패 예외를 처리하는 예제

0) DTO 클래스

@Getter
public class UserDTO {

    @NotBlank(message = "이름은 필수 입력 항목입니다.")
    @Size(min = 2, max = 20, message = "이름은 2자 이상, 20자 이하여야 합니다.")
    private String name;

    @NotBlank(message = "이메일은 필수 입력 항목입니다.")
    private String email;
}

1) 공통 응답 처리를 위한 객체 생성

공통 응답 처리를 위한 응답 객체 생성

2) GlobalExceptionHandler 클래스

  • 예외 핸들러 선언
    • @ExceptionHandler(MethodArgumentNotValidException.class)
    • @ExceptionHandler는 특정 예외가 발생했을 때 해당 메서드로 처리를 위임하는 역할
    • 여기서는 MethodArgumentNotValidException 예외가 발생하면 이 메서드가 호출된다.
  • 메서드 시그니처
    • public ResponseEntity<ErrorResponse> handlerMethodArgumentNotValidException(MethodArgumentNotValidException e)
      • 이 메서드는 MethodArgumentNotValidException을 매개변수로 받아 처리하며, ResponseEntity 객체를 반환한다.
      • 예외 메시지와 상태 코드를 담은 ErrorResponse 객체를 클라이언트에게 반환
  • 에러 메시지 저장을 위한 Map 생성
    • Map<String, String> errors = new HashMap<>();
      • 검증 실패한 필드 이름과 해당 에러 메시지를 저장하기 위해 HashMap을 생성한다.
      • 필드 이름(Key)와 에러 메시지(Value)를 저장할 자료 구조를 초기화한다.
  • 검증 실패 정보 가져오기
    • e.getBindingResult().getFieldErrors()
              .forEach(error -> errors.put(error.getField(), error.getDefaultMessage()));
      1. getBindingResult() : 검증 실패 결과를 가져옴.
      2. getFieldErrors() : 필드별 검증 실패 목록을 반환한다.
      3. forEach(...) : 각 필드 에러를 순회하며, 필드 이름(getField())과 에러 메시지를 errors 맵에 저장한다.
    • 모든 검증 실패 항목을 처리하여, 필드 이름과 해당 에러 메시지 쌍으로 저장.
  • ErrorResponse 객체 생성
    • ErrorResponse errorResponse = new ErrorResponse(errors.toString(), HttpStatus.BAD_REQUEST.value());
      1. errors.toString() : 저장된 에러 메시지를 문자열로 변환
      2. HttpStatus.BAD_REQUEST.value() : HTTP 400 상태 코드를 설정
      3. ErrorResponse : 메시지와 상태 코드를 포함하는 응답 객체를 생성한다.
    • 검증 실패 메시지와 상태 코드를 담은 ErrorResponse 객체를 생성한다.
  • ResponseEntity로 응답 반환
    • return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
      1. ResponseEntity를 생성하며, 응답 본문으로 ErrorResponse 객체를 포함한다.
      2. HTTP 상태 코드를 400으로 설정한다.
    • 클라이언트에게 검증 실패 메시지와 상태 코드가 포함된 HTTP 응답을 반환

3) 오류 응답 데이터

포스트맨 응답 데이터

  1. 클라이언트 요청: 데이터를 전송하며 DTO 검증 조건에 맞지 않는 값을 포함할 수 있음.
  2. 유효성 검증: 스프링이 @Valid 어노테이션을 기반으로 데이터를 검증.
  3. 예외 발생: 검증 실패 시 MethodArgumentNotValidException 예외가 발생.
  4. 예외 처리: @ExceptionHandler 메서드가 예외를 가로채고 상세 검증 정보를 처리.
  5. 응답 반환: 클라이언트에게 유효성 검증 실패 메시지와 상태 코드(400)를 반환.
[클라이언트] 
   ↓ 요청
[컨트롤러 메서드]
   ↓ 데이터 매핑 및 @Valid 검증
[유효성 검증 실패]
   ↓ 예외 발생 (MethodArgumentNotValidException)
[GlobalExceptionHandler]
   ↓
[예외 처리 핸들러 실행]
   ↓
[응답 생성 (ErrorResponse)]
   ↓
[HTTP 400 응답]
   ↓
[클라이언트로 응답 반환]