본문으로 바로가기
반응형

[스프링부트 (3)] SpringMVC(2) Spring Boot View 설정 및 JSP 연동하기(Thymeleaf 추가)

 

안녕하세요. 갓대희 입니다. 이번 포스팅은 [ 스프링 부트 View 설정방법 ] 입니다. : ) 

 

 

 

이번 포스팅에선 MVC 패턴중 View 설정하는 방법을 설명하려 한다. 그리고 JSP 및 VelocityThymeleaf를 예제를 포함 하려 한다.

 

0. 들어가기 앞서

▶ JSP 제한 사항

 - 스프링 공식문서에 보면 내장된 서블릿 컨테이너에는 jsp 제한사항이 있다.

https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-developing-web-applications
 - 스프링 부트는 가능하다면 jsp를 피하고 Thymeleaf와 같은 템플릿 엔진을 사용하라고 권장한다.

 

7.1.10. Template Engines

As well as REST web services, you can also use Spring MVC to serve dynamic HTML content. 
Spring MVC supports a variety of templating technologies, including Thymeleaf, FreeMarker, and JSPs. 
Also, many other templating engines include their own Spring MVC integrations.

Spring Boot includes auto-configuration support for the following templating engines:
FreeMarker, Groovy, Thymeleaf, Mustache

If possible, JSPs should be avoided. There are several known limitations when using them with embedded servlet containers.

7.4.5. JSP Limitations
When running a Spring Boot application that uses an embedded servlet container 
(and is packaged as an executable archive), there are some limitations in the JSP support.

With Jetty and Tomcat, it should work if you use war packaging. 
An executable war will work when launched with java -jar, and will also be deployable to any standard container. 
JSPs are not supported when using an executable jar.
Undertow does not support JSPs.
Creating a custom error.jsp page does not override the default view for error handling. Custom error pages should be used instead.

 

 

1. Spring Boot에서의 View 

▶ 스프링 부트에서도 여러가지 뷰를 사용 할 수 있다.

ㆍJSP/JSTL
Thymeleaf
FreeMarker
Velocity
Groovy Template Engine
Tiles

... etc

 

※ 앞서 포스팅에서 기본 프로젝트 구조를 잡았을 것이다
src > main > reousrce > [static] 폴더엔 정적 리소스들을 추가하였을 것이다.
src > main > reousrce > [templates] 폴더도 확인할 수 있을 것인데 Thymeleaf(.html), Velocity(.vm)등과 관련된 파일만 동작하고 jsp 파일은 추가하여도 작동하지 않으니 참고 하자.

 

※ 폴더 구조

src
└─ main
   └─ resource
      └─ templates (View: Thymeleaf, Groovy, Velocity 등)
      └─ static    (정적 컨텐츠 : html, css, js, image 등)

 

▶ 내장 Tomcat

 - 스프링부트는 웹 개발을 위해 자주 사용되는 Spring의 Component들과 Tomcat, Jetty 등의 경량 웹 어플리케이션 서버를 통합한 경량의 웹개발 프레임 워크이다.

 - 즉 별도의 웹 어플리케이션 서버 없이 SpringBoot를 통해 프레임워크와 웹 어플리케이션 서버를 통합했다고 생각하면 된다.

 

앞서 포스팅에서 다음 디펜던시(spring-boot-starter-web)를 추가 하였을 것이다.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

spring-boot-starter-web 에는 tomcat이 포함되어 있지만, JSP 엔진은 포함하고 있지 않다.
하지만 간단한 설정만 해주면 JSP view를 사용 가능하다.

이제부터 jsp를 사용하는 방법을 알아 보도록 하자.

 

"JSP"
2. pom.xml / gradle 설정 

▶ pom.xml

 - jasper,jstl을 의존성에 추가해야 JSP파일의 구동이 가능하다.
   (앞서 말했듯이 jsp 파일은 Springboot의 templates 폴더안에서 작동하지 않으니 참고하자.)

<dependency>
	<groupId>org.apache.tomcat.embed</groupId>
	<artifactId>tomcat-embed-jasper</artifactId>
	<scope>provided</scope>
</dependency>
        
<!-- jstl 라이브러리 -->
<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>jstl</artifactId>
</dependency>

▶ build.gradle

compile('org.apache.tomcat.embed:tomcat-embed-jasper')
compile('javax.servlet:jstl:1.2')

 

 

3. JSP  경로 설정(디렉토리 생성)

▶ 스프링 부트에서도 여러가지 뷰를 사용 할 수 있다.

 - WEB-INF/jsp/(또는 WEB-INF/views/)

   (full 경로를 말씀해달라고 하시는 분들이 있어 추가하자면 다음과 같습니다.

    /src/main/webapp/WEB-INF/jsp/)
 - 톰캣기반 자바 웹어플리케이션에서는 보안상 jsp 위치를 URL로 직접접근할 수 없는 WEB-INF폴더 아래 위치시킨다.

 

ex) test.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>View Test Page</title>
</head>
<body>
	<h2>Hello! ${name}</h2>
	<div>JSP List Test</div>
        <c:forEach var="item" items="${list}" varStatus="idx">
        ${idx.index}, ${item} <br />
        </c:forEach>
</body>
</html>

 

 

4. application.properties

※ Spring 애플리케이션 시작시 application.properties 파일에 정의된 내용을 로드한다.
   (스프링부트의 AutoConfiguration을 통해 자동 설정한 속성값들이 존재하며, application.properties의 해당 값들은 오버라이드 한다.)

▶ server.port

 - 별다른 설정을 하지 않으면 default 포트는 8080이다.
 - Spring Boot에 기본적으로 내장되어있는 Tomcat과 Jetty와 같은 WAS의 포트번호를 임의로 변경 할 수 있다.

server.port = 8888

▶ prefix/suffix

 - jsp 페이지를 처리하기 위한 prefix와 suffix를 application.properties에 추가 하자.
 - 앞서 생성한 JSP 경로를 prefix로 선언, 그리고 확장자럴 suffix로 선언할 수 있다.

spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

 

 

5. Controller 설정
@RequestMapping("/test")
public ModelAndView test() throws Exception{
	ModelAndView mav = new ModelAndView("test");
	mav.addObject("name", "goddaehee");

	List<String> testList = new ArrayList<String>();
	testList.add("a");
	testList.add("b");
	testList.add("c");

	mav.addObject("list", testList);
	return mav;
}

 

 

6. jsp파일 서버 재시작 없이 바로 적용하기

 - 스프링 부트는 스프링 프로젝트와 다르게, 동적 파일들의 파일 변경을 자동으로 반영하지 않는다.
 - 기존 스프링과 동일하게 자동 반영하기 위해선 다음과 같은 설정을 추가하여 주면 된다.
 - Spring Boot 2.x 버전 기준으로 포스팅 한다.
 

▶ pom.xml

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-devtools</artifactId>
</dependency>

▶ application.properties

devtools.livereload.enabled=true

 

※참고 Spring Boot 1.x 버전 기준으로는 application.properties에 다음 내용을 추가해주면 적용된다.

server.jsp-servlet.init-parameters.development=true 

 

◎ Application을 실행하여 보자. 다음과 같은 화면을 볼 수 있다.

 

 

"thymeleaf"
7. thymeleaf

Thymeleaf 홈페이지 
https://www.thymeleaf.org/index.html

Thymeleaf + Spring
 - https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html

 

▶ Dependency 추가

 - pom.xml

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

 

 - build.gradle

implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

▶ 파일 기본 경로 및 html 생성

 - 경로 : /src/main/resources/templates/thymeleaf

 - thymeleafTest.html

<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Hello thymeleaf</title>
</head>
<body>
	<h1>Hello thymeleaf</h1>
	<h2>name = <span th:text="${testModel.name}"></span></h2>
	<h3>id =  <span th:text="${testModel.id}"></span></h3>
</body>
</html>

▶ application.properties 설정 

#JSP와 같이 사용할 경우 뷰 구분을 위해 컨트롤러가 뷰 이름을 반환할때 thymeleaf/ 로 시작하면 타임리프로 처리하도록 view-names 지정
spring.thymeleaf.view-names=thymeleaf/*
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
#thymeleaf를 사용하다 수정 사항이 생길 때 수정을 하면 재시작을 해줘야 한다. 이를 무시하고 브라우저 새로고침시 수정사항 반영을 취해 cache=false 설정(운영시는 true)
spring.thymeleaf.cache=false
spring.thymeleaf.check-template-location=true

 Vo 설정 

package com.god.bo.test.vo;

public class TestVo {
    private Long mbrNo;
    private String id;
    private String name;

    public TestVo() {
    }

    public TestVo(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getMbrNo() {
        return mbrNo;
    }

    public void setMbrNo(Long mbrNo) {
        this.mbrNo = mbrNo;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Controller 설정 

@RequestMapping("/thymeleafTest")
public String thymeleafTest(Model model) {
    TestVo testModel = new TestVo("goddaehee", "갓대희") ;
    model.addAttribute("testModel", testModel);
    return "thymeleaf/thymeleafTest";
}

 

 

스프링부트를 이용해 아주 간단히 View 설정해 보았다.

 

 

반응형

댓글을 달아 주세요

  1. 1111 2020.02.19 20:52

    안녕하세요 예제 하면서 질문이 있습니다.

    jsp를 적용시키고나서 thymeleaf를 적용하면서,

    Circular view path [thymeleafTest]: would dispatch back to the current handler URL [/thymeleafTest] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
    javax.servlet.ServletException: Circular view path [thymeleafTest]: would dispatch back to the current handler URL [/thymeleafTest] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
    이 발생합니다.


    에러가 발생을 해서 기존에 적용되어있는
    build.gradle 에있는
    compile('org.apache.tomcat.embed:tomcat-embed-jasper')
    compile('javax.servlet:jstl:1.2')
    두가지를 주석처리했으며,

    spring.mvc.view.prefix(suffix)를 주석처리한 상태입니다.

    그리고, Controller에 기존에 적용되어있는것도 주석 처리했으며, thymeleaf를 불러오는 메서드만 남겼습니다

    • gradle import가 잘됫는지, 프로퍼티에 prefix, suffix 설정이 올바른지 등 확인해봐야할 것 같은데 확실한진 잘 모르겠네요.
      참고-
      https://stackoverflow.com/questions/29953245/configure-viewresolver-with-spring-boot-and-annotations-gives-no-mapping-found-f

  2. anonymous 2020.03.10 17:47

    안녕하세요 작성해주신글 보면서 스프링부트를 잘 따라하고 있습니다. 궁금증이 생겨 댓글 남깁니다.
    마지막 타임리프 컨트롤러생성에 TestModel testmodel 은 어디서 생성되는건지 궁금합니다.
    현재 맞지않는 타입이라고 해서 진행이 되지않습니다..

  3. anonymous 2020.03.15 01:27

    안녕하세요 타임리프 진행중에 오류가 생겨 질문드립니다.
    현재 메이븐으로 부트사용중인데 셋팅하신대로 한것같은데 localhost:8083/thymeleafTest 접속시
    화이트라벨 에러 페이지와 함께 /WEB-INF/jsp/thymeleaftTest.jsp 이렇게 나옵니다.
    thymeleafTest.html 파일은 템플릿에 넣어두었는데 경로를 저렇게 인식하는게 문제인것같은데 해결방법을 알고싶습니다.

    • 혹시 application.properties나 application.yml 설정에서

      #JSP와 같이 사용할 경우 뷰 구분을 위해 컨트롤러가 뷰 이름을 반환할때 thymeleaf/ 로 시작하면 타임리프로 처리하도록 view-names 지정 spring.thymeleaf.view-names=thymeleaf/* spring.thymeleaf.prefix=classpath:templates/ spring.thymeleaf.suffix=.html

      해당 부분도 잘 설정 되어 있을까요?

    • anonymous 2020.03.15 17:08

      프로퍼티에
      server.port = 8083

      spring.mvc.view.prefix=/WEB-INF/jsp/
      spring.mvc.view.suffix=.jsp

      spring.thymeleaf.view names=thymeleaf/*
      spring.thymeleaf.prefix=classpath:templates/
      spring.thymeleaf.suffix=.html

      spring.thymeleaf.cache=false
      spring.thymeleaf.check-template-location=true

      spring.devtools.livereload.enabled=true

      이렇게 되어있습니다.

    • 1. html 경로를 변경 해주세요.
      ▶ before : /src/main/resources/templates
      ▶ after : /src/main/resources/templates/thymeleaf

      2. controller 에서 return 부분을 다음과 같이 바꿔주세요.
      ▶ before : thymeleafTest
      ▶ after : thymeleaf/thymeleafTest

      이렇게 한번 해주시면 될지 ㅠ

      물론 폴더 구조를 잡을때
      /WEB-INF/jsp로 설정할수도 있지만 일단 전 기본 폴더인 templates 하위에 잡아 둔거니 참고 부탁드립니다.

    • 아. 그리고 아까 적어주신 부분에서 이부분도 확인해주세요.


      프로퍼티에
      server.port = 8083

      spring.mvc.view.prefix=/WEB-INF/jsp/
      spring.mvc.view.suffix=.jsp

      spring.thymeleaf.view names=thymeleaf/*
      spring.thymeleaf.prefix=classpath:templates/
      spring.thymeleaf.suffix=.html

      spring.thymeleaf.cache=false
      spring.thymeleaf.check-template-location=true

      spring.devtools.livereload.enabled=true

      =>>>>>>>>>>>>>>>>>>>>>
      spring.thymeleaf.view names=thymeleaf/*

      이 부분에 view names => view-names
      - 가 빠졌는지도 확인해주세요.

    • 만약 yml을 쓰시면 (> 는 공백)

      spring:
      >>mvc:
      >>>>view:
      >>>>>>prefix: /WEB-INF/jsp/
      >>>>>>suffix: .jsp

      >>thymeleaf:
      >>>>prefix: classpath:templates/
      >>>>suffix: .html
      >>>>view-names: thymeleaf/*
      >>>>cache: false
      >>>>check-template-location: true

    • Favicon of https://aricode.tistory.com BlogIcon 아리 코딩 2020.11.05 11:21 신고

      저도 같은 에러가 생겼는데

      갓대희님 말씀대로 html 경로를 변경하니, 잘 나옵니다!

  4. 좋은하루 2020.03.25 11:21

    안녕하세요 간단하고 유익한 예제를 올려주셔서 정말 감사합니다.
    thymeleaf 를 통해 컨트롤러에서 해당 값을 출력하는 부분을 하고 있는데,
    왜 처음 생성했던 TestController에서 함수를 만들어 실행하면 정상 동작하는데,
    TestRestController에서 함수를 만들어 실행하면 그냥 string 출력 되서 나올까요??
    차이점이 뭔지 궁금합니다~!!!

  5. 곰장어 2020.06.19 13:06

    new TestVo("goddaehee", "갓대희";); 에서 에러라인이 발생됩니다
    TestVo에 생성자가 2개인 걸 정의해주지않아서 그런거같은데
    위 예제에선 따로 설명이되어있지않네요

  6. beast 2020.09.13 21:51

    어우 드디어 해결했네 ㅠㅠ
    별에 별짓을 다해도 화이트라벨 error 500 떠서 처음부터 다시 만들었는데 드디어 에러원인을 찾았네요
    저처럼 삽질하는 사람 있을 까봐 남깁니다.

    제 경우에는 application.properties 에서
    spring.thymeleaf.view-names=thymeleaf/*
    spring.thymeleaf.prefix=classpath:templates/
    spring.thymeleaf.suffix=.html

    classpath:templates/ <- 부분이 문제였습니다
    (역슬래쉬)/ 이후에 공백이 있더라구요...prefix뿐만아니라 view-name, suffix로 설정하는 값들도 아마 문자열 처리되기 때문에 에러발생하는 사람들 공백문자 있는지 한번 확인해보시기 바랍니다.. 저는 진짜 원인 찾느라 2시간은 걸린듯..

  7. newbie 2020.10.31 04:47

    혹시 저 WEB-INF 의 경로좀 알수있을까요? ㅠㅠ

  8. newcomer 2020.10.31 04:49

    Whitelabel Error Page
    This application has no explicit mapping for /error, so you are seeing this as a fallback.

    Sat Oct 31 04:45:58 KST 2020
    There was an unexpected error (type=Not Found, status=404).
    JSP file [/WEB-INF/views/test.jsp] not found

    이렇게 뜨네요 ㅠ 파일을 못찾는건 제가 경로를 잘못설정한거같은데 WEB-INF 폴더 가 어디에 위치해야하는 걸까요 ? ㅠㅠ

    application.properties 에는 아래와 같이

    spring.mvc.view.prefix=/WEB-INF/views/
    spring.mvc.view.suffix=.jsp

    설정해놨습니다

  9. 11111 2020.11.12 22:44

    안녕하세요 현재 3.9.14버전으로 jsp띄울려는데 error페이지만뜨네요 이유를 모르겠습니다.
    pom, apllication.properties에 추가도 다 해주었는데 말이죠..

  10. qqqq 2021.02.25 11:13

    안녕하세요! 덕분에 좋은 예제 학습하고 있습니다.
    공부를 하던 중 문제가 발생하여 글을 남깁니다.

    localhost:8080/thymeleafTest를 실행시키면 리턴값인 thymeleaf/thymeleafTest가 보이고 html파일은 보이지 않습니다.

  11. chanchan 2021.03.19 22:52

    질문을 남기려 하는데 다른 질문들에 너무 대답을 잘 해주셔서 할 필요가 없네요..
    node 사용하다가 spring 처음 해봐서 잘 이해도 안 되고 답답했는데 도움이 많이 됩니다.
    감사합니다!

  12. 꼬마개발자푸푸 2021.05.08 13:05

    혹시 멀티 뷰 리졸버 설정도 다뤄주셨으면 안될까요 ㅠㅠ

    상황은 이렇습니다.

    [Spring Legacy Project + Mustache 템플릿]을 사용하고 있는 기존의 프로젝트를
    -> [Spring Boot Project + JSP or Thymeleaf 중 하나의 템플릿] 변경 후 개선하는 상황입니다 ㅠㅠ

    기존 낡은 프로젝트을 새롭게 마이그레이션 하는 경우 어떻게 해야하는지 요령이 없어서
    간단히 알고자 합니다.

    멀티 뷰 리졸버 설정에 대해서 알고 싶은 이유는 유지 보수를 단계적으로 진행할 때 편할 것 같기 때문입니다.

    일단 제 계획은 이렇습니다.

    [Spring Legacy Project + Mustache 템플릿] 로 구성된 기존 프로젝트 소스코드를

    1단계. [Spring Boot Project + Mustache ] : Spring Legacy -> Spring Boot 로 각종 설정 작업 진행.
    -> 일단 기존 프로젝트의 템플릿인 Mustache 와 소스 코드를 그대로 유지시키고, 스프링 부트 환경으로 전체적인 프로젝트를 변경한다.

    2단계. [Spring Boot Project + Mustache + Thymeleaf or JSP ] : 멀티 뷰 리졸버 설정을 통해 사용할 뷰 템플릿 엔진을 추가한다.

    3단계. 기존 Mustache 소스 코드 토대로 -> 점진적으로 Thymeleaf 또는 JSP로의 뷰코드 변경을 진행한다.
    -> 소스코드 마이그레이션이 완료되면 기존의 Mustache 템플릿과 관련된 설정 및 소스코드를 제거한다.

    4단계. [Spring Boot Project + Thymeaf or JSP] : 최종적으로 이와 같은 형태로 마감한다.

    이렇게 구상 중에 있는데요.
    현재 상황에서 2번 단계가 너무 어려운데 실무에서 이런 작업이 있을 경우
    어떻게 해결하는 것이 베스트 프랙티스 인지 예제나 요령 좀 간단히 포스팅 해주실 수 있을까요 ㅠㅠ

  13. BlogIcon 김동윤 2021.07.24 08:49

    정말 감사합니다 덕분에 오류 해결하였습니다ㅠㅠㅠ

  14. Favicon of https://summeriscoding.tistory.com BlogIcon 썸머는본투비휴먼 2021.11.08 17:29 신고

    안녕하세요.
    저는 지금 jsp로 설정되어있는 프로젝트에 thymeleaf붙이는 작업을 하고있는데 jsp설정이 properties로 되어있는게 아니라 @Bean을 써서 InternalResourceViewResolver 으로 되어있는데요 이게 우선순위가 쎈건지 thymeleaf가 죽어도 먹지를 않습니다ㅠ 얘가 setOrder가 2로 되어있어서 3으로 바꾸고 thymeleaf엔 그냥 view resolver사용해서 order2로 주기도하고 뭐 등등 다 해봤는데 죽어도 jsp의 /WEB-INF /xxxx.jsp 즉,prefix,suffix가 붙어서 갑니다.. 어떻게 해야할까요ㅠ 답답하네욤.. InternalResourceViewResolver을 죽이고 jsp,thymeleaf가 동시에 사용할수있는 view resolver가 있을까요 아니면 어떤 방법이 있을까요?