이 문서의 내용
테스트 환경 및 주요 아젠다
요청이 Controller로 매핑되고 있으나 templateResolver가 jsp 파일 경로 읽지 못하고 Error resolving template [greeting], template might not exist or might not be accessible by any of the configured Template Resolvers 오류가 발생합니다.
There was an unexpected error (type=Internal Server Error, status=500).
Error resolving template [greeting], template might not exist or might not be accessible by any of the configured Template Resolvers
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [greeting], template might not exist or might not be accessible by any of the configured Template Resolvers
at org.thymeleaf.engine.TemplateManager.resolveTemplate(TemplateManager.java:869)
at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:607)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1072)
at org.thymeleaf.spring5.view.ThymeleafView.renderFragment(ThymeleafView.java:366)
at org.thymeleaf.spring5.view.ThymeleafView.render(ThymeleafView.java:190)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1397)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1142)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1726)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:840)
웹 요청을 처리하는 Controller는 파라미터를 Model로 다시 전달하고 View에서 표시하기를 원합니다.
@RequestMapping(value="/greeting", method=RequestMethod.GET)
public ModelAndView greeting(@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) throws Exception {
ModelAndView mav = new ModelAndView("greeting");
mav.addObject("name", name);
return mav;
}
프로젝트의 resources 디렉토리 구조는 다음과 같습니다.
jsp 파일은 templates/WEB-INF/views 디렉토리 하위 파일로 포함됩니다.
$ tree
├─static
│ ├─css
│ └─js
└─templates
└─WEB-INF
└─views
└─greeting.jsp
application.yml은 다음과 같습니다.
spring:
mvc:
view:
prefix: /templates/WEB-INF/views/
suffix: .jsp
프로젝트의 build.gradle은 다음과 같습니다.
dependencies {
implementation('org.springframework.boot:spring-boot-starter-thymeleaf')
implementation('org.springframework.boot:spring-boot-starter-log4j2')
implementation('org.springframework.boot:spring-boot-starter-web')
implementation('org.apache.logging.log4j:log4j-web:2.14.+')
implementation('javax.servlet:jstl')
implementation('org.apache.tomcat.embed:tomcat-embed-jasper')
developmentOnly('org.springframework.boot:spring-boot-devtools')
testImplementation('org.springframework.boot:spring-boot-starter-test')
}
Step 1: Exception 발생 코드 디버깅
웹에서 해당 URL로 요청을 보내고 오류가 발생된 지점을 찾습니다.
Controller가 ModelAndView 객체를 리턴하는 코드에 Breakpoint를 걸고 진행하면 DispatcherServlet에서 view 객체를 렌더링하는 코드에 진입합니다.
이때 view 객체는 ThymeleafView 인스턴스입니다.
view.render(mv.getModelInternal(), request, response);
당므으로 TemplateResolver가 jsp 파일 경로를 탐색하는 코드에 진입합니다.
prefix = "/templates/"suffix = ".html"
application.yml에서 view 경로를 지정하고 있으나 TemplateResolver가 참조하는 경로와 차이가 있습니다.
spring:
mvc:
view:
prefix: /templates/WEB-INF/views/
suffix: .jsp
Step 2: application.yml 수정
application.yml에서 thymeleaf 관련 설정을 추가합니다.
spring:
thymeleaf:
check-template-location: true
prefix: classpath:/templates/WEB-INF/views/
suffix: .jsp
Spring 애플리케이션을 다시 시작하고 웹 요청을 디버깅합니다.
TemplateResolver가 참조하는 경로가 thymeleaf.prefix와 thymeleaf.suffix으로 적용됩니다.
웹 요청이 정상적으로 처리되는 것을 확인 할 수 있습니다.