API 예외 처리 - 스프링 부트 기본 오류 처리
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {}
- errorHtml(): produces = MediaType.TEXT_HTMl_VALUE: 클라이언트 요청의 Accept 헤더 값이 text/html인 경우에는 errorHtml()을 호출해서 view를 제공한다.
- error(): 그 외 경우에 호출되고 ResponseEntity로 HTTP Body에 JSON 데이터를 반환한다.
API 예외 처리 - HandlerExceptionResolver
스프링 MVC는 컨트롤러(핸들러) 밖으로 예외가 던져진 경우 예외를 해결하고, 동작을 새로 정의할 수 있는 방법을 제공한다. 컨트롤러 밖으로 던져진 예외를 해결하고, 동작 방식을 변경하고 싶으면 HandlerExceptionResolver를 사용하면 된다.
HandlerExceptionResolver 인터페이스
public interface HandlerExceptionResolver {
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex);
}
MyHandlerExceptionResolver
package hello.exception.resolver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
try {
if (ex instanceof IllegalArgumentException) {
log.info("IllegalArgumentException resolver to 400");
response.sendError(HttpServletResponse.SC_BAD_REQUEST,
ex.getMessage());
return new ModelAndView();
}
} catch (IOException e) {
log.error("resolver ex", e);
}
return null;
}
}
ExceptionResolver 활용
예외 상태 코드 변환
예외를 response.sendError(x x x) 호출로 변경해서 서블릿에서 상태 코드에 따른 오류를 처리하도록 위함
이후 WAS는 서블릿 오류 페이지를 찾아서 내부 호출, 예를 들어 스프링 부트가 기본으로 설정한 /error가 호출됨
뷰 템플릿 처리
ModelAndView에 값을 채워서 예외에 따른 새로운 오류 화면을 뷰 렌더링해서 고객에게 제공
API 응답 처리
response.getWriter().println("hello"); 처럼 HTTP 응답 바디에 직접 데이터를 넣어주는 것도 가능하고 JSON으로 응답하면 API 응 답 처리를 할 수 있다.
HandlerExceptionResolver 활용
예외가 발생하면 WAS까지 예외가 던져지고, WAS에서 오류 페이지 정보를 찾아서 다시 /error를 호출하는 과정은 생각해보면 너무 복잡하다. ExceptionResolver를 활용하면 예외가 발생했을 때 이런 복잡한 과정 없이 여기에서 문제를 해결할 수 있다.
API 예외 처리 - 스프링이 제공하는 ExceptionResolver
스프링 부트가 기본으로 제공하는 ExceptionReslover
1. ExceptionHandlerExceptionResolver
@ExceptionHandler를 처리한다. API 예외 처리는 대부분 이 기능으로 해결한다.
2. ResponseStatusExceptionResolver
HTTP 상태 코드를 지정해준다.
3. DefaultHandlerExceptionResolver
스프링 내부 기본 예외를 처리한다.
API 예외 처리 - @ExceptionHandler
API 예외처리의 어려운 점
- HandlerExceptionResolver를 떠올려 보면 ModelAndView를 반환해야 했다. 이것은 API 응답에 필요하지 않다.
- API 응답을 위해 HttpServletResponse에 직접 응답 데이터를 넣어주었다. 이것은 과거 서블릿을 사용하던 시절로 돌아간 것 같다.
@ExceptionHandler 예외 처리 방법
@ExceptionHandler 애노테이션을 선언하고, 해당 컨트롤러에서 처리하고 싶은 예외를 지정해주면 된다. 해당 컨트롤러에서 예외가 발생하면 이 메서드가 호출된다. 참고로 지정한 예외 또는 그 예외의 자식 클래스는 모두 잡을 수 있다.
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalExHandle(IllegalArgumentException e) {
log.error("[exceptionHandle] ex", e);
return new ErrorResult("BAD", e.getMessage());
}
다양한 예외 - 다양한 예외들을 한번에 처리할 수 있다.
@ExceptionHandler({AException.class, BException.class})
public String ex(Exception e) {
log.info("exception e", e);
}
예외 생략 - 생략하면 메서드 파라미터의 예외가 지정된다.
@ExceptionHandler
public ResponseEntity<ErrorResult> userExHandle(UserException e) {}
API 예외 처리 - @ControllerAdvice
package hello.exception.exhandler;
import hello.exception.exception.UserException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
public class ApiExceptionV2Controller {
@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;
}
}
@ControllerAdvice
- @ControllerAdvice는 대상으로 지정한 여러 컨트롤러에 @ExceptionHandler, @InitBinder 기능을 부여해주는 역할을 한다.
- @ControllerAdvice에 대상을 지정하지 않으면 모든 컨트롤러에 적용된다. (글로벌 적용)
- @RestControllerAdvice는 @ControllerAdvice와 같고, @ResponseBody가 추가되어 있다.
'Spring Study > MVC 패턴' 카테고리의 다른 글
[MVC 패턴] 예외 처리와 오류 페이지 (0) | 2023.09.01 |
---|---|
[MVC 패턴] 로그인 처리2 - 필터, 인터셉트 (0) | 2023.08.31 |
[MVC 패턴] 로그인 처리1 - 쿠키, 세션 (0) | 2023.08.29 |
[MVC 패턴] 검증2 - Bean Validation (0) | 2023.08.24 |
[MVC 패턴] 검증1 - Validation (0) | 2023.08.24 |
댓글