TIL&WIL

2023-07-14 TIL (예외 처리)

blues_jun 2023. 7. 14. 21:45

문제상황

  • 개인 과제의 요구 사항 중에서 validation의 유효성을 검사하고 이에 따른 예외를 처리하는 것이 있었다.
  • Controller에서 try-catch 로 이에 해당하는 MethodArgumentNotValidException을 처리하려고 했는데 아직 이유는 잘 모르지만 오류가 발생했다.
  • 다른 방법을 찾아보니 @ExceptionHandler로 예외를 처리할 수 있었다.

@ExceptionHandler

  • @Controller, @RestController가 적용된 Bean내에서 발생하는 예외를 잡아서 하나의 메서드에서 처리해주는 기능
  • Controller, Restcontroller에만 적용이 가능하다 (@Service 같은 빈에서는 안된다.)
  • @ExceptionHandler에 등록한 Controller에만 적용된다. ( 다른 Controller에서 같은 exception이 발생하더라도 예외 처리를 할 수 없음
  • 리턴 타입은 자유롭게 가능
  • 메서드의 파라미터도 자유롭게 가능

@ControllerAdvice

  • @ControllerAdvice : 전역으로 예외를 처리할 수 있도록 해줌

해결

에러를 해결하는데 크게 두 가지 방법이 있었다.

  • @ControllerAdvice를 활용하여 해당 예외를 Controller 전역에서 처리
  • @ExceptionHandler를 해당 Controller에 선언하여 지역에서 처리

@ControllerAdvice를 활용하여 해당 예외를 Controller 전역에서 처리

 이 방법은 새로운 클래스를 만들에서 그 클래스를 @ControllerAdvice를 선언해주고 그 안에서 @ExceptionHandler로 해당 예외를 처리해주면 된다. 이렇게 되면 Controller 전역에서 해당 예외를 같은 방법으로 처리할 수 있다.

//@ControllerAdvice : 전역으로 예외를 처리할 수 있음
@ControllerAdvice
public class ExceptionController {

    //@valid  유효성체크에 통과하지 못하면  MethodArgumentNotValidException 이 발생한다.
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ApiResponseDto> methodValidException() {
        return ResponseEntity.badRequest().body(new ApiResponseDto("잘못된 형식입니다.", HttpStatus.BAD_REQUEST.value()));
    }

}

@ExceptionHandler를 해당 Controller에 선언하여 지역에서 처리

이 방법은 해당 Controller에 메소드를 선언해주는 것이다. 해당 Controller에서 예외가 발생한 경우에 대신 예외를 처리해준다. 다른 클래스에서 발생한 에러는 처리하지 않는다.

 

@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    // 회원 가입
    // @valid 유효성체크에 통과하지 못하면  MethodArgumentNotValidException 이 발생한다. -> ControllerAdvice, ExceptionHandler 로 전역에서 예외 관리
    @PostMapping("/user/signup")
    public ResponseEntity<ApiResponseDto> signup(@Valid @RequestBody SignupRequestDto requestDto) {
        try {
            userService.signup(requestDto);
        } catch (IllegalArgumentException e) {
            return ResponseEntity.badRequest().body(new ApiResponseDto("중복된 username 입니다", HttpStatus.BAD_REQUEST.value()));
        }
        return ResponseEntity.ok().body(new ApiResponseDto("회원가입에 성공했습니다.", HttpStatus.CREATED.value()));
    }

    // 로그인 처리
    @GetMapping("/user/login/successful")
    public ResponseEntity<ApiResponseDto> login() {
        return ResponseEntity.ok().body(new ApiResponseDto("로그인에 성공했습니다.", HttpStatus.OK.value()));
    }

    @GetMapping("/user/login/fail")
    public ResponseEntity<ApiResponseDto> failLogin() {
        return ResponseEntity.ok().body(new ApiResponseDto("회원을 찾을 수 없습니다.", HttpStatus.BAD_REQUEST.value()));
    }
    
    // @valid  유효성체크에 통과하지 못하면  MethodArgumentNotValidException 이 발생한다.
    // 다른 Controller에서 동일한 예외가 발생해도 처리하지 않는다.
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ApiResponseDto> methodValidException() {
        return ResponseEntity.badRequest().body(new ApiResponseDto("잘못된 형식입니다.", HttpStatus.BAD_REQUEST.value()));
    }
    
}

spring은 새로운 개념이 끊임없이 나오는 것 같다.

이 부분을 전부 자연스럽게 사용할 수 있도록 열심히 공부하자..!