SpringMVC์ View. View์ ์ญํ ์ ์ฌ์ฉ์์๊ฒ UI๋ฅผ ์ ๊ณตํ๊ฑฐ๋ ์ ๋ ฅ์ ๋ฐ๊ณ , ์ฒ๋ฆฌํ ๊ฒฐ๊ณผ๊ฐ์ ๋ณด์ฌ์ฃผ๋ ์ญํ ์ ํ๋๊ฒ์ ์๋ค. ๋ํ์ ์ผ๋ก FrontframeWork๋ฅผ ์ฌ์ฉํด์ RestAPI๋ก ๊ตฌํํ๊ฑฐ๋ SSR(Server Side Rendering)์ธ Template Engine์ ์ฌ์ฉํ๋ค. ๋ํ์ ์ผ๋ก ์ฌ์ฉ๋๋ Template Engine์ Thymeleaf, Apache Freemarker, Mustache, Groovy Templates ๋ฑ์ด ์๋ค.
Template Engine์ ๋์ ์ธ ๋ฐ์ดํฐ๋ฅผ ๋ฐํ์ผ๋ก HTML๋ฑ์ ํ ํ๋ฆฟ์ ์์ฑํ๋ ์ญํ ์ ํ๋ค. Server์ธก์์ ๋๋๋ง ๋๊ธฐ ๋๋ฌธ์ SSR(Server Side Rendering)์ด๋ผ ํ๋ฉฐ ์๋ฒ์ธก์ ์ฝ๋์ HTML ๋ฌธ๋ฒ์ ์กฐํฉํ์ฌ ์ต์ข ๊ฒฐ๊ณผ๋ฌผ์ ํด๋ผ์ด์ธํธ์๊ฒ ์ ๋ฌํ๊ฒ ๋๋ค.
Spring์์ ๊ถ์ฅํ๋ Template Engine. HTML ๊ธฐ๋ฐ์ ํ ํ๋ฆฟ์ ์์ฑํ๋ฉฐ, thymeleaf์ ๋ฌธ๋ฒ์ ์ฌ์ฉํ์ฌ ๋์ ์ธ ๋ฐ์ดํฐ๋ฅผ ๋๋๋งํ๋ค. HTMLํ์ผ๋ก ์์ฑ๋๊ธฐ ๋๋ฌธ์ ๋๋๋ง์ ํ์ง ์๋๋ผ๋ ๋งํฌ์ ๋ ํ ํ๋ฆฟ์ ํ์ธ ํ ์ ์๊ณ if, each ์ ๊ฐ์ ๋ก์ง๋ ์ง์ํ๋ค.
์ฌ์ฉ๋ฒ
1. dependency ์ถ๊ฐ
โป ํ ์คํธ ํ๊ฒฝ : SpringBoot 2.7.12 / IntelliJ IDE / Windows 10
// gradle์ ๊ฒฝ์ฐ build.gradle์ ์ถ๊ฐ implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' / // maven ์ ๊ฒฝ์ฐ pom.xml ์ ์ถ๊ฐ <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
์์ ํด๋นํ๋ dependency๋ฅผ ๊ฐ๊ฐ ๋น๋ํด์ ๋ง๊ฒ ์ถ๊ฐํ๋ค.
2. application.properties์ ViewResolver ๊ด๋ จ ์ค์ ์ถ๊ฐ
spring.thymeleaf.prefix=classpath:/templates/ spring.thymeleaf.suffix=.html
templates ํด๋ ๋ด๋ถ์ ํ์ฅ์๊ฐ .html์ธ ํ์ผ๋ก ViewResolver๋ฅผ ์ง์ ํ๋ ๋ด์ฉ์ด๋ค. ๋ง์ฝ ๋ค๋ฅธ ๊ฒฝ๋ก๋ก ์ง์ ํ๋ค๋ฉด ์์ ํ ์ ์ฅํ๋ค. ํ์ผ์ resources>templates์ ์ ์ฅํ๋ฉด ์๋์ผ๋ก ViewResolver๊ฐ ์๋์ผ๋ก ํ์ผ์ ๊ฒฝ๋ก๋ฅผ ์ก์์ฃผ๊ธฐ ๋๋ฌธ์ Controller์ return ๊ฐ์๋ ํ์ผ๋ช ๋ง ์ ์ผ๋ฉด ๋๋ค.3. ํ๊ทธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ์ธ
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
HTML ํ์ผ ์๋จ์ th๋ก thymeleaf์ ํ๊ทธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ์ธํ๋ค.
4. ํ๊ทธ๋ฅผ ์ฌ์ฉํ์ฌ ๋์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ธ๋ฉํ๋ค.
Server ์ธก ์ฝ๋ GetMapping์ ํตํด test๋ผ๋ ๋ณ์๋ช ์ผ๋ก "ํ ์คํธ์ ๋๋ค." ๊ฐ์ ๋ฃ์ด testView๋ผ๋ ์ด๋ฆ์ View ํ์ผ๋ก ์ ๋ฌํ๋ค.<!DOCTYPE html> <html lang="ko" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>TEST</title> </head> <body> <h1 th:text="${test}"></h1> </body> </html>
th:text="${test}" model.addAttribute๋ฅผ ํตํด ์ ๋ฌ๋ test๊ฐ ๊ฐ์ด ๋ฐ์ธ๋ฉ๋๋๋ก SpringEL์ธ ${๋ณ์๋ช }์ ์ฌ์ฉํด ํ ํ๋ฆฟ์ ์์ฑํ๋ค.
th:text ๋ฌธ๋ฒ์ ์ฌ์ฉํด์ h1 ํ๊ทธ๋ฅผ ์์ฑํ์ง ์์์์๋ "ํ ์คํธ์ ๋๋ค."๋ผ๋ ๊ฐ์ด ๋์จ๊ฒ์ ํ์ธํ์๋ค.
Server์์ ๋ ๋๋ง์ ๊ฐ์ด ๋ฐ์ธ๋ฉ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๋ํ ์ฌ๋ฌ๊ฐ์ง ๋ฌธ๋ฒ์ ์ฌ์ฉํ์ฌ Style์ ์ ์ฉํ๊ฑฐ๋ value์ ์ ์ฉํ๋๋ฑ ๋ค์ํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
๊ธฐ๋ณธ ๋ฌธ๋ฒ
- ๋ณ์ ์ ์ธ : ${๋ณ์๋ช }
- ๋ณ์ ๊ฐ ์ถ๋ ฅ: [[๋ณ์๋ช ]]
- ์กฐ๊ฑด๋ฌธ :
<div th:if="${์กฐ๊ฑด์}"> ์กฐ๊ฑด์ด ์ฐธ์ผ ๋ ๋ณด์ฌ์ง๋ ๋ด์ฉ </div> <div th:unless="${์กฐ๊ฑด์}"> ์กฐ๊ฑด์ด ๊ฑฐ์ง์ผ ๋ ๋ณด์ฌ์ง๋ ๋ด์ฉ </div>
- ๋ฐ๋ณต๋ฌธ
<ul> <li th:each="item : ${์ปฌ๋ ์ }"> [[item]] </li> </ul>
- ์์ฑ๊ฐ ์ค์
<a th:href="@{๊ฒฝ๋ก/URL}">๋งํฌ</a> <img th:src="@{์ด๋ฏธ์ง_๊ฒฝ๋ก}" alt="์ด๋ฏธ์ง">
- ์ธํด๋ฃจ๋ (๋ค๋ฅธ ํ ํ๋ฆฟ์ ํฌํจ)
<div th:include="ํ ํ๋ฆฟ_ํ์ผ๋ช :: ์น์ _์ด๋ฆ"></div>
๋งํฌ๋ฅผ ํตํด ๋ค๋ฅธ ๋ฌธ๋ฒ์ ํ์ธํ ์ ์๋ค.
Apache Freemarker ๊ฐ์ ๊ฒฝ์ฐ Apache ์ฌ๋จ์์ ๊ด๋ฆฌํ๋ ์คํ์์ค Template Engine ์ด๋ฉฐ ํ์ผ ํ์ฅ์๋ช ์ .FTL(Freemarker Template Langueage)๋ก ์์ฑ๋๋ค. ์กฐ๊ฑด๋ฌธ, ๋ฐ๋ณต๋ฌธ, ๋งคํฌ๋ก์ ๋ฉ์๋ ๋ฑ์ ์๋ฐ์ ํ๋ก๊ทธ๋๋ฐ ์์๋ค์ ๊ฐ์ง๊ณ ์๋ค.
์ฌ์ฉ๋ฒ
1. dependency ์ถ๊ฐ
โป ํ ์คํธ ํ๊ฒฝ : SpringBoot 2.7.12 / IntelliJ IDE / Windows 10
// gradle์ ๊ฒฝ์ฐ implementation 'org.springframework.boot:spring-boot-starter-freemarker' // maven์ ๊ฒฝ์ฐ <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency>
2. application.properties ์ค์
spring.freemarker.template-loader-path=classpath:/templates/ spring.freemarker.suffix=.ftl
freemarker ๊ด๋ จ ViewResolver๋ฅผ ์ค์ ํด ์ค๋ค. resource/templates ๊ธฐ๋ณธ๊ฒฝ๋ก๋ฅผ ๊ฐ์ง๊ณ .ftl ํ์ฅ์๋ฅผ ๊ฐ์ง ํ์ผ๋ก ์ค์ ํ๋ค.3. ๋ฉ์๋ ๋ฐ ํ ํ๋ฆฟ ์์ฑ
๊ธฐ๋ณธ์ ์ธ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ๊ฒฝ์ฐ ${๋ณ์๋ช }์ ์ฌ์ฉํ๋ค. ์ฝ๋ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ "ํ๋ฆฌ๋ง์ปค ํ ์คํธ์ ๋๋ค."๊ฐ ๊ฒฐ๊ณผ๋ก ๋์จ๊ฒ์ ํ์ธ ํ ์ ์๋ค.
๊ธฐ๋ณธ ๋ฌธ๋ฒ
- ๋ณ์์ ์ธ : <#assign ๋ณ์๋ช = ๊ฐ>
- ๋ณ์ ์ฐธ์กฐ: ${๋ณ์๋ช }
- ์กฐ๊ฑด๋ฌธ :
<#if ์กฐ๊ฑด์> ์กฐ๊ฑด์ด ์ฐธ์ผ ๋ ์คํํ ๋ด์ฉ <#elseif ๋ค๋ฅธ_์กฐ๊ฑด์> ๋ค๋ฅธ ์กฐ๊ฑด์ด ์ฐธ์ผ ๋ ์คํํ ๋ด์ฉ <#else> ๋ชจ๋ ์กฐ๊ฑด์ด ๊ฑฐ์ง์ผ ๋ ์คํํ ๋ด์ฉ </#if>
- ๋ฐ๋ณต๋ฌธ :
<#list ๋ฆฌ์คํธ as ๋ณ์๋ช > ๋ฐ๋ณตํ ๋ด์ฉ </#list> <#foreach ๋ณ์๋ช in ๋ฆฌ์คํธ> ๋ฐ๋ณตํ ๋ด์ฉ </#foreach> <#list 1..10 as i> ๋ฐ๋ณตํ ๋ด์ฉ (${i}) </#list>
- ํจ์
${๋ณ์?upper_case} // ๋ณ์๋ฅผ ๋๋ฌธ์๋ก ๋ณํ ${๋ณ์?lower_case} // ๋ณ์๋ฅผ ์๋ฌธ์๋ก ๋ณํ ${๋ณ์?length} // ๋ณ์์ ๊ธธ์ด ๋ฐํ ${๋ณ์?default("๊ธฐ๋ณธ๊ฐ")} // ๋ณ์๊ฐ ์์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ ์ง์
- ์ธํด๋ฃจ๋ (๋ค๋ฅธ ํ ํ๋ฆฟ์ ํฌํจ)
<#include "๋ค๋ฅธ_ํ ํ๋ฆฟ.ftl">
๋งํฌ๋ฅผ ํตํด ์ถ๊ฐ ์ ๋ณด๋ฅผ ํ์ธ ํ ์ ์๋ค.
๊ฐ๋จํ๊ณ ์ง๊ด์ ์ธ Template Engine์ด๋ฉฐ ๋ก์ง์ด ์๋ Logic-Less Templates ๋ผ ๋ถ๋ฆฐ๋ค. ์กฐ๊ฑด๋ฌธ์ด๋ ๋ฐ๋ณต๋ฌธ์ ์์ง๋ง ์ด๋ ํ ํ๋ฆฟ์ ์ ์ฉ๋๊ธฐ ์ ์ ์๋ฒ ๋๋ ํด๋ผ์ด์ธํธ์์ ์ฒ๋ฆฌ๋๋ฏ๋ก ํ ํ๋ฆฟ์์ ์ฒ๋ฆฌํ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ฌ์ฉ๋ฒ
1. dependency ์ถ๊ฐ
โป ํ ์คํธ ํ๊ฒฝ : SpringBoot 2.7.12 / IntelliJ IDE / Windows 10
//gradle์ ๊ฒฝ์ฐ > build.gradle implementation 'com.github.spullara.mustache.java:compiler:0.9.6' //maven์ ๊ฒฝ์ฐ > pom.xml <dependency> <groupId>com.github.spullara.mustache.java</groupId> <artifactId>compiler</artifactId> <version>0.9.6</version> </dependency>
mustache ๋ ๊ธฐ๋ณธ์ ์ธ ๊ฒฝ๋ก๋ก resources/templates๋ฅผ ์ก๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ณ๋์ ViewResolver ์ค์ ์ ํ์์น ์๋ค. ํ์ผ ํ์ฅ์๋ .mustache ์ด๋ค.2. ๋ฉ์๋ ๋ฐ ํ ํ๋ฆฟ ์์ฑ
๊ธฐ๋ณธ ๋ฌธ๋ฒ์ {{๋ณ์๋ช }}์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ธ๋ฉํ๋ค. #์ ์ฌ์ฉํ์ฌ if๋ฌธ์ด๋ ๋ฐ๋ณต๋ฌธ๋ ์ฌ์ฉ๊ฐ๋ฅํ๋ค. ํ์ง๋ง ์ง์ ์ ์ธ ๋ก์ง์ด ์กด์ฌํ์ง ์๊ธฐ ๋๋ฌธ์ ์๋ฒ์์ ๋ก์ง์ ๋ํ ์ฒ๋ฆฌ ํ View๋ก ์ ๋ฌํด์ผํ๋ฉฐ ํ ํ๋ฆฟ๊ณผ ๋ก์ง์ ๋ช ํํ ๋ถ๋ฆฌ์์ผ ์ ์ง๋ณด์์ฑ๊ณผ ํ ํ๋ฆฟ์ ์ฌ์ฌ์ฉ์ฑ์ ๋์ ํ ํ๋ฆฟ ์์ง์ด๋ค.
๊ธฐ๋ณธ ๋ฌธ๋ฒ
- ๋ณ์ ์ถ๋ ฅ: {{๋ณ์๋ช }}
- ๋ณ์ ๊ฐ์ด HTML์ด๋ ํน์ ๋ฌธ์๋ฅผ ํฌํจํ๋ ๊ฒฝ์ฐ: {{{๋ณ์๋ช }}} (์ด์ค์ผ์ดํ ์ฒ๋ฆฌํ์ง ์์)
- ์กฐ๊ฑด์ :
{{#์กฐ๊ฑด์}} ์กฐ๊ฑด์ด ์ฐธ์ผ ๋ ๋ณด์ฌ์ง๋ ๋ด์ฉ {{/์กฐ๊ฑด์}} {{^์กฐ๊ฑด์}} ์กฐ๊ฑด์ด ๊ฑฐ์ง์ผ ๋ ๋ณด์ฌ์ง๋ ๋ด์ฉ {{/์กฐ๊ฑด์}}
- ๋ฐ๋ณต๋ฌธ :
<ul> {{#๋ฐฐ์ด๋ช }} <li>{{.}}</li> {{/๋ฐฐ์ด๋ช }} </ul>
- ์์ฑ๊ฐ ์ค์ :
<a href="{{๊ฒฝ๋ก/URL}}">๋งํฌ</a> <img src="{{์ด๋ฏธ์ง_๊ฒฝ๋ก}}" alt="์ด๋ฏธ์ง">
- ํ ํ๋ฆฟ ์ธํด๋ฃจ๋ :
{{> ํ ํ๋ฆฟ_ํ์ผ๋ช }}
Groovy ์ธ์ด๋ฅผ ๋ฐํ์ผ๋ก ๋ง๋ค์ด์ง Template Engine. Groovy ์คํฌ๋ฆฝํ ์ธ์ด์ java์ธ์ด๋ฅผ ์ฌ์ฉํ์ฌ ๋์ ์ธ ์ปจํ ์ธ ๋ฅผ ์์ฑํ ์ ์๋ค. java์ ํธํ์ด ๋๊ธฐ ๋๋ฌธ์ java์ ๋ฉ์๋๋ ํด๋์ค๋ฅผ ํธ์ถํ์ฌ ์ฌ์ฉํ ์ ์๋ค. ๋ํ ํ ํ๋ฆฟ์ ์์์ ์ง์ํ์ฌ ์ฌ์ฌ์ฉ์ ์ฉ์ดํ๋ค.
์ฌ์ฉ๋ฒ
1. dependency ์ถ๊ฐ
โป ํ ์คํธ ํ๊ฒฝ : SpringBoot 2.7.12 / IntelliJ IDE / Windows 10
// gradle > build.gradle implementation 'org.codehaus.groovy:groovy-templates:<version>' // maven > pom.xml <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-templates</artifactId> <version>3.0.7</version> </dependency>
2. ViewResolver ์ค์
spring.groovy.template.prefix=/templates/ spring.groovy.template.suffix=.tpl
resources/templates๋ฅผ ๊ธฐ๋ณธ ๊ฒฝ๋ก๋ก ์ค์ ํ ํ์ฅ์๋ฅผ .tpl๋ก ์ค์ ํ๋ค.3. ๋ฉ์๋ ์์ฑ ๋ฐ ํ ํ๋ฆฟ ์์ฑ
๋ณ์ ์ฐธ์กฐ๋ ${๋ณ์๋ช } ์ผ๋ก ๋ณ์ ์ฐธ์กฐํ๋ฉฐ ${age > 18 ? '์ฑ์ธ' : '๋ฏธ์ฑ๋ ์'} ์ ๊ฐ์ ํํ์์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ธ๋ฉ ํ ์ ๋ ์๋ค.
๊ธฐ๋ณธ ๋ฌธ๋ฒ
- ๋ณ์ ์ฐธ์กฐ : ${๋ณ์๋ช }
- ํํ์ : ${ํํ์} ex) ${์กฐ๊ฑด ? '์ฐธ':'๊ฑฐ์ง'}
- ๋ฐ๋ณต๋ฌธ : <% for (์์ : ์ปฌ๋ ์ ) { %> ๋ฐ๋ณต๋ ๋ด์ฉ <% } %>
<% for (item in items) { %> <li>${item}</li> <% } %>
- ์กฐ๊ฑด๋ฌธ : <% if (์กฐ๊ฑด) { %> ๋ํ๋ผ ๊ฐ <% } %>
<% if (age >= 18) { %> <p>์ฑ์ธ</p> <% } else { %> <p>๋ฏธ์ฑ๋ ์</p> <% } %>
- ์ธํด๋ฃจ๋ : header.tpl๊ณผ footer.tpl ํ์ผ์ ๋ด์ฉ์ ํ์ฌ ํ ํ๋ฆฟ์ ์ธํด๋ฃจ๋ ํ๋ค.
<% include "header.tpl" %> <h1>Welcome!</h1> <% include "footer.tpl" %>
Spring Initializr์์ ์ ์ฉ๊ฐ๋ฅํ ๋ํ์ ์ธ ํ ํ๋ฆฟ ์์ง 4๊ฐ์ง์ ๋ํด ์์๋ณด์๋ค. jsp์ ๊ฐ์ ๋ง์ด ์ฌ์ฉ๋๋ ๋ค๋ฅธ ํ ํ๋ฆฟ ์์ง๋ ์์ง๋ง jsp์ ๊ฒฝ์ฐ SpringBoot์์๋ ์ฌ์ฉ์ ๊ถ์ฅํ์ง ์๊ธฐ(์ค์ ํ๋ค๋ฉด ์ฌ์ฉ์ ๊ฐ๋ฅํ๋ค.) ๋๋ฌธ์ ๊ณต์์ ์ผ๋ก ์ฌ์ฉ์ ๊ถ์ฅํ๋ ํ ํ๋ฆฟ ์์ง์ ํ์ธํด๋ดค๋ค. 4๊ฐ์ ํ ํ๋ฆฟ ์์ง์ค ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋๊ฒ์ Thymeleaf๊ฐ ๋ง์ด ์ฌ์ฉ๋์ง๋ง ์ฌ์ฉํ๋ ํ๋ก์ ํธ์ ์ํฉ์ ๋ฐ๋ผ ์ ์ ํ ํ ํ๋ฆฟ ์์ง์ ์ฌ์ฉํ๋๊ฒ์ด ์ข์ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค.