1. 예외 처리 과정
우리가 코드를 짜면서 예외처리는 아주 중요하면서 어렵다.
과할만큼 상세하고 다양하게 예외처리를 해주면 클라이언트와 서버는 더 안정적인 프로그램이 될 수 있도록 도와준다.
하지만 여러 예외처리를 하다보면 코드가 엄청나게 복잡해 진다.
if 문으로 잡든, try-catch로 잡든 상위 메소드로 예외처리를 위임하는 코드는 복잡해진다.
그렇게되면 유지보수에 굉장한 어려움을 겪는다.
또한 비즈니스 로직에 집중하기 어렵고, 비즈니스 로직과 관련된 코드보다 예외처리를 위한 코드가 더 많아지는 경우도 생긴다.
이런문제를 개선하기 위해 @ExceptionHandler 와 @ControllerAdvice 를 사용한다.
2. @ExceptionHandler
@ExceptionHandler의 경우 @Controller, @RestController 가 적용된 Bean 에서 발생하는 예외를 잡아서 하나의 메서드에서 처리하는 기능이다.
@ExceptionHandler에 설정한 예외가 발생하면 Handler가 실행된다.
@Service, @Repository 가 적용된 Bean 에서는 사용이 불가능하다.
아래는 @ExceptionHandler 인터페이스의 내용이다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
/**
* Exceptions handled by the annotated method. If empty, will default to any
* exceptions listed in the method argument list.
*/
Class<? extends Throwable>[] value() default {};
}
@ExceptionHandler 를 사용할때 value 설정을 통하여 어떤 예외를 잡을것인지 설정할 수 있다.
여기서 주의할 점은 value 를 설정하지 않으면 모든 예외를 잡기 때문에 예외를 설정해주어야 한다.
필요에따라 다음과같이 List 형태로 여러 예외들을 설정할 수 있다.
@ExceptionHandler({Excpetion.class, Exception2.class})
실제 @Controller Bean에 사용한 예를 보면
@Controller
public class IndexController {
// ...
@ExceptionHandler(value = NullPointerException.class)
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}
@ExceptionHandler 는 등록된 Controller에서만 적용이 된다. 다른 Controller 에서의 예외처리는 불가능하다.
똑같은 기능을 하는 코드의 중복성은 코드의 질을 떨어뜨린다.
이러한 번거로움을 해결하기 위해서 @ControllerAdvice가 등장하였다.
3. @ControllerAdvice
@ControllerAdvice 는 @Controller 어노테이션이 있는 모든곳에서의 예외를 잡을수 있도록 해준다.
@ControllerAdvice 안에 있는 @ExceptionHandler는 모든 컨트롤러에서 발생하는 예외사항을 잡을 수 있다.
@ControllerAdvice 의 속성 설정을 통하여 원하는 컨트롤러나 패키지만 선택할 수 있다.
따로 설정하지 않으면 모든 패키지에 있는 Controller에 적용된다.
아래는 @ControllerAdvice 인터페이스이다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
//...
}
@ControllerAdvice 는 새로운 클래스 파일을 만들어 사용하면 된다. 아래를 보면 ProcuctControllerAdvice 라는 클래스에 @ControllerAdvice 어노테이션을 붙였다. 그후 @ExceptionHandler 로 처리하고 싶은 예외를 처리하였다.
@ControllerAdvice
public class ProductControllerAdvice {
@ExceptionHandler(DataAccessException.class)
public ResponseEntity<String> dataExceptionHandle() {
return ResponseEntity.badRequest().build();
}
@ExceptionHandler(SQLException.class)
public ResponseEntity<String> sqlExceptionHandle() {
return ResponseEntity.status(INTERNAL_SERVER_ERROR).build();
}
}
4. @RestControllerAdvice
@RestControllerAdvice 도 @ControllerAdvice와 동일한 역할을 한다. 단지 객체를 반환할 수 있는 의미를 가지고 있다.
@ControllerAdvice 와 달리 응답의 body 객체에 넣어 반환이 가능하다.
@RestController 에서만 예외를 잡는것이 아닌 @Controller 발생하는 예외도 다 잡을수 있다.