[스프링부트] @ExceptionHandler를 통한 예외처리

윤재열·2023년 3월 28일
0

Spring

목록 보기
72/72
post-custom-banner

@ExceptionHandler 사용법

@ExceptionHandler는 Controller 계층에서 발생하는 에러를 잡아서 메서드로 처리해주는 기능입니다.

  • Service, Repository에서 발생하는 에러는 제외합니다.
@Controller
public class TestController {
	//....
    	@ExceptionHandler
        public ResponseEntity<String> handle(IOException ex){
        //....
        }
}
  • 이와 같이 @Controller로 선언된 클래스 안에서 @ExceptionHandler 어노테이션으로 메서드 안에서 발생할 수 있는 에어를 처리할 수 있습니다.

여러개의 Exception 처리

@ExceptionHandlervalue값으로 어떤 Exception을 처리할 것인지 넘겨 줄 수 있습니다.

  • 이떄 만약 value를 설정해주지 않는 다면 Exception을 잡아 버리기 때문에 구체적으로 적어주시는 것이 좋습니다.

예시

@Controller
public class TestController {
	//...
    
    @ExceptionHandler({FileSystemException.class,RemoteException.class})
    public ResponseEntity<String> handle(Exception ex) {
    //...
    }
}
  • 메서드 인자로 Exception ex를 받고 있고, @ExceptionHandlervalue값으로 특정 Exception들을 설정해주고 있습니다.
  • @ExceptionHandler({IOException.class}) 처럼 넓은 범가 아닌 ExceptionHandler({FileSystemException.class,RemoteException.class}) 로 구체적으로 적어주는 것을 권장 합니다.

ControllerAdvice

@ControllerAdvice에서 @ExceptionHandler 사용

  • @ControllerAdvice@Controllerhandler에서 발생하는 에러들을 모두 잡아줍니다.
  • @ControllerAdvice 안에서 @ExceptionHandler를 사용하여 에러를 잡을 수 있습니다.
@ControllerAdvice
public class ExceptionHandlers {

	@ExceptionHandler(FileNotFoundException.class)
    public ResponseEntity handlerFileException() {
	   return new ResponseENtity(HttpStatus.BAD_REQUEST);
    }
}

범위 설정

@ControllerAdvice는 모든 에러를 잡아주기 때문에 일부 에러만 처리하고 싶을 경우에는 따로 설정을 해주면 됩니다.

    1. Annotation
    1. basePackages(+basepackagesClasses)
    1. assignableTypes
// 1.
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}

// 2.
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}

// 3.
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}

주의 사항

  • 어노테이션, 베이스패키지 등 설정자들은 모두 런타임시에 수행되기 때문에 너무 많은 설정자들을 사용하면 성능이 떨어질 수 있습니다.

@RestControllerAdvice

  • @RestControllerAdvice@ControllerAdvice@ResponseBody를 가지고 있습니다.
  • @Controlelr처럼 작동하며 @ResponseBody를 통해 객체를 리턴 할 수 있습니다.
    @ReestControllerAdvice인터페이스
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
	// ...	
}

@ControllerAdvice VS @RestControllerAdvice

  • @ControllerAdvice@Component어노테이션을 가지고 있으므로 컴포넌트 스캔을 통해 스프링 빈으로 등록됩니다.
  • @RestControllerAdvice@ControllerAdvice@ResponseBody어노테이션으로 이루어져 있고 HTML,JSP 와 같은 뷰템플릿 보다는 ResponseBody 로 값을 리턴할 수 있습니다.

문제의 코드

리팩토링하는법

1. ErrorCode 작성

@RequiredArgsConstrutor
@Getter
public enum ErrorCode{

	//409 CONFLICT 중복된 리소스
	DUCPIATE(409,"이미 존재하는 차량번호 입니다.");
    private final int status;
    private final String message;
}
  • Enum 클래스로 사용할 에러들을 적어줍니다.
  • status 값과 error message만 프론트에 넘겨줄 예정이므로 두개만 작성하였습니다.
  • status 대신 HttpStatus를 사용할 수도 있습니다.

추가적으로 SuccessCode도 따로 만드는게 좋을 것 같다.
후에 기획이 바뀌어 HttpStatus나 message를 수정할떄 상수로 모여 있는 곳에 가면 일괄적으로 수정이 가능하기 떄문입니다.

2. CustomException 작성

@AllArgsConstructor
@Getter
public class CustomException extends RuntimeException {
	private final ErrorCode errorCode;
}
  • RuntimeException을 상속받는 CustomException클래스를 생성합니다.
  • 위에 ErrorCode에서 작성한 409 에러는 따로 잡아주어야 하기 때문에 필요한 클래스입니다.
  • Enum 타입인 ErrorCode를 필드로 추가해줍니다.

GlobalExceptionHandler 작성

@RestControllerAdvice
public class GlobalExceptionHandler {
	
    @ExceptionHandler({CustomException.class})
    protected ResponseEntity handlerCustomException(CustomException ex) {
    return new ResponseEntity(new ErrorDto(ex.getErrorCode().getStatus(), ex.getErrorCode().getMessage()), HttpStatus.valueOf(ex.getErrorCode().getStatus()));
    
    @ExceptionHandler({ Exception.class })
    protected ResponseEntity handleServerException(Exception ex) {
        return new ResponseEntity(new ErrorDto(INTERNAL_SERVER_ERROR.getStatus(), INTERNAL_SERVER_ERROR.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
  • @ExceptionHandlervalue 값으로 어떤 @Exception을 잡을지 정해줍니다.
    그래서 아까 만들었던 CustomException.class를 넣어주고,
    설정했던 ErrorCode의 status와 message만 ResponseEntity에 담아서 리턴해주었습니다.
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ErrorDto {
	private int status;
    private String message;
}

이때 ExceptionHandler가 붙은 함수는 꼭 protected or private으로 처리를 해주어야 합니다.
외부에서 함수를 부르게 되면 그대로 에러 객체를 리턴하기 때문입니다.

profile
블로그 이전합니다! https://jyyoun1022.tistory.com/
post-custom-banner

0개의 댓글