본문 바로가기

Java/Spring

jsp 파일 경로를 읽지 못하는 "Error resolving template, template might not exist or might not be accessible by any of the configured Template Resolvers" 오류

이 문서의 내용

    테스트 환경 및 주요 아젠다

    더보기
    더보기

    이 프로젝트의 개발 환경

    • 주요 개발 환경 및 언어
      • Springframework.boot 2.5.4
      • OpenJDK 17
    • 기타 환경
      • IntelliJ IDE 2020.3.4 Ultimate Edition

    요청이 Controller로 매핑되고 있으나 templateResolverjsp 파일 경로 읽지 못하고 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.prefixthymeleaf.suffix으로 적용됩니다.

    웹 요청이 정상적으로 처리되는 것을 확인 할 수 있습니다.