[Spring Boot] 스프링 웹 MVC - Thymeleaf

dsunni·2020년 8월 6일
6

1. 템플릿엔진

참고

https://insight-bgh.tistory.com/252


템플릿 엔진이란?

img

템플릿 엔진이란 동적 컨텐츠를 생성하는 방법이다.

템플릿 양식과 특정 데이터 모델에 따른 입력 자료를 결합하여 결과 문서를 출력하는 소프트웨어를 말하며 view code(html)과 data logic code(db connection)을 분리해주는 기능을 한다.

스프링 MVC에서 주로 동적인 View를 만드는데 사용한다. 그렇다고 View만 만드는데 사용하지는 않으며 이메일 등 다양한 용도로 사용 가능하다.

템플릿 엔진에는 서버사이드, 클라이언트 사이드 두 가지가 있다.

  • 서버 사이드 템플릿 엔진 : 서버에서 가져온 데이터를 미리 정의된 템플릿에 넣어 html을 그린 뒤 클라이언트에게 전달해준다.
    • HTML 코드에서 고정적으로 사용되는 부분은 템플릿으로 만들어두고, 동적으로 생성되는 부분만 템플릿에 소스코드를 끼워넣는 방식으로 동작한다.
  • 클라이언트 사이드 템플릿 엔진 : HTML 형태로 코드를 작성하며, 동적으로 DOM을 그리게 해주는 역할을 한다.
    • 데이터를 받아서 DOM 객체에 동적으로 그려주는 프로세스를 담당한다.

스프링 부트가 자동 설정을 지원하는 템플릿 엔진

  • FreeMarker
  • Groovy
  • Thymeleaf
  • Mustache

JSP를 권장하지 않는 이유

Thymeleaf와 달리 JSP는 스프링부트가 자동 설정을 지원하지 않으며 권장하지도 않는다.

왜 Spring Boot에서는 JSP를 권장하지 않을까?

우선 JSP를 사용하면 JAR 패키징을 할 수 없어 WAR 패키징을 해야한다. 물론, WAR패키징으로도 임베디드 톰캣으로 실행할 수 있고 배포도 가능하다.

하지만 Undertow라는 최근에 만들어진 서블릿 엔진이 JSP를 지원하지 않는 등 제약사항이 있다.

또한 JSP에 대한 의존성을 넣으면 의존성 문제가 생기는 경우도 있다.


JAR vs WAR

JAR와 WAR 모두 JAVA의 jar 툴을 이용해 생성된 파일이며 애플리케이션을 쉽게 배포하고 동작시킬 수 있도록 관련 파일을 패키징해준다.

  • JAR (Java Archive)
    • .jar 자바 프로젝트를 압축한 파일
    • 자바 리소스, 속성파일, 라이브버리 등이 포함되어있음
    • 원하는 구조로 구성이 가능하며 JDK(Java Development Kit)에 포함되어 있는 JRE(Java Runtime Environment)만 가지고도 실행 가능하다.
  • WAR(Web Application Archive)
    • .war servlet / jsp 컨테이너에 배치할 수 있는 웹어플리케이션 압축 파일
    • 웹 관련 자원만을 포함 (JSP, Servlet, JAR, Class, HTML 등)
    • JAR과 달리 WEB-INF 및 META-INF 디렉토리로 사전 정의된 구조를 사용하며 실행하기 위해서 Tomcat과 같은 웹 서버 또는 웹 컨테이너(WAS)가 필요하다.
    • WAR도 JAVA의 jar 옵션(java -jar)을 이용해 생성하는 JAR 파일의 일종이다.


2. Thymeleaf

Thymeleaf는 비교적 최근에 만들어진 템플릿 엔진이며 서버사이드 자바 템플릿 엔진의 한 종류이다.

JSP와 Thymeleaf의 가장 큰 차이점은 JSP와 달리 Servlet Code로 변환되지 않다는 점이다. 따라서 비즈니스 로직과 분리되어 오로지 View에 집중할 수 있다.


(1) 의존성 추가

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

기본적으로 모든 View는 src - templates 디렉토리에 있다.


(2) Test

Controller

@Controller
public class SampleController {
    @GetMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("name", "dsunni");
        return "hello";
    }
}
  • @Controller 이기 때문에 return하는 String은 View의 이름이된다.
  • Model data를 받을 model을 선언

hello.html (template - hello.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
</body>
</html>

TestController

@RunWith(SpringRunner.class)
@WebMvcTest(SampleController.class)
public class SampleControllerTest {
    @Autowired
    MockMvc mockMvc;

    @Test
    public void hello() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/hello"))
                .andExpect(status().isOk())
                .andDo(print())
                .andExpect(view().name("hello"))
                .andExpect(model().attribute("name", is("dsunni")));

    }
}
  • 요청 : "/"
  • 응답
    • 모델의 attribute : name : dsunni
    • 뷰 이름 : hello

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /hello
       Parameters = {}
          Headers = {}
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = me.dsunni.SampleController
           Method = public java.lang.String me.dsunni.SampleController.hello(org.springframework.ui.Model)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = hello
             View = null
        Attribute = name
            value = dsunni

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = {Content-Language=[en], Content-Type=[text/html;charset=UTF-8]}
         
<!-- 렌더링된 결과 -->         
     Content type = text/html;charset=UTF-8
             Body = <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
</html>
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

.andDo(print()) 를 통해 렌더링 된 결과까지 확인할 수 있는데, 이것은 Thymeleaf를 사용했기 때문이다.

JSP를 사용하면 본문을 확인(렌더링된 결과)하는 것이 매우 힘들다.

서블릿 엔진 자체가 JSP 템플릿을 완성시키기 때문에 응답으로 내보낼 최종적인 View를 확인하기 위해서는 서블릿 엔진 개입이 필수적이다.

반면 Thymeleaf는 서블릿 컨테이너의 개입 없이 독자적으로 최종적인 View를 완성한다.

테스트에서 사용한 mockMVC는 가짜 서블릿 컨테이너이며 실제 서블릿 컨테이너가 할 수 있는 일을 다 할 수 없다. 따라서 만약 JSP였더라면 렌더링된 결과를 확인할 수 없없을 것이다.


(3) Thymeleaf 사용

https://www.thymeleaf.org/doc/articles/standarddialect5minutes.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1 th:text="${name}">Name</h1>
</body>
</html>
  • Thymeleaf 사용하기 위해 th 네임스페이스 추가

  • Controller에서 Model로 받아온 name 사용

    • <h1 th:text="${name}">Name</h1>

image
Controller에서 내려준 name attribute값이 잘 전달되었다

profile
https://dsunni.tistory.com/ 이사갑니답

0개의 댓글