[Spring] @ControllerAdvice, @ExceptionHandler

손시연·2022년 8월 2일
0

project

목록 보기
7/11

스프링 부트의 예외 처리 방식

@ControllerAdvice로 모든 컨트롤러에서 발생할 예외를 정의
@ExceptionHandler를 통해 발생하는 예외마다 처리할 메소드를 정의

  • @ControllerAdvice
    • @Controller@RestController에서 발생한 예외를 한 곳에서 관리하고 처리할 수 있게 도와주는 어노테이션
    • try-catch 를 통한 예외 처리는 코드라인도 길어지고 가독성이 떨어짐
    • 예외 발생 시 json 형태로 결과를 반환하기 위해서는 @RestControllerAdvice 를 사용하면 됨
    • 동일한 유형의 Error Response 반환
    • 확장성이 용이한 Custom Exception 사용
  • @ExceptionHandler
    • 예외 처리 상황이 발생하면 해당 Handler로 처리하겠다고 명시하는 어노테이션
    • 어노테이션 뒤에 괄호를 붙여 어떤 ExceptionClass 를 처리할지 설정할 수 있음
      • @ExceptionHandler(00Exception.class)
    • Controller 내에서, ControllerAdvice 내에서 모두 ExceptionHandler 를 호출할 수 있음
      • Controller 내에서 호출한 경우가 우선순위를 가짐

실행 코드

  • @RestController 에서 호출
    • @RestControllerAdvice 에서 호출한 것보다 우선순위가 높음
@RestController
public class ExceptionController {

	private final Logger LOGGER = LoggerFactory.getLogger(ExceptionController.class);

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<Map<String, String>> ExceptionHandler(Exception e) {
        HttpHeaders responseHeaders = new HttpHeaders();
//        responseHeaders.add(HttpHeaders.CONTENT_TYPE, "application/json");
        HttpStatus httpStatus = HttpStatus.BAD_REQUEST;

        LOGGER.info(e.getMessage());
        LOGGER.info("Controller 내 ExceptionHandler 호출");

        Map<String, String> map = new HashMap<>();
        map.put("error type", httpStatus.getReasonPhrase());
        map.put("code", "400");
        map.put("message", "에러 발생");

        return new ResponseEntity<>(map, responseHeaders, httpStatus);
    }
}
  • @RestControllerAdvice 에서 호출
    • @RestController 에서 호출한 것보다 우선순위가 낮음
    • 함께 있으면 호출되지 않음
@RestControllerAdvice
public class AroundHubExceptionHandler {

	private final Logger LOGGER = LoggerFactory.getLogger(AroundHubExceptionHandler.class);

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<Map<String, String>> ExceptionHandler(Exception e) {
        HttpHeaders responseHeaders = new HttpHeaders();
//        responseHeaders.add(HttpHeaders.CONTENT_TYPE, "application/json");
        HttpStatus httpStatus = HttpStatus.BAD_REQUEST;

        LOGGER.error("Advice 내 ExceptionHandler 호출, 원인: {}, 메시지: {}", e.getCause(), e.getMessage());

        Map<String, String> map = new HashMap<>();
        map.put("error type", httpStatus.getReasonPhrase());
        map.put("code", "400");
        map.put("message", "에러 발생");

        return new ResponseEntity<>(map, responseHeaders, httpStatus);
    }
}
  • 테스트(ExceptionController.class)
@RestController
public class ExceptionController {
    @PostMapping("/exception")
    public void exceptionTest() throws Exception {
        throw new Exception();
    }
}
  • 실행결과
    • @RestController 에서 호출한 것이 RestControllerAdvice 에서 호출한 것보다 우선순위가 높음 -> Controller 내 ExceptionHandler 호출 로그 출력


예외 처리 커스터마이징

  • AroundHubExceptionHandler.java
    • @ExceptionHandler(value = Exception.class) : Exception.class 에러가 발생하면 JSON 형식으로 발생한 예외를 정의함
    • @ExceptionHandler(value = AroundHubException.class) : 다양한 예외 사례들을 오버라이딩으로 나열하여 처리
@RestControllerAdvice
public class AroundHubExceptionHandler {

    private final Logger LOGGER = LoggerFactory.getLogger(AroundHubExceptionHandler.class);

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<Map<String, String>> ExceptionHandler(Exception e) {
        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.add(HttpHeaders.CONTENT_TYPE, "application/json");
        HttpStatus httpStatus = HttpStatus.BAD_REQUEST;

        LOGGER.error("ExceptionHandler 호출, 원인: {}, 메시지: {}", e.getCause(), e.getMessage());

        Map<String, String> map = new HashMap<>();
        map.put("error type", httpStatus.getReasonPhrase());
        map.put("code", "400");
        map.put("message", "에러 발생");

        return new ResponseEntity<>(map, responseHeaders, httpStatus);
    }

    // ExceptionHandler 오버라이딩
    @ExceptionHandler(value = AroundHubException.class)
    public ResponseEntity<Map<String, String>> ExceptionHandler(AroundHubException e) {
        HttpHeaders responseHeaders = new HttpHeaders();

        LOGGER.error("AroundHubException 호출, 원인: {}, 메시지: {}", e.getCause(), e.getMessage());

        Map<String, String> map = new HashMap<>();
        map.put("error type", e.getHttpStatusType());
        map.put("error code", Integer.toString(e.getHttpStatusCode()));
        map.put("message", e.getMessage());

        return new ResponseEntity<>(map, responseHeaders, e.getHttpStatus());
    }
}
  • AroundHubException.java
    • 에러 생성자
public class AroundHubException extends Exception {

    private static final long serialVersionUID = 4663380430591151694L;

    private ExceptionClass exceptionClass;
    private HttpStatus httpStatus;

    public AroundHubException(ExceptionClass exceptionClass, HttpStatus httpStatus, String message) {
        super(exceptionClass.toString() + message);
        this.exceptionClass = exceptionClass;
        this.httpStatus = httpStatus;
    }

    public int getHttpStatusCode() {
        return httpStatus.value();
    }

    public String getHttpStatusType() {
        return httpStatus.getReasonPhrase();
    }

    public HttpStatus getHttpStatus() {
        return httpStatus;
    }
}
  • Constants.java, enum ExceptionClass
    • 보여줄 에러 메시지를 만듦
public class Constants {

    public enum ExceptionClass {

        MEMBER("Member"), DRAWING("Drawing"), HEART("Heart"), SCRAP("Scrap"), NFT("Nft");

        private String exceptionClass;

        ExceptionClass(String exceptionClass) {
            this.exceptionClass = exceptionClass;
        }

        public String getExceptionClass() {
            return exceptionClass;
        }

        @Override
        public String toString() {
            return getExceptionClass() + " Exception. ";
        }
    }
}
  • 테스트(ExceptionController.class)
@RestController
public class ExceptionController {


    @PostMapping(value = "/member/exception")
    public void exceptionTest2() throws AroundHubException {
        throw new AroundHubException(Constants.ExceptionClass.MEMBER, HttpStatus.FORBIDDEN, "접근이 금지되었습니다.");
    }
}
  • 실행 결과
    • AroundHubException.java 와 관련된 에러 로그 출력

profile
Server Engineer

0개의 댓글