@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
으로 처리를 해주어야 합니다.
외부에서 함수를 부르게 되면 그대로 에러 객체를 리턴하기 때문입니다.