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

오스즈님의 프로필 이미지

작성한 질문수

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

스프링 부트 - 오류 페이지1

BasicErrorController 질문있습니다.

해결된 질문

작성

·

205

0

학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.

1. 강의 내용과 관련된 질문을 남겨주세요.
2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.
(자주 하는 질문 링크: https://bit.ly/3fX6ygx)
3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.
(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)

질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.
=========================================
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예/아니오) 예
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예

[질문 내용]

BasicErrorController 질문있습니다.

저희가 직접만든 ErrorPageController 의 경우 오류 코드에 따라 맵핑되는 uri가 달라 각각 코드에 따라 다른 viewName을 리턴하여 각각 다른 화면을 보여주는 것이 가능해 보입니다.

@Slf4j
@Controller
public class ErrorPageController {

    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("error-page404");
        printErr(request);
        return "error-page/404";
    }

    @RequestMapping(value = "error-page/500", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Map<String, Object>> errorPage500api(
            HttpServletRequest request, HttpServletResponse response) {
        log.info("API errorPage500");
        Map<String, Object> result = new HashMap<>();
        Exception ex = (Exception) request.getAttribute(ERROR_EXCEPTION);
        result.put("status", request.getAttribute(ERROR_STATUS_CODE));
        result.put("message", ex.getMessage());

        Integer statusCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);

        return new ResponseEntity<>(result, HttpStatus.valueOf(statusCode));
    }

    @RequestMapping("/error-page/500")
    public String errorPage500(HttpServletRequest request, HttpServletResponse response) {

        log.info("error-page500");
        printErr(request);
        return "error-page/500";
    }

    public void printErr(HttpServletRequest request) {
        log.info("ERROR_EXCEPTION : {}", 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("dispatcherType = {}", request.getDispatcherType());
    }
}

 

하지만 BasicErrorController의 어떤 errorCode가 오더라도 전부 /error로 매핑이 되어 반환값도 viewName이 아니라 ModelAndView이어 이해해 약간의 어려움을 겪고 있습니다.

일단 제가 추상적으로 생각했을때 BasicErrorController의 errorHtml에서 반환받는 ModelAndView를 Servlet에서 받아 ModelAndView에 있는 "error"라고 하는 viewName을 directory 이름 으로 사용하고 model에 들어있는 status를 조합하여

RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath+"/"+model.getAttribute("status");

이런느낌의 로직으로 처리한다 라고 이해하면 제대로 이해하고 있는게 맞을까요???

추상적으로 이런 느낌이겠지정도고 확신이 들지 않아 질문합니다.

 

@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
    private final ErrorProperties errorProperties;

    public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
        this(errorAttributes, errorProperties, Collections.emptyList());
    }

    public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes, errorViewResolvers);
        Assert.notNull(errorProperties, "ErrorProperties must not be null");
        this.errorProperties = errorProperties;
    }

    @RequestMapping(
        produces = {"text/html"}
    )
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = this.getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }

    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        HttpStatus status = this.getStatus(request);
        if (status == HttpStatus.NO_CONTENT) {
            return new ResponseEntity(status);
        } else {
            Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
            return new ResponseEntity(body, status);
        }
    }

//생략

 

답변 2

0

안녕하세요. 오스즈님, 공식 서포터즈 David입니다.

생각하신 것과 비슷하게 에러에 맞는 뷰 이름이 생성되게 됩니다.

DefaultErrorViewResolver 클래스의 resolve 메서드를 참고해주세요.

만약 500 에러라면 viewName으로 500이 넘어옵니다.

private ModelAndView resolve(String viewName, Map<String, Object> model) {
		String errorViewName = "error/" + viewName;
		TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
				this.applicationContext);
		if (provider != null) {
			return new ModelAndView(errorViewName, model);
		}
		return resolveResource(errorViewName, model);
	}

감사합니다.

0

안녕하세요, 인프런 AI 인턴입니다.

오스즈님께서 질문하신 BasicErrorController 에 대한 처리 방식에 대해 궁금하신 부분이 있으신 것 같습니다. BasicErrorController와 에러 페이지 처리 방식에 대해 이해하시는 데 도움이 될 몇 가지 정보를 공유해 드리겠습니다.

먼저, Spring Boot의 BasicErrorController는 기본적인 에러 처리 컨트롤러로서 /error 경로에 매핑되어 있으며, 발생하는 모든 에러들을 처리하게 됩니다. 이 컨트롤러는 HTTP 요청의 Accept 헤더에 따라 HTML 페이지나 JSON 응답을 반환합니다. 주로 errorHtml 메서드는 웹 브라우저의 요청에 대해 HTML 응답을, 그리고 error 메서드는 API 요청에 대해 JSON 응답을 반환하는 역할을 합니다.

질문하신 내용에 대한 답변으로는, BasicErrorController가 반환하는 ModelAndView 객체는 실제로 Spring MVC가 처리하는 과정에서 View 이름("error")과 모델 객체를 활용하여 최종적으로 사용자에게 보여질 뷰를 결정합니다. 여러분이 언급한 RequestDispatcher를 직접 사용하는 대신, Spring MVC 내부에서 이러한 처리를 자동으로 수행합니다. 즉, ModelAndView 객체의 View 이름과 모델 데이터를 기반으로 적절한 에러 페이지를 렌더링하는 것입니다.

이 부분에 대해 더 구체적으로 이해하시려면, BasicErrorController의 작동 원리Spring MVC의 뷰 해석 과정에 대해 살펴보시는 것이 좋습니다. View Resolver 구성 요소가 어떻게 ModelAndView 내의 View 이름을 실제 뷰 경로로 변환하는지, 그리고 이 과정에서 상태 코드와 모델 데이터가 어떻게 활용되는지를 이해하시면 도움이 될 것입니다.

이해를 돕기 위해 관련된 내용을 공유 드립니다:
- Spring Boot의 ErrorPage 제공 기능에서는 BasicErrorController가 자동으로 /error에 매핑되어 있는 방식과 스프링 부트가 어떻게 에러 페이지를 처리하는지에 대해 설명하고 있습니다.

이 내용들을 통해 BasicErrorController을 포함한 Spring Boot의 에러 처리 메커니즘에 대한 이해를 높일 수 있을 것입니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.