본문 바로가기

Spring

Spring ExceptionHandler & ControllerAdvice

728x90
반응형

💡 SpringBoot가 제공하는 ExceptionResolver

1. ExceptionHandlerExeptionResolver

@ExceptionHandler 처리 > API 예외처리 

2. ResponseStatusExceptionResolver

@ResponseStatus(value=HttpStatus.NOT_FOUND) Http 상태코드를 지정해준다.

3. DefaultHandlerExceptionResolver

스프링 내부 기본 예외를 처리한다.


☝ ExceptionHandler

@Target ( 값 = METHOD )
  @Retention ( 값 = RUNTIME )
  @Documented 
public @interface ExceptionHandler

특정 핸들러 클래스 또는 핸들러 메서드에서 예외를 처리하기 위한 annotation

@Controller, @RestController가 적용된 Bean에서 발생하는 예외를 잡는다.

 

@ExceptionHandler(value = RuntimeException.class)
public String handle(RuntimeException ex) {
    return "RuntimeException";
}

value에 어떤 예외를 잡을 지 설정한다. value를 지정하지 않으면 모든 예외를 잡는다.

@ExceptionHandler({NullpointerException.class, IOException.clss})

처럼 List형태로 예외를 정할 수도 있다.

 

👉 적용예시

@GetMapping("/getInfo")
public ResponseEntity getInfo(@RequestParam("id") String id) throws Exception {
    if(id.equals("ex"))
        throw new RuntimeException();
    return new ResponseEntity(HttpStatus.OK);
}

@ExceptionHandler(value = RuntimeException.class)
public String handle(RuntimeException ex) {
    return "RuntimeException";
}

Controller에서 RuntimeException이 발생하면 ExceptionHandler를 호출한다.

다른 컨트롤러에서 Exception발생시 동직하지 않으므로 해당 컨트롤러에서도 구현을 해줘야 한다.

똑같은 기능을 반복하는 것을 피하기 위해 @ControllerAdvice를 적용할 수 있다.


☝ @ControllerAdvice

@Controller에서 발생하는 예외를 잡을 수 있다. 새로운 클래스를 만들어서 사용할 수 있다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {

	@AliasFor("basePackages")
	String[] value() default {};
}

패키지 상관없이 전역에서 사용할 수 있다.


☝ @RestControllerAdvice

@ControllerAdvice와 동일한데 응답의 Body에 객체를 넣어서 반환할 수 있다. @RestController와 @Controller모두 예외를 잡을 수 있다. @ResponseBody를 통해 객체를 리턴할 수 있다는 뜻이다.

@RestControllerAdvice
public class CommonExceptionHandler {

    @ExceptionHandler(value = RuntimeException.class)
    public ResponseEntity handle(RuntimeException ex) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
    
    @ExceptionHandler(DataAccessException.class)
    public ResponseEntity<String> dataExceptionHandle() {
        return ResponseEntity.badRequest().build();
    }
}

☝ ResponseStatusExceptionResolver

- @ResponseStatus

- ResponseStatusException

위의 두 가지 경우를 처리한다. 

 

🙂@ResponseStatus

@Slf4j
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class CommonBadRequestException extends RuntimeException{

    public CommonBadRequestException(String message) {
        super(message);
    }
}

RuntimeException을 상속 받아서 구현한다.

ResponseStatus에 응답할 코드를 넣으면 된다.

 

@GetMapping("/resEx")
public void resEx() {
    throw new CommonBadRequestException("RES Exception");
}

resEx를 호출하면 500이 리턴된다.

 

⛔ @ExceptionHandler에 에러가 설정되어 있으면 우선순위가 더 높다.

ERROR Controller 설정할 경우 /error가 호출되서 ErrorController에 설정된 @ResponseStatus가 우선순위가 높다.

 

👇 ErrorController 설정방법

출처: https://tweety1121.tistory.com/entry/Spring-boot-thymeleaf-로-Error페이지-처리 [Pli's 개발일기]

 

Spring boot thymeleaf 로 Error페이지 처리

🔍 모든 에러를 다 잡아낼 수 없기 때문에 Spring에서 ErrorController를 구현해서 에러페이지를 처리할 수 있다. package org.springframework.boot.web.servlet.error; import org.springframework.stereotype.C..

tweety1121.tistory.com

 

🙂ResponseStatusException

ResponseStatus의 경우 ActorNotFoundException이 발생하면 동일한 오류메시지와 상태코드를 생성한다.

@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Actor Not Found")
public class ActorNotFoundException extends Exception {
    // ...
}

예외와 긴말한 결합이 생긴다.

@GetMapping("/actor/{id}")
public String getActorName(@PathVariable("id") int id) {
    try {
        return actorService.getActor(id);
    } catch (ActorNotFoundException ex) {
        throw new ResponseStatusException(
          HttpStatus.NOT_FOUND, "Actor Not Found", ex);
    }
}

대안으로 ResponseStatusException을 생성하여 사용하면 동일한 유형의 예외라도 별도로 처리할 수 있고 

다른 상태코드를 설정할 수 있어서 긴밀한 결합을 줄일 수 있다.

728x90
반응형