@ExceptionHandler는 Controller 계층에서 발생하는 에러를 잡아서 메서드로 처리해주는 기능입니다.
@Controller
public class TestController {
//....
@ExceptionHandler
public ResponseEntity<String> handle(IOException ex){
//....
}
}
@Controller로 선언된 클래스 안에서 @ExceptionHandler 어노테이션으로 메서드 안에서 발생할 수 있는 에어를 처리할 수 있습니다.@ExceptionHandler의 value값으로 어떤 Exception을 처리할 것인지 넘겨 줄 수 있습니다.
value를 설정해주지 않는 다면 Exception을 잡아 버리기 때문에 구체적으로 적어주시는 것이 좋습니다.@Controller
public class TestController {
//...
@ExceptionHandler({FileSystemException.class,RemoteException.class})
public ResponseEntity<String> handle(Exception ex) {
//...
}
}
Exception ex를 받고 있고, @ExceptionHandler의 value값으로 특정 Exception들을 설정해주고 있습니다.@ExceptionHandler({IOException.class}) 처럼 넓은 범가 아닌 ExceptionHandler({FileSystemException.class,RemoteException.class}) 로 구체적으로 적어주는 것을 권장 합니다.@ControllerAdvice 는 @Controller와 handler에서 발생하는 에러들을 모두 잡아줍니다.@ControllerAdvice 안에서 @ExceptionHandler를 사용하여 에러를 잡을 수 있습니다.@ControllerAdvice
public class ExceptionHandlers {
@ExceptionHandler(FileNotFoundException.class)
public ResponseEntity handlerFileException() {
return new ResponseENtity(HttpStatus.BAD_REQUEST);
}
}
@ControllerAdvice는 모든 에러를 잡아주기 때문에 일부 에러만 처리하고 싶을 경우에는 따로 설정을 해주면 됩니다.
// 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는 @ControllerAdvice와 @ResponseBody를 가지고 있습니다.@Controlelr처럼 작동하며 @ResponseBody를 통해 객체를 리턴 할 수 있습니다.@ReestControllerAdvice인터페이스@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
// ...
}
@ControllerAdvice 는 @Component어노테이션을 가지고 있으므로 컴포넌트 스캔을 통해 스프링 빈으로 등록됩니다.@RestControllerAdvice는 @ControllerAdvice와@ResponseBody어노테이션으로 이루어져 있고 HTML,JSP 와 같은 뷰템플릿 보다는 ResponseBody 로 값을 리턴할 수 있습니다.
@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를 수정할떄 상수로 모여 있는 곳에 가면 일괄적으로 수정이 가능하기 떄문입니다.
@AllArgsConstructor
@Getter
public class CustomException extends RuntimeException {
private final ErrorCode errorCode;
}
RuntimeException을 상속받는 CustomException클래스를 생성합니다.ErrorCode에서 작성한 409 에러는 따로 잡아주어야 하기 때문에 필요한 클래스입니다.Enum 타입인 ErrorCode를 필드로 추가해줍니다.@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);
}
}
@ExceptionHandler의 value 값으로 어떤 @Exception을 잡을지 정해줍니다.CustomException.class를 넣어주고,@Data
@AllArgsConstructor
@NoArgsConstructor
public class ErrorDto {
private int status;
private String message;
}
이때
ExceptionHandler가 붙은 함수는 꼭protected or private으로 처리를 해주어야 합니다.
외부에서 함수를 부르게 되면 그대로 에러 객체를 리턴하기 때문입니다.