Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- Thymeleaf
- pointcut
- 알고리즘
- 스프링
- 자바
- Proxy
- Spring Boot
- jpa
- AOP
- 김영한
- springdatajpa
- JPQL
- Exception
- 스프링 핵심 원리
- Greedy
- db
- kotlin
- Servlet
- 스프링 핵심 기능
- 그리디
- Android
- 백준
- QueryDSL
- http
- transaction
- spring
- java
- 인프런
- SpringBoot
- JDBC
Archives
- Today
- Total
개발자되기 프로젝트
[API예외] @ExceptionHandler 본문
1. HTML 화면 오류 & API 오류
- 웹 브라우저에 HTML 화면을 제공할 때는 오류가 발생하면 BasicErrorController 사용이 편함
- 이때는 단순히 5xx, 4xx 관련된 오류 화면을 보여주면 된다.
- BasicErrorController 는 이런 메커니즘을 모두 구현해둠.
- 그런데 API는 각 시스템 마다 응답의 모양도 다르고, 스펙도 모두 다르다.
- 예외 상황에 단순히 오류 화면을 보여주는 것이 아니라,
- 예외에 따라서 각각 다른 데이터를 출력해야 할 수도 있다.
- 그리고 같은 예외라고해도 어떤 컨트롤러에서 발생했는가에 따라서 다른 예외 응답을 내려주어야 할 수 있다.
- 즉 세밀한 제어가 필요하다.
2. API예외 처리의 어려운 점.
- HandlerExceptionResolver 를 떠올려 보면 ModelAndView 를 반환해야 했다.
- 이것은 API 응답에는 필요하지 않다.
- API 응답을 위해서 HttpServletResponse 에 직접 응답 데이터를 넣어주었다.
- 특정 컨트롤러에서만 발생하는 예외를 별도로 처리하기 어렵다.
- 예를 들어서 회원을 처리하는 컨트롤러에서 발생하는 RuntimeException 예외와
- 상품을 관리하는 컨트롤러에서 발생하는 동일한 RuntimeException 예외를 서로 다른 방식으로 처리하고 싶다??
3. @ExceptionHandler
- 스프링은 API 예외 처리 문제를 해결하기 위해 @ExceptionHandler 라는 애노테이션을 사용
- 이것이 바로 ExceptionHandlerExceptionResolver 이다.
- 스프링은 ExceptionHandlerExceptionResolver 를 기본으로 제공하고,
- 기본으로 제공하는 ExceptionResolver 중에 우선순위도 가장 높다.
- 실무에서 API 예외 처리는 대부분 이 기능을 사용
4. ApiExceptionV2Controller
- @ExceptionHandler(Exception)
- return으로 원하는 객체 반환 가능.
- 해당 객체는 JSON으로 변환됨.
@RestController
@Slf4j
public class ApiExceptionV2Controller {
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandler(IllegalArgumentException e){
log.error("[exceptionHandler] ex", e);
return new ErrorResult("BAD", e.getMessage());
}
@GetMapping("/api2/members/{id}")
public MemberDto getMember(@PathVariable("id") String id){
if(id.equals("ex")){
throw new RuntimeException("잘못된 사용자");
}
if(id.equals("bad")){
throw new IllegalArgumentException("잘못된 입력값");
}
if(id.equals("user-ex")){
throw new UserException("사용자 오류");
}
return new MemberDto(id, "hello"+id);
}
@Data
@AllArgsConstructor
static class MemberDto{
private String memberId;
private String name;
}
}
- ErrorResult
@Data
@AllArgsConstructor
public class ErrorResult {
private String code;
private String message;
}
- 처리 흐름
- 예외 발생
- DispatcherServlet에서 ExceptionResolver 호출
- 이 때 ExceptionHandlerExceptionResolver가 호출됨.
- 해당 ExceptionResolver는 예외가 발생한 Handler 내부에서 @ExceptionHandler가 붙은 메서드를 찾음.
- 해당 메서드가 처리할 수 있는 exception이 맞으면 해당 메서드 실행.
- @RestController이니까 @ResponseBody도 적용됨,HttpMessageConverter 실행됨.
- 그래서 예외가 발생했지만 정상 흐름으로 바꿔서 return을 한다.
- 어???정상?? HttpStatus가 200으로 나간다.. ㅋㅋㅋ
- 어.. 상태코드 바꾸고 싶음..
5. 상태 코드 변경
- 예외를 처리할 메서드에 @ResponseStatus를 붙이고, 상태코드를 지정하면됨 ㅋㅋㅋ
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandler(IllegalArgumentException e){
log.error("[exceptionHandler] ex", e);
return new ErrorResult("BAD", e.getMessage());
}
- 이렇게 되면 ExceptionHandlerExceptionResolver에서 모든 예외 처리가 끝난다.
- 해당 ExceptionHandler가 호출한 예외 처리 메서드에서
- json반환, 상태코드 지정, 정상흐름으로 끝! 그냥 정상임. 지금부턴 예외 상황 아님.
- 예외가 터진 상황이 아니기 때문에 DiapatcherServlet에서 예외 처리를 위한 별도 실행이 필요 없음.
6. Test2 : ResponseEntity
- ResponseEntity<>도 return 가능
- HttpEntity
- 메시지 바디 정보 직접 반환 -> new HttpEntity<>(body)
- 헤더 정보 포함 가능
- view를 조회하지 않음 -> Http 응답 메시지 body에 데이터 넣어서 보내버림.
@ExceptionHandler(UserException.class)
public ResponseEntity<ErrorResult> UserExHandler(UserException e){
log.error("[exceptionHandler] ex", e);
ErrorResult errorResult = new ErrorResult("USER-EX", e.getMessage());
return new ResponseEntity<>(errorResult, HttpStatus.BAD_REQUEST);
}
7. Test3 : Exception
- @ExceptionHandler를 통해 최상위 예외인 Exception을 처리해 보자.
- 다른 method에서 처리하지 못하는 Exception을 받을 수 있음.
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler
public ErrorResult exHandler(Exception e){
log.error("[exceptionHandler] ex", e);
return new ErrorResult("EX","내무오류");
}
8. 정리
- @ExceptionHandler는 해당 Controller에 내부에만 영향이 있음.
- 참고로 지정한 예외 또는 그 예외의 자식 클래스는 모두 잡을 수 있음.
- 스프링의 우선순위는 항상 자세한 것이 우선권을 가진다.
- 예를 들어서 부모, 자식 클래스가 있고 다음과 같이 예외가 처리된다.
@ExceptionHandler(부모예외.class)
public String 부모예외처리()(부모예외 e) {}
@ExceptionHandler(자식예외.class)
public String 자식예외처리()(자식예외 e) {}
- 다양한 예외를 한번에 처리 가능
@ExceptionHandler({AException.class, BException.class})
public String ex(Exception e) {
log.info("exception e", e);
}
- @ExceptionHandler 에 예외를 생략할 수 있다. 생략하면 메서드 파라미터의 예외가 지정됨.
@ExceptionHandler
public ResponseEntity<ErrorResult> userExHandle(UserException e) {}
- 파리미터와 응답
- @ExceptionHandler 에는 마치 스프링의 컨트롤러의 파라미터 응답처럼
- 다양한 파라미터와 응답을 지정할 수 있음.
9. HTML 오류 화면
- ModelAndView를 반환하면 view return 가능 ㅋㅋ
@ExceptionHandler(ViewException.class)
public ModelAndView ex(ViewException e) {
log.info("exception e", e);
return new ModelAndView("error");
}
10. GitHub : 211001 @ExceptionHandler
'인프런 > [인프런] 스프링 MVC 2' 카테고리의 다른 글
[TypeConverter] Spring Type Converter (0) | 2021.10.01 |
---|---|
[API예외] @ControllerAdvice (0) | 2021.10.01 |
[API예외] Spring제공 ExceptionResolver2 (0) | 2021.10.01 |
[API예외] Spring제공 ExceptionResolver (0) | 2021.09.29 |
[API예외] HandlerExceptionResolver 활용 (0) | 2021.09.28 |
Comments