ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 에러 페이지 반환하기 (Feat.ErrorController)
    Spring 2022. 8. 29. 16:25

     

    과제제출형 면접을 진행하면서 예외처리는 모두 GlobalExceptionHandler(ControllerAdvice)에서 처리가 되도록 설정하였다. 

    그러나 웹상에서 잘못된 경로로 입력하여 들어갈 경우에는 가장 기본적인 예외인 whiteLabel 에러 페이지가 나타나고 이에 대해 왜 그런가 하는 질문을 받게 되었으나 답변을 제대로 하지 못하였다 😭. 

     

    이 질문의 의도는 ErrorController의 처리 방법에 대해서 묻는 질문으로 서블릿 컨테이너에서 등록된 서블릿에서 요청처리를 하다가 오류가 발생하였지만 해당 서블릿에서 처리하지 못하고 서블릿 컨테이너까지 오류가 전파되었을 때 서블릿 컨테이너가 오류(ServletException으로 래핑)를 처리하기 위해 특정 경로(server.error.path)로 해당 요청 처리를 위임할때 사용된다. 

    이러한 기본 동작들은 HandlerExceptionResolver에 의해 이루어지고, 별도의 에러 페이지표현이 필요할 때 사용된다. 

     

     

    WAS(여기까지 전파) <- 필터 <- 서블릿 <- 인터셉터 <- 컨트롤러(예외발생)


    Spring MVC 내에서는 @HandlerException을 통해 각 @Controller별로 예외 처리를 하거나, 
    @HandlerException을 @ControllerAdvice에 등록하여 전역적으로 예외를 처리할 수 있었지만, 

    Spring MVC 내에서 처리하지 못한 예외들은 ServletException으로 포장되어 서블릿 컨테이너까지 전파되며

    서블릿 컨테이너는 예외를 처리하기 위해나 경로로 예외 처리를 위임하게 된다. 

     

     

     

     

    먼저 application.properties에서 설정을 추가해준다. 먼저 application.properties에서 설정을 추가해준다. 기본적으로 제공되는 whitelabel 페이지를 사용하지 않겠다는 표시를 추가해준다.
    server.error.include-exception=TRUE
    server.error.include-stacktrace=ALWAYS
    server.error.whitelabel.enabled=FALSE
    server.error.path=/error

     

     

    우선 제대로 반응이 되는지 확인하기 위해서 JSON타입의 반환이 제대로 이루어지는지 확인하고자 했다. 이를 위해 @RestController를 사용했다. 

    @RestController
    public class GlobalErrorController implements ErrorController {
    
        @RequestMapping(value = "/error")
        public ErrorResponse handleError(HttpServletRequest request) {
            Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
            HttpStatus httpStatus = HttpStatus.valueOf(Integer.parseInt(status.toString()));
            int statusCode = Integer.parseInt(status.toString());
            return new ErrorResponse(statusCode, httpStatus.getReasonPhrase());
        }
    }
    @Getter
    @Setter
    public class ErrorResponse {
    
        private int status;
        private String message;
        private LocalDateTime timestamp;
    
        public ErrorResponse(int status, String message) {
            this.message = message;
            this.status = status;
            this.timestamp = LocalDateTime.now();
        }
    
        @Override
        public String toString() {
            return "ErrorResponse{" +
                "status=" + status +
                ", message='" + message + '\'' +
                ", timestamp=" + timestamp +
                '}';
        }
    }

     

     

    잘못된 페이지로의 요청을 보내면 다음과 같이 JSON 타입의 ErrorResponse가 잘 도착하는 것을 확인할 수 있었다. 

     

     

     

    이번엔 올바를 페이지를 반환하도록 설정해보자. thymeleaf를 사용하기 때문에 다음과 같이 설정을 추가해주었다. 

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    spring.thymeleaf.prefix=classpath:/templates/
    spring.thymeleaf.suffix=.html
    spring.thymeleaf.cache=false
    spring.thymeleaf.check-template-location=true

     

     

    그 다음 동적 템플릿을 이용해 예외를 알려줄 것이므로 templates 클래스 안에 각각의 오류 html을 작성해준다. 

     

    기본적인 오류 메세지 표시는 다음과 같았다. 

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml"
          xmlns:th="http://www.thymeleaf.org"
          xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
          layout:decorate="~{layout/default_layout}">
    
    <th:block layout:fragment="head">
      <title>Spring Boot Error PAGE</title>
    </th:block>
    
    <body>
    <h1>This is  Error Page!</h1>
    <div>
      Following Error has occurred. <br><br>
      CODE: <span th:text="${response.getStatus()}"></span><br>
      MESSAGE: <span th:text="${response.getMessage()}"></span><br>
      TIMESTAMP: <span th:text="${response.getTimestamp()}"></span><br>
    </div>
    </body>
    
    </html>

     

     

    다음과 같이 ErrorController를 설정해준다. 

    @Controller
    public class GlobalErrorController implements ErrorController {
    
        private static final String NOT_FOUND_PAGE = "404";
        private static final String INTERNAL_SERVER_ERROR_PAGE = "500";
        private static final String ERROR_PAGE = "error";
    
        @RequestMapping(value = "/error")
        public String handleError(HttpServletRequest request, Model model) {
            Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
            HttpStatus httpStatus = HttpStatus.valueOf(Integer.parseInt(status.toString()));
            ErrorResponse errorResponse = new ErrorResponse(httpStatus.value(), httpStatus.getReasonPhrase());
            model.addAttribute("response", errorResponse);
            if (httpStatus == HttpStatus.NOT_FOUND) {
                return NOT_FOUND_PAGE;
            }
            else  if (httpStatus == HttpStatus.INTERNAL_SERVER_ERROR) {
                return INTERNAL_SERVER_ERROR_PAGE;
            }
            return ERROR_PAGE;
        }
    }

     

     

    다음과 같이 각각의 오류 타입에 따라서 URI나 METHOD가 올바르지 않은 경우에 다음과 같이 예외페이지를 반환한다. 

     

     

    만약 URI나 METHOD는 올바르지만, 다음과 같이 아이디가 존재하지 않아 Custom 예외를 설정하고 @ControllerAdvice에서 설정해준 경우에는 다음과 같이 설정에 따라서 예외정보를 반환한다. 성공! 🙌

     

     

     

Designed by Tistory.