발생하는 예외에 따라서 400, 404 등 상태코드를 다르게 정의하고 싶을 때는 어떻게 해야할까?
- ExceptionResolver를 사용하면 된다.
ExceptionResolver
ExceptionResolver는 말 그대로 컨트롤러(핸들러)에서 발생한 예외를 해결하는 역할을 한다. 앞서 예외를 보면 컨트롤러에서 예외가 발생하면 수 많은 단계를 거쳐 WAS까지 전달되게 되는데, 이 ExceptionResolver를 사용하게 되면 WAS까지 예외가 던져지지 않고 여기서 해결이 가능하다.
- ExceptionResolver를 사용하지 않으면 예외가 WAS까지 전달되어 WAS에서 서버 내부 오류로 인식해 500 상태코드를 전달하게 된다.
- 하지만, ExceptionResolver를 적용하면 예외를 ExceptionResolver가 해결하고 modelAndView를 반환하여 WAS 정상 처리로 인식하고 ExceptionResolver 내에서 정의한 에러 상태코드를 클라이언트에 전달할 수 있다.
HandlerExceptionResolver 만들기
사용자 정의로 ExceptionResolver를 만들려면 HandlerExceptionResolver 인터페이스를 implements하여 메서드를 구현하면 된다.
UserHandlerExceptionResolver - 사용자 정의 예시
package hello.exception.resolver;
import com.fasterxml.jackson.databind.ObjectMapper;
import hello.exception.exception.UserException;
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;
import java.util.HashMap;
import java.util.Map;
@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"); //해당 예외는 400 오류로 처리함을 명시.
String acceptHeader = request.getHeader("accept"); //요청으로부터 헤더 정보를 받아옴.
response.setStatus(HttpServletResponse.SC_BAD_REQUEST); //상태코드를 400으로 변경.
//헤더 정보가 json인 경우
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);//Map(json)객체를 String으로 변환.
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().write(result);
return new ModelAndView();
}else{
//헤더 정보가 json이 아닌 경우 (text/html)
return new ModelAndView("error/500"); //500.html 호출.
}
}
}catch (IOException e){
log.error("resolver ex", e);
}
return null;
}
}
- HTTP 요청 해더의 ACCEPT 값이 application/json 이면 JSON으로 오류를 내려주고, 그 외 경우에는 error/ 500에 있는 HTML 오류 페이지를 보여준다.
webConfig에 만든 ExceptionResolver 등록
우리가 만든 ExceptionResolver를 사용하려면 스프링이 알 수 있도록 Bean을 등록해야 한다. ExceptionResolver는 extendHandlerExceptionResolvers() 메서드를 오버라이딩 하여 추가로 등록하면 된다.
import hello.exception.resolver.UserHandlerExceptionResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
//exceptionResolver 등록
@Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
resolvers.add(new UserHandlerExceptionResolver()); // 만든 exceptionResolver를 추가
}
}
- 오버라이딩 할 때, configureHandlerExceptionResolvers(..) 메서드도 존재하는데 이를 사용하면 스프링이 기본으로 등록하는 기본 ExceptionResolver가 제거되므로 주의해야 한다.
ExceptionResolver 응답 반환
ExceptionResolver는 크게 3가지로 예외에 대한 처리를 반환할 수 있다.
- 상태코드 변환 (400, 402, ...)
- 상태코드 변환은 response.setStatus()를 통해 상태코드를 변환하고, 아무것도 없는 빈 ModelAndView를 반환한다.
- 뷰 템플릿 처리
- ModelAndView에 사용자에게 보여주고 싶은 에러페이지 (.html) 의 View이름을 담아 반환한다.
- 예) 위의 코드(UserHandlerExceptionResolver )에서 헤더 정보가 json이 아닌 경우 확인.
- API 응답 처리
- HTTP 응답 바디에 직접 데이터를 넣어주는 것도 가능하다. 여기에 JSON 으로 응답하면 API 응답 처리를 할 수 있다.
- 예) 위의 코드(UserHandlerExceptionResolver )에서 헤더 정보가 json인 경우 확인.
이처럼 ExceptionResolver를 활용하면 컨트롤러의 예외를 복잡하게 WAS까지 전달된 후 다시 컨트롤러로 내려와 처리되는 과정 없이 ExceptionResolver 내에서 해결한 후 그 응답을 바로 클라이언트에 전달할 수 있다.
'Spring > SpringMVC' 카테고리의 다른 글
클라이언트가 보낸 데이터를 확인하는 방법 (filter 적용하기) (0) | 2024.04.23 |
---|---|
ExceptionResolver (0) | 2024.03.18 |
API 예외 처리 (1) | 2024.02.27 |
스프링 부트 - 오류페이지 처리 (0) | 2024.02.19 |
서블릿 예외처리 (0) | 2024.02.15 |