스프링 부트의 예외처리 방식은 크게 2개가 존재
@ControllerAdvice를 통한 모든 Controller에서 발생할 수 있는 예외처리
@ExceptionHandler를 통한 특정 COntroller의 예외처리
@ControllerAdvice로 모든 컨트롤러에서 발생할 예외를 정의하고
@ExceptionHandler를 통해 발생하는 예외마다 처리할 메소드를 정의
AroundHubException으로 새로운 Exception을 만들 수 있다
모든 예외 클래스는 Throwable클래스를 상속받고 있음
Exception은 수많은 자식 클래스가 있고
RuntimeException은 UncheckedExcpetion이며, 그 외 Exception은 Checked Exception으로 볼 수 있음
처리여부가 가장 큰 차이!
- @ControllerAdvice, @RestControllerAdvice
@ControllerAdvice는 Spring에서 제공하는 어노테이션
@Controller나 @RestController에서 발생하는 예외를 한 곳에서 관리하고 처리할 수 있게 하는 어노테이션
설정을 통해 범위 지정이 가능하며, Default값으로 모든 Controller에 대해 예외처리를 관리함
- @RestControllerAdvice(basePackages="arounghub.thinkground.studio")와 같이 패키지 범위를 설정할 수 있음
예외 발생 시 json의 형태로 결과를 반환하기 위해서
는@RestControllerAdvice
를 사용하면 됨.
- @ExceptionHandler
예외처리 상황이 발생하면 해당 Handler로 처리하겠다고 명시하는 어노테이션
어노테이션 뒤에 괄호를 붙여 어떤 ExceptionVlass를 처리할지 설정할 수 있음
- @ExceptionHandler(00Exception.calss)
Exception.class는 최상위 클래스로 하위 세부 예외 처리 클래스로 설정한 핸들러가 존재하면, 그 핸들러가 우선처리하게 되며, 처리되지 못하는 예외처리에 대해 ExcpetionClass에서 핸들링함.
@ControllerAdvice로 설정된 클래스 내에서 메소드로 정의할 수 있지만, 각 Controller 안에 설정도 가능
전역설정(@ControllerAdvice)보다 지역설정(Controller)으로 정의한 Handler가 우선순위를 가짐
: NullPOintException은 자식클래스니까 우선순위를 갖는다.
실습
//예외처리 예시
@PostMapping("/exception")
public void exceptionTest() throws Exception{
throw new Exception();
}
@ExceptionHandler(value=Exception.class)
public ResponseEntity<Map<String, String>> ExceptionHandler(Exception e){
HttpHeaders responseHeaders=new HttpHeaders();
HttpStatus httpStatus=HttpStatus.BAD_REQUEST;
LOGGER.info(e.getLocalizedMessage());
LOGGER.info("Controller 내 Exception 호출");
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);
}
package com.example.testproject.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class HelloController {
//
private final Logger LOGGER= LoggerFactory.getLogger(HelloController.class);
@RequestMapping("/hello")
public String hello(){
return "Hello World";
}
@PostMapping("log-test")
public void logTest(){
LOGGER.trace("trace log");
LOGGER.debug("debug log");
LOGGER.info("info log");
LOGGER.warn("warn log");
LOGGER.error("error log");
}
//예외처리 예시
@PostMapping("/exception")
public void exceptionTest() throws Exception{
throw new Exception();
}
@ExceptionHandler(value=Exception.class)
public ResponseEntity<Map<String, String>> ExceptionHandler(Exception e){
HttpHeaders responseHeaders=new HttpHeaders();
HttpStatus httpStatus=HttpStatus.BAD_REQUEST;
LOGGER.info(e.getLocalizedMessage());
LOGGER.info("Controller 내 Exception 호출");
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);
}
}
- HelloController 스웨거 실행
json형태의 responsebody를 받고 HelloController null 찍힘.
HelloController.java vs @RestControllerAdvice 어노테이션 설정한ConAroundHubExcpetionHandler.java 둘중 HelloController가 우선함을 알 수 있음.
- HelloController의 @ExceptionHandler 주석 처리하고 진행하면?
-> @RestControllerAdvice 어노테이션 설정한ConAroundHubExcpetionHandler.java 를 타서 예외를 설정한다.
예외처리 2가지
- 예외가 발생해서 처리할 수 있는 경우
try-catch : 대체 데이터로 처리 가능
@RestControllerAdvice(통합적으로 관리 가능한 @Exception 처리) : 에러메세지를 어떻게 통합적으로 관리를 할것인가? 처리하지 않고 오류발생했다고 클라이언트쪽에 되받아치는 방식 사용가능 / 각각의 상황에 따라 디테일하게 처리 해주면 통합처리 필요없을수도 있음.- 처리할 수 없는 경우
자바에서 제공해주는 Exception으로 처리가능한 부분 처리해주고, 미세포인트 잡아서 처리가능한 부분은 => 커스텀으로 관리해줄수 있음.