일반적인 예외처리
- Spring에서는 발생한 Exception을 기반으로 오류를 처리할 수 있도록
@ExceptionHandler
를 제공
- 해당 애너테이션에 선언된 예외 및 하위 예외에 대해서 특정 메서드가 처리할 수 있도록 함
-> 보통의 핸들러와 마찬가지로 @ResponseStatus
를 통해 응답 코드 정의가능
-> ModelAndView
, String
을 반환하여 view를 resolve할 수 있고, ResponseEntity<T>
반환도 가능
@RestController
@RequestMapping("/boards")
public class BoardController {
@GetMapping("/{id}")
public Board get(@PathVariable Long id) {
if (id < 1L) {
throw new BoardNotFoundException("invalid id: " + id);
}
return new Board("title", "content");
}
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(BoardNotFoundException.class)
public Map<String, String> handle(BoardNotFoundException e) {
log.error(e.getMessage(), e);
Map<String, String> errorAttributes = new HashMap<>();
errorAttributes.put("code", "BOARD_NOT_FOUND");
errorAttributes.put("message", e.getMessage());
return errorAttribute;
}
}
글로벌 예외처리
왜 해야하는가,
handleLineException
메소드를 통해 컨트롤러, 서비스 내에서 발생하는 예외를 처리할 수 있음
-> 컨트롤러가 늘어나면 중복 코드가 늘어나고, 유지보수 또한 어려워짐
-> 전역 예외처리를 통해 해결 가능
ControllerAdvice, RestControllerAdvice 사용으로 예외 처리
- 전역에서 예외처리를 하기 위해
@ControllerAdvice
또는 @RestControllerAdvice
사용
- AOP(Aspect Oriented Programming)의 방식이며 Application 전역에 발생하는 모든 서비스의 예외를 한 곳에서 관리할 수 있게 해줌
- 컨트롤러에서 발생하는 모든 예외는
@RestControllerAdvice
가 잡고, @ExceptionHandler
가 개별적으로 예외를 잡아 처리하는 방식
@RestControllerAdvice
@ControllerAdvice
public class ExceptionController {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ExceptionResponse> clientBadRequest(final IllegalArgumentException ex){
return new ResponseEntity<>(new ExceptionResponse(ex.getMessage()), HttpStatus.BAD_REQUEST);
}
}
모든 예외를 하나의 Enum 클래스로
- 동일한 구조의 응답 포맷을 미리 설정하기 위해 enum 클래스로 관리
@AllArgsConstructor
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum ExceptionStatus {
USER_NOT_FOUND("회원을 찾을 수 없습니다."),
EVENT_BOOKMARK_NOT_FOUND("이벤트 북마크를 찾을 수 없습니다."),
EVENT_NOT_FOUND("이벤트를 찾을 수 없습니다."),
POST_NOT_FOUND("게시글을 찾을 수 없습니다."),
COMMENT_NOT_FOUND("댓글을 찾을 수 없습니다."),
CAFE_NOT_FOUND("카페를 찾을 수 없습니다.");
private final String message;
public String getStatus(){
return name();
}
public String getMessage(){
return message;
}
}
ExceptionResponse
- 예외 응답을 처리할 Response 클래스
ResponseEntity<ExceptionResponse>
예외가 발생했을 때 ExceptionResponse 형식으로 예외 정보를 Response로 내려줌
@AllArgsConstructor
@Getter
public class ExceptionResponse {
private String message;
}
활용 예시
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
ErrorCode code = ex.getErrorCode();
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse(code.getCode(), code.getMessage(), null));
}
}
- @RestControllerAdvice : 모든 컨트롤러에 적용됨
- 예외를 일일이 try/catch하지 않고도 처리 가능함
- @ExceptionHandler : 특정 예외 타입에 대한 처리 지정 가능
- BusinessException : 개발자가 의도적으로 던지는 사용자 정의 예외
- 에러 코드를 반환하고 HTTP Body에 JSON 형태로 담음
기타 궁금점
@ControllerAdvice
, @RestControllerAdvice
의 차이는 뭘까
RestController = Controller + ResponseBody
RestControllerAdvice = ControllerAdvice + ResponseBody
-> 위 같은 구조이기때문에 controller의 예외도 RestControllerAdvice가 잡아줄 수 있음
@RestControllerAdvice
는 @RestController
와 마찬가지로 @ResponseBody
가 있어서 자바 객체를 Json/Xml 형태로 반환하여 HTTP Response Body에 담을 수 있다는 차이가 있음
Spring 전역에서 예외처리