본문 바로가기

[스프링부트/업무일지 만들기] ChatGPT가 알려준 스프링 전역 예외 처리 구현 하기

ironwhale 2024. 6. 6.

서비스를 만들다 보면 어김없이 예외처리, 에러처리를 해야될 때가 반드시 옵니다. 그리고 계속 반복적으로 사용되는 예외처리 하는부분이 발생합니다. 그래서 스프링에서는 전역적으로 예외처리를 하는 @ControllerAdvice를 지원합니다. 이번에는 컨트롤러에서 발생하는 에러를 처리하는 GlobalExceptionHandler를 만들어 보겠습니다. 

 

해당 내용은 사실 제가 참조자료로 사용하는 있는 교재에는 없는 내용으로 제가 만든 코드를 챗GPT에게 리뷰 요청하니 알려줘서 적용하게 되었습니다. 


애너테이션 지정

애너테이션은 @ControllerAdvice, @RestControllerAdvice 두가지를 썼습니다. 

유효성 검사 예외 처리

유효성 검사 예외처리 부분은 빈칸으로 두면 안되는데 빈칸으로 두고 서버로 요청을 보내거나 할때 사용되는 예외처리입니다. 예를 들면 회원가입 시 빈칸으로 두면 안되는 아이디 부분을 빈칸으로 두고 회원 가입을 시도한다거나 업무일지를 빈칸으로 두고 저장하거나 이메일을 입력해야되는데 어뚱한 숫자를 입력하는 등 이런 유효성 검사에 대한 예외처리를 하는 부분입니다. 

- MethodArgumentNotValidException 예외의 발생 상황

  1. 요청 본문 유효성 검사 실패:
    • 요청 본문이 JSON 또는 XML 형식일 때, 해당 본문이 Spring의 @Valid 또는 @Validated 어노테이션이 적용된 객체로 변환될 때 유효성 검사를 수행합니다. 이때 유효성 검사 규칙을 위반하면 MethodArgumentNotValidException이 발생합니다.
  2. 폼 데이터 유효성 검사 실패:
    • HTML 폼 데이터가 바인딩될 때, @Valid 또는 @Validated 어노테이션이 적용된 객체로 변환되며 유효성 검사를 수행합니다. 이때도 유효성 검사 규칙을 위반하면 MethodArgumentNotValidException이 발생합니다.

- 예외 처리 로직

handleValidationExceptions 메서드는 발생한 예외에서 모든 유효성 검사 오류를 추출하여, 각 필드와 관련된 오류 메시지를 Map<String, String> 형태로 구성합니다. 그런 다음, 이 오류 맵을 ResponseEntity에 담아 클라이언트에게 400 BAD REQUEST 응답과 함께 반환합니다.

  • ex.getBindingResult().getAllErrors().forEach((error) -> { ... }): 예외로부터 모든 오류를 가져와 반복 처리합니다.
  • String fieldName = ((FieldError) error).getField(): 오류가 발생한 필드 이름을 추출합니다.
  • String errorMessage = error.getDefaultMessage(): 오류 메시지를 추출합니다.
  • errors.put(fieldName, errorMessage): 필드 이름과 오류 메시지를 맵에 추가합니다.
  • return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST): 오류 맵을 ResponseEntity에 담아 400 BAD REQUEST 응답을 반환합니다.

이와 같이, handleValidationExceptions 메서드는 클라이언트가 보낸 데이터의 유효성 검사가 실패했을 때 발생하는 예외를 처리하여, 클라이언트에게 적절한 오류 메시지를 반환합니다.

 

 


인증, 권한부여 실패 관련 예외 처리

이번에는 로그인 해야되는데 안했거나 권한이 없는데 권한 밖에 일을 하려고 할때 에러를 발생시키는 예외처리를 해보도록 하겠습니다. 

 

public String handleSecurityException(SecurityException ex, Model model): SecurityException을 매개변수로 받아 처리하는 메서드입니다. Model 객체는 뷰에 데이터를 전달하는 데 사용됩니다.

 

각 부분에 대한 설명

  • @ExceptionHandler(SecurityException.class): 이 어노테이션은 이 메서드가 SecurityException 예외를 처리하도록 지정합니다. 애플리케이션의 어느 부분에서 SecurityException이 발생하더라도 이 메서드가 호출됩니다.
  • @ResponseStatus(HttpStatus.FORBIDDEN): 이 어노테이션은 HTTP 응답 상태 코드를 403 FORBIDDEN으로 설정합니다. 이는 클라이언트에게 접근이 금지되었음을 알리는 상태 코드입니다.
  • public String handleSecurityException(SecurityException ex, Model model): SecurityException을 매개변수로 받아 처리하는 메서드입니다. Model 객체는 뷰에 데이터를 전달하는 데 사용됩니다.

 


이렇게 애너테이션 하나 사용 했다고 자동으로 예외처리를 해주는 스프링이라는 프레임워크는 개발자들에게 편리함을 주는거 같습니다. 

전체코드

@ControllerAdvice
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(SecurityException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public String handleSecurityException(SecurityException ex, Model model) {
        model.addAttribute("error", ex.getMessage());
        return ex.getMessage(); // 포워드할 에러 페이지
    }
}

 


이제 부터는 회원가입, 로그인, 로그아웃을 스프링 스큐리티를 이용해 구현해보도록 하겠습니다. 

댓글