본문 바로가기

Spring

@ExceptionHandler 와 @ControllerAdvice

 

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 발생하는 예외도 다 잡을수 있다.