≣ 목차
HandlerExceptionResolver 기본 개념
예외 처리를 하지 못해서 예외가 컨트롤러 밖으로 전달된 경우 이 예외를 해결하고, 동작을 새로 정의할 수 있는 방법을 제공한다. 즉 컨트롤러 밖으로 던져진 예외를 해결하고, 동작 방식을 변경하고 싶으면 HandlerExceptionResolver를 사용하면 됩니다.
ExceptionResolver 적용 후
위에 사진을 보면 컨트롤러에 가는 중 예외가 발생한다. 예외 발생 시 HandlerExceptionResolver에게 물어봅니다. 이 오류 해결할 수 있어? 어 나 해결할 수 있어 그러면 HandlerExceptionResolver를 구현한 클래스에서 sendError()메소드를 사용하면 서버는 sendError에 있는 HTTP 상태 코드를 반환하게 됩니다. 그리고 ModelAndView를 반환하는 이유는 예외를 처리해서 정상 흐름처럼 변경하기 위해 ModelAndView를 반환합니다.
HandlerExceptionResolver 구현체 예시
@Slf4j
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
try {
if (ex instanceof IllegalAccessException) {
log.info("IllegalAccessException to 400");
response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
return new ModelAndView();//
}
} catch (IOException e) {
log.error("resolver ex", e);
}
return null;
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override // MyHandlerExceptionResolver 등록
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
resolvers.add(new MyHandlerExceptionResolver());
}
}
❗주의할 점❗
WebMvcConfigurer인터페이스에 configureHandlerExceptionResolvers()메소드를 사용하면 스프링이 기본으로 등록하는 ExceptionResolver이 제거가 됩니다.
HandlerExceptionResolver 반환 값에 따른 비교
- 빈 ModelAndView, ModelAndView 지정, null 값 반환 총 3가지로 분류할 수 있습니다.
- 빈 ModelAndView: 뷰를 렌더링 하지 않고, 정상 흐름으로 서블릿이 리턴된다.
- ModelAndView 지정: ModelAndView에 View , Model 등의 정보를 지정해서 반환하면 뷰를 렌더링 한다. - 오류 페이지로 사용할 수 있다
- null:다른 ExceptionResolver 구현체를 찾아서 실행한다. 만약 처리할 수 있는 ExceptionResolver가 없으면 예외 처리가 안되고, 기존에 발생한 예외를 서블릿 밖으로 던집니다.
ExceptionResolver를 사용하면 컨트롤러에서 예외가 발생해도 예외를 처리하기 위해 만든 xxxxxExceptionResolver에서 예외를 처리해버립니다. 이 과정을 단축할 수 있는 방법에 대해서 알려드리겠습니다.!!!
HandlerExceptionResolver 활용
1. xxxxException를 상속 받는 구현체 예시
public class UserException extends RuntimeException {
public UserException() {
super();
}
public UserException(String message) {
super(message);
}
public UserException(String message, Throwable cause) {
super(message, cause);
}
public UserException(Throwable cause) {
super(cause);
}
protected UserException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
2. 예외를 처리하는 UserHandlerExceptionResolver 작성
@Slf4j
public class UserHandlerExceptionResolver implements HandlerExceptionResolver {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
try {
if (ex instanceof UserException) {
log.info("UserException resolver to 400");
String acceptHeader = request.getHeader("accept");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
if ("application/json".equals(acceptHeader)) {
Map<String, Object> errorResult = new HashMap<>();
errorResult.put("ex", ex.getClass());
errorResult.put("message", ex.getMessage());
String result = objectMapper.writeValueAsString(errorResult);
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().write(result);
return new ModelAndView();
} else {
//TEXT/HTML
return new ModelAndView("error/400");
}
}
} catch (IOException e) {
log.error("resolver ex", e);
}
return null;
}
}
해당 코드가 핵심 로직인데 UserException라는 예외가 오면 if문 아래에서 예외를 처리합니다.
3. UserHandlerExceptionResolver을 등록
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
resolvers.add(new MyHandlerExceptionResolver());
resolvers.add(new UserHandlerExceptionResolver());
}
}
4. 컨트롤러에서 xxxxException이 발생하는 경우에 xxxxException로 예외 처리
@Slf4j
@RestController
public class ApiExceptionController {
@Data
@AllArgsConstructor
static class MemberDTO {
private String MemberId;
private String name;
}
@GetMapping("/api/members/{id}")
public MemberDTO getMember(@PathVariable("id") String id) {
if (id.equals("ex")) {
throw new RuntimeException("잘못된 사용자");
} (id.equals("user-ex")) {
throw new UserException("사용자 오류");
}
return new MemberDTO(id, "hello" + id);
}
}
이렇게 로직을 구성하면 컨트롤러에서 예외가 발생해도 예외를 처리하기 위해 만든 xxxxxExceptionResolver에서 예외를 처리해버린다. 따라서 예외가 발생해도 서블릿 컨테이너까지 예외가 전달되지 않고, 컨트롤러에서 예외 처리를 처리하는 효과를 볼 수 있습니다.(정확히는 스프링 MVC에서 처리함) 결과적으로 WAS 입장에서는 정상 처리 및 예외가 발생한 지 모른다는 것이 핵심입니다.
'Spring' 카테고리의 다른 글
[Spring+Thymeleaf] Spring Converter 및 Formatter 정리 (0) | 2024.07.11 |
---|---|
[Spring] 스프링이 제공하는 ExceptionResolver에 대하여 (0) | 2024.07.08 |
[OAuth2.0 + JWT + Spring] 스프링 시큐리티로 OAuth2.0 로그인 구현(카카오) (0) | 2024.07.01 |
[spring] 의존관계 자동 주입 - 생성자 주입을 사용해야 되는 이유 (1) | 2024.03.24 |
[Spring] Spring 기본 개념 - 스프링 컨테이너 (1) | 2024.03.24 |