Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[박재홍] [스프링부트 어플리케이션의 error페이지는 어디서 만들어질까요?] #1

Open
JaeHongDev opened this issue Mar 30, 2023 · 2 comments

Comments

@JaeHongDev
Copy link
Member

JaeHongDev commented Mar 30, 2023

@chhs2131
@wnsgh12s
@HiiWee
@JaeHongDev

❗️이슈 발생 위치

섹션제목: 톰캣의 error페이지와 spring boot의 에러페이지가 다르다?


🤔 왜 이슈를 생성했나요?

이전 스터디에서 자바와 톰캣으로 스프링 프레임워크와 유사한 기능을 제공하는 프레임워크를 처음부터 구현해본 경험을 했습니다.
우리가 만든 프레임워크에서 존재하지 않는 url로 접근했을 때 톰캣의 에러페이지가 표시되는데, 이는 스프링부트 어플리케이션을 실행시켰을 때의 에러 페이지와 다릅니다.

그래서 한번 다른 분들도 해당 Error 페이지가 어떻게 생성되는지 한번 찾아보면 좋을 것 같아서 해당 이슈를 생성하게 되었습니다!


😁 공유하고 싶은 내용 or 질문하고 싶은 내용


📌 참고자료, 공유하고 싶은 자료

힌트를 드리자면 url매핑이 이뤄지는 코드를 보다가 결국 어떤식으로 View가 생성되는지 생각해보면서 의존성을 하나씩 확인해보면 좋을것 같아요!
*추가로 html 파일로 만들어지지 않습니다!

@JaeHongDev JaeHongDev changed the title [${박재홍}] ${스프링부트 어플리케이션의 error페이지는 어디서 만들어질까요?} [박재홍] [스프링부트 어플리케이션의 error페이지는 어디서 만들어질까요?] Mar 30, 2023
@chhs2131
Copy link
Member

chhs2131 commented Apr 4, 2023

RequestMapping 선언되지않은 path를 입력했을 때 Whitelabel Error Page 를 누가 생성할까?

를 기준으로 찾아보았습니다!

image

낯익은 DispatcherServlet.doService() 부터 시작하여, 최종적으로 초기화된 View.render()를 통해 결과를 전달하는 익숙한 로직을 확인했습니다 😄

그럼 어떤 View를 사용할지는 언제 설정될까요? View는 ModelAndView.getViewName()을 기준으로 생성됩니다. RequestMapping되지 않은 path의 경우 viewName이 error 로 설정됩니다. viewName은 곧 ModelAndView에 대응되는 Object 명인 것 같습니다.

DispatcherServlet.doDispatch에 ModelAndView 변수가 존재하며 HandlerAdapter를 통해 초기화됩니다. 내부적으로 어떤 과정을 통해 error 까지 도달하게 되는지는.....! 확인 못해봤습니다 😅

최종적으로 ViewName error 와 매칭되는 ErrorMvcAutoConfiguration에 render()가 실행되며, 내부적으로 String.Builder()를 통해 html 페이지를 제작하는걸 볼 수 있었습니다.


참고한 코드

참고용 DispatcherServlet.java 의 render() 메소드

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
  // Determine locale for request and apply it to the response.
  ...
  View view;
  String viewName = mv.getViewName();
  if (viewName != null) {
	  // We need to resolve the view name.
	  view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
	  if (view == null) {
		  throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
				  "' in servlet with name '" + getServletName() + "'");
	  }
  }
  ...
  
  // Delegate to the View object for rendering.
  ...
  try {
	  if (mv.getStatus() != null) {
		  request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, mv.getStatus());
		  response.setStatus(mv.getStatus().value());
	  }
	  view.render(mv.getModelInternal(), request, response);
  }
  ...
}

참고용 ErrorMvcAutoConfiguration.java

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "server.error.whitelabel", name = "enabled", matchIfMissing = true)
@Conditional(ErrorTemplateMissingCondition.class)
protected static class WhitelabelErrorViewConfiguration {
  private final StaticView defaultErrorView = new StaticView();
  
  @Bean(name = "error")
  @ConditionalOnMissingBean(name = "error")
  public View defaultErrorView() {
	  return this.defaultErrorView;
  }
  ...

@HiiWee
Copy link
Member

HiiWee commented Apr 7, 2023

글이 길어져서 #6 이슈로 따로 작성했습니다!
핵심적인 흐름만 담아보려 했지만, 그래도 많이 길어지네요.. 이해가 수월하게 됐으면 좋겠습니다!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants