인프런 커뮤니티 질문&답변

박준규님의 프로필 이미지
박준규

작성한 질문수

스프링 MVC 2편 - 백엔드 웹 개발 활용 기술

서블릿 예외 처리 - 오류 페이지 작동 원리

request.attribute 에 null 만 나옵니다

작성

·

1.6K

0

package hello.exception.servlet;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Slf4j
@Controller
public class ErrorPageController {
    //RequestDispatcher 상수로 정의되어 있음
    public static final String ERROR_EXCEPTION = "jakarta.servlet.error.exception";
    public static final String ERROR_EXCEPTION_TYPE = "jakarta.servlet.error.exception_type";
    public static final String ERROR_MESSAGE = "jakarta.servlet.error.message";
    public static final String ERROR_REQUEST_URI = "jakarta.servlet.error.request_uri";
    public static final String ERROR_SERVLET_NAME = "jakarta.servlet.error.servlet_name";
    public static final String ERROR_STATUS_CODE = "jakarta.servlet.error.status_code";
    
    @RequestMapping("/error-page/404")
    public String errorPage404(HttpServletRequest request, HttpServletResponse
            response) {
        log.info("errorPage 404");
        printErrorInfo(request);
        return "error-page/404";
    }
    @RequestMapping("/error-page/500")
    public String errorPage500(HttpServletRequest request, HttpServletResponse response) {
        log.info("errorPage 500");
        printErrorInfo(request);
        return "error-page/500";
    }
    private void printErrorInfo(HttpServletRequest request) {
        log.info("ERROR_EXCEPTION: ex=", request.getAttribute(ERROR_EXCEPTION));
        log.info("ERROR_EXCEPTION_TYPE: {}", request.getAttribute(ERROR_EXCEPTION_TYPE));
        log.info("ERROR_MESSAGE: {}", request.getAttribute(ERROR_MESSAGE)); 
        // ex의 경우 NestedServletException 스프링이 한번 감싸서 반환
        log.info("ERROR_REQUEST_URI: {}", request.getAttribute(ERROR_REQUEST_URI));
        log.info("ERROR_SERVLET_NAME: {}", request.getAttribute(ERROR_SERVLET_NAME));
        log.info("ERROR_STATUS_CODE: {}", request.getAttribute(ERROR_STATUS_CODE));
        log.info("dispatchType={}", request.getDispatcherType());
    }
}
2023-08-13T23:49:48.327+09:00  INFO 11300 --- [nio-8080-exec-6] h.exception.servlet.ErrorPageController  : errorPage 500
2023-08-13T23:49:48.327+09:00  INFO 11300 --- [nio-8080-exec-6] h.exception.servlet.ErrorPageController  : ERROR_EXCEPTION: ex=
2023-08-13T23:49:48.327+09:00  INFO 11300 --- [nio-8080-exec-6] h.exception.servlet.ErrorPageController  : ERROR_EXCEPTION_TYPE: null
2023-08-13T23:49:48.328+09:00  INFO 11300 --- [nio-8080-exec-6] h.exception.servlet.ErrorPageController  : ERROR_MESSAGE: null
2023-08-13T23:49:48.328+09:00  INFO 11300 --- [nio-8080-exec-6] h.exception.servlet.ErrorPageController  : ERROR_REQUEST_URI: null
2023-08-13T23:49:48.328+09:00  INFO 11300 --- [nio-8080-exec-6] h.exception.servlet.ErrorPageController  : ERROR_SERVLET_NAME: null
2023-08-13T23:49:48.328+09:00  INFO 11300 --- [nio-8080-exec-6] h.exception.servlet.ErrorPageController  : ERROR_STATUS_CODE: null
2023-08-13T23:49:48.328+09:00  INFO 11300 --- [nio-8080-exec-6] h.exception.servlet.ErrorPageController  : dispatchType=REQUEST
// WebServerCustomizer

package hello.exception;

import org.springframework.boot.web.server.ConfigurableWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

@Component
public class WebServerCustomizer implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {

    @Override
    public void customize(ConfigurableWebServerFactory factory) {

        ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error-page/404");
        ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error-page/500");
        ErrorPage errorPageEx = new ErrorPage(RuntimeException.class, "/error-page/500");

        factory.addErrorPages(errorPage404, errorPage500, errorPageEx);
    }
}
// ServerExController

package hello.exception.servlet;

import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import java.io.IOException;

@Slf4j
@Controller
public class ServletExContoller {

    // Exception (예외)
    @GetMapping("/error-ex")
    public void errorEx() {
        throw new RuntimeException("예외 발생!");
        // HTTP Status 500 – Internal Server Error 발생
    }

    // response.sendError(HTTP 상태 코드, 오류 메시지)
    @GetMapping("/error-404")
    public void error404(HttpServletResponse response) throws IOException {
        response.sendError(404, "404 오류!");
    }

    @GetMapping("/error-500")
    public void error500(HttpServletResponse response) throws IOException {
        response.sendError(500);
    }
}

스프링부트 3.1.2 버젼으로 생성해서 String 상수의 "javax"를 "jakarta"로 변경한 것 외엔 강의에서 사용된 코드와 동일하게 작성하였습니다.

그런데 /error-ex 로 접속할땐 상수값에 담긴 정보가 정상적으로 나오지만 /error-page/500, /error-page/404 로 접속하면 상수에 담긴 정보가 모두 null 로 나옵니다.

/error-page/500, /error-page/404 로 접속할땐 request.getAttribute() 에 아예 값이 안들어가는 것 같은데 무엇이 문제일까요?

답변 4

0

혹시 이 에러 해결되셨나요? 준규님과 증상이 완전히 동일한데요..

박준규님의 프로필 이미지
박준규
질문자

안녕하세요.

내용을 다시 살펴보고 그때 어떻게 해결했는지 생각해보았습니다.

결론적으로 여러가지 방법을 시도했었지만 null이 출력되는것은 해결하지 못하고 포기했습니다.

 

  1. 먼저 저는 RequestDispatcher의 상수를 직접 static import하는 형태로 가져왔지만 결과는 똑같이 null이 나왔습니다.

ex) ERROR_EXCEPTION -> RequestDispatcher.ERROR_EXCEPTION
  1. request.getAttribute 자체가 동작하지 않는 것인지 확인하기 위해 테스트해보았지만, request는 정상적으로 작동했습니다.

@RequestMapping("/error-page/500")
    public String errorPage500(HttpServletRequest request, HttpServletResponse response) {
        log.info("errorPage 500");

        // request에 새로운 속성 "testId" 와 그 값 112 추가
        request.setAttribute("testId", 112);

        printErrorInfo(request);
        return "error-page/500";
    }

//////////////////////

private void printErrorInfo(HttpServletRequest request) {
        log.info("ERROR_EXCEPTION: ex= {}", request.getAttribute(ERROR_EXCEPTION));
        log.info("ERROR_EXCEPTION_TYPE: {}", request.getAttribute(ERROR_EXCEPTION_TYPE));
        log.info("ERROR_MESSAGE: {}", request.getAttribute(ERROR_MESSAGE));
        log.info("ERROR_REQUEST_URI: {}", request.getAttribute(ERROR_REQUEST_URI));
        log.info("ERROR_SERVLET_NAME: {}", request.getAttribute(ERROR_SERVLET_NAME));
        log.info("ERROR_STATUS_CODE: {}", request.getAttribute(ERROR_STATUS_CODE));
        log.info("dispatchType={}", request.getDispatcherType());

        // "testId" 속성값 로그 출력 -> 결과 112 (정상 출력)
        log.info("test = {}", request.getAttribute("testId"));
    }
  1. 이로인해 저는 RequestDispatcher의 상수값이 비어있다고 생각했고 프로젝트의 외부 라이브러리(external libraries)에서 RequestDispatcher의 상수들에 입력된 경로가 존재하는지 찾아보았습니다. 그러나 해당 라이브러리 속 jakarta.servlet 패키지 내부에 error 패키지를 찾을 수 없었고, DispatcherType만 찾을수 있었습니다.

String ERROR_EXCEPTION = "jakarta.servlet.error.exception";
String ERROR_EXCEPTION_TYPE = "jakarta.servlet.error.exception_type";
String ERROR_MESSAGE = "jakarta.servlet.error.message";
String ERROR_REQUEST_URI = "jakarta.servlet.error.request_uri";
String ERROR_SERVLET_NAME = "jakarta.servlet.error.servlet_name";
String ERROR_STATUS_CODE = "jakarta.servlet.error.status_code";

image

이로인해 그 당시 애초에 톰캣10 라이브러리 속 jakarta.servlet.error 경로에 무언가가 존재하지않아서 null 값이 뜨겠거니 하고 넘어갔습니다. 혹시 나중에라도 해결방법이 존재하거나 해결하셨다면 언제든 말씀주시면 감사하겠습니다.

안녕하세요.

저도 비슷한 문제로 스프링 부트 버전이 3.2.4 로 강좌보다 상위 버전이라 문제가 발생한 듯 합니다.

String 문자열의 javax -> jakarta 로 변경해서 해 보세요.

 

그리고 response.sendError로 던진 에러에서는 ERROR_EXCEPTION 과 ERROR_EXCEPTION_TYPE 값은 null 이 나오는게 맞다고 강의에서 말씀하고 있네요.

 

해결이 되셨길 바랍니다.

0

저도 같은 상황인데 어떻게 해결하셨나요?

printErrorInfo 메소드를 호출하고 있는데 null로 찍히고 있고 error-ex 호출시에만 값이 찍혀요

0

RequestDispatcher의 상수들을 static import 해서 사용해 보세요

 

0

안녕하세요, 인프런 AI 인턴이에요.

request.attribute가 null만 나온다는 문제인데요. 코드를 살펴보니 printErrorInfo 메소드가 호출되지 않아서 그런 것 같습니다. printErrorInfo 메소드에서 request의 attribute 값을 확인하고 출력하도록 구현되어 있을텐데, 호출되지 않으면 attribute 값이 null로 나올 수 있습니다.

printErrorInfo 메소드를 호출하는 부분을 확인해보세요. 예를 들어, errorPage404 메소드 내에서 printErrorInfo(request) 코드를 잘 추가하고 있는지, 혹은 다른 곳에서 이 메소드를 호출하도록 설정되어 있는지 등을 확인해보세요. 이 부분을 확인하고 수정하면 attribute 값이 정상적으로 출력될 것입니다.

추가적으로, request.attribute를 사용하기 전에 해당 attribute가 null인지 먼저 체크하는 것도 좋은 방법입니다. null 체크를 통해 NullPointerException 오류를 방지할 수 있어요.

혹시 원인을 찾지 못하거나 더 많은 도움이 필요하시면, 관련 코드의 전체 내용과 함께 더 자세한 정보를 알려주세요. 가능한 빠르게 도움을 드리도록 하겠습니다. 감사합니다.

박준규님의 프로필 이미지
박준규
질문자

감사합니다!

박준규님의 프로필 이미지
박준규

작성한 질문수

질문하기