기존의 순수 HTML로 이루어져 있던 프로젝트에 타임리프가 제공하는 입력 폼 기능을 적용한다. (재고 등록 및 수정 폼)

해당 프로젝트의 초기 설정은 다음과 같다.

 

2023.03.10 - [Spring] - 초기 프로젝트 설정 (재고 관리 서비스)

 

th:object  :  커맨드 객체 지정
*{ . . . }  :  선택 변수 식, th:object 에서 선택한 객체에 접근
th:field  :  HTML 태그의 id, name, value 속성을 자동으로 처리
ex) th:field
렌더링 전  -  <input type="text" th:field="*{itemName}" />
렌더링 후  -  <input type="text" id="itemName" name="itemName" th:value="*{itemName}" />

 

 

등록 Form

  • th:object 적용을 위해 먼저 해당 오브젝트 정보를 넘겨주어야 함
  • 등록 폼이기 때문에 데이터가 비어있는 빈 오브젝트 item을 만들어 뷰에 전달 

등록 Controller

@GetMapping("/add")
public String addForm(Model model) {
	model.addAttribute("item", new Item());
	return "form/addForm";
}

 

등록 Form.HTML

<form action="item.html" th:action th:object="${item}" method="post">
	<div>
		<label for="itemName">상품명</label>
		<input type="text" id="itemName" th:field="*{itemName}" class="formcontrol" placeholder="이름을 입력하세요">
	</div>
 
	<div>
		<label for="price">가격</label>
		<input type="text" id="price" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요">
	</div>

	<div>
		<label for="quantity">수량</label>
		<input type="text" id="quantity" th:field="*{quantity}" class="formcontrol" placeholder="수량을 입력하세요">
	</div>

 

  • th:object="${item}"  :  <form> 에서 사용할 객체 지정, 선택 변수 식 ${ . . . } 적용 가능
  • th:field="*{itemName}"
    -  위 코드에서 사용한 선택 변수 식 *{item}${item.itemName} 과 같음 ( th:objectitem을 선택했기 때문에 선택 변수 식 적용 가능 )
    th:field 는 앞선 설명과 같이 id, name, value 속성을 모두 자동으로 등록
        -  id : th:field 에서 지정한 변수 이름과 같음 ( id="itemName" )
        -  name : th:field 에서 지정한 변수 이름과 같음 ( name="itemName" )
        -  value : th:field 에서 지정한 변수의 값 사용 ( value="" )

 

'Thymeleaf' 카테고리의 다른 글

[Thymeleaf] 타임리프 스프링 통합  (0) 2023.03.10
[Thymeleaf] 템플릿 레이아웃  (0) 2023.03.09
[Thymeleaf] 템플릿 조각  (0) 2023.02.01
[Thymeleaf] 자바스크립트 인라인  (0) 2023.01.31
[Thymeleaf] 블록  (0) 2023.01.30

타임리프는 스프링을 사용하지 않는 환경에서도 활용  가능하지만, 스프링과 통합하여 사용하면 좀 더 생산성 있는 개발을 할 수 있는 여러 기능을 제공한다. 

 

스프링 통합으로 추가되는 기능들

  • 스프링 SpringEL 문법 통합
  • 스프링 검증, 오류 처리 통합
  • 스프링 변환 서비스 통합
  • 스프링 메시지, 국제화 기능의 편리한 통합
  • ${@myBean.doSomething()} 과 같은 스프링 빈 호출 지원
  • 편린한 폼 관리를 위한 추가 속성
    - th:object : 기능 강화, 폼 커맨드 객체  선택
    - th:field, th:errors, th:errorclass
  • 폼 컴포넌트 기능
    - checkbox, radio button, List 등을 편리하게 사용

 

 

타임리프 사용을 위한 설정

- 타임리프를 스프링에 적용하기 위해서는 타임리프 템플릿 엔진을 스프링 빈에 등록하고, 타임리프용 뷰 리졸버를 스프링 빈으로 등록 필요

- 하지만 스프링 부트에서는 아래와 같은 코드를 등록해줌으로서 build.gradle을 통해 이러한 과정을 자동으로 진행

- Gradle은 타임리프 통합을 위한 관련 추가 라이브러리를 다운로드하고, 설정을 위한 스프링 빈을 등록하는 등의 과정 진행

 

build.gradle

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

 

타임리프 메뉴얼
-  기본 메뉴얼 : https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
-  스프링 통합 메뉴얼 : https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html
-  그 외 스프링 부트 타임리프 설정 :
https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-applicationproperties.html#common-application-properties-templating

 

'Thymeleaf' 카테고리의 다른 글

[Thymeleaf] 입력 폼 처리  (0) 2023.03.10
[Thymeleaf] 템플릿 레이아웃  (0) 2023.03.09
[Thymeleaf] 템플릿 조각  (0) 2023.02.01
[Thymeleaf] 자바스크립트 인라인  (0) 2023.01.31
[Thymeleaf] 블록  (0) 2023.01.30

템플릿 레이아웃은 기존의 템플릿 조각에서 코드의 일부 조각을 가져와 사용했던 것을 확장하여, 레이아웃에 넘겨 사용하는 것이다.

 

그중 하나의 예로 <head> 태그에 공통으로 사용을 희망하는 css나 javascript 등의 속성 및 정보들을 한곳에 모아두고 사용하며, 페이지에 따라 필요로 하는 추가적인 정보 사용을 위해 다음과 같이 설정한다.

 

 

1. 일부 - <head>

Controller

@GetMapping("/layout")
public String layout() {
	return "template/layout/layoutMain";
}

 

Layout.HTML

<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="common_header(title,links)">

	<title th:replace="${title}">레이아웃 타이틀</title>

	<!-- 공통 -->
	<link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}">
	<link rel="shortcut icon" th:href="@{/images/favicon.ico}">
	<script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"></script>
 
 	<!-- 추가 -->
	<th:block th:replace="${links}" />
    
</head>

 

Main.HTML

<head th:replace="template/layout/base :: common_header(~{::title},~{::link})">
	<title>메인 타이틀</title>
	<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
	<link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">
</head>

 

결과

<head>
	<title>메인 타이틀</title>
    
	<!-- 공통 -->
	<link rel="stylesheet" type="text/css" media="all" href="/css/awesomeapp.css">
	<link rel="shortcut icon" href="/images/favicon.ico">
	<script type="text/javascript" src="/sh/scripts/codebase.js"></script>
    
	<!-- 추가 -->
	<link rel="stylesheet" href="/css/bootstrap.min.css">
	<link rel="stylesheet" href="/themes/smoothness/jquery-ui.css">
</head>

 

th:replace="template/layout/base :: common_header(~{::title},~{::link})
  • ~{::title} : 현재 페이지의 <title> 태그 전달
  • ~{::link} : 현재 페이지의 <link> 태그 전달
※ 결과
  -  공통 파트의 유지
  -  추가 파트에 layout.html에서 전달된 태그들이 전달되어 추가

 

 

 

2. 전체 <html>

Controller

@GetMapping("/layoutExtend")
public String layoutExtends() {
	return "template/layoutExtend/layoutExtendMain";
}

 

Layout.HTML

<!DOCTYPE html>
<html th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org">
<head>
	<title th:replace="${title}">레이아웃 타이틀</title>
</head>
<body>
<h1>레이아웃 H1</h1>
<div th:replace="${content}">
	<p>레이아웃 컨텐츠</p>
</div>
<footer>
 레이아웃 푸터
</footer>
</body>
</html>

 

Main.HTML

<!DOCTYPE html>
<html th:replace="~{template/layoutExtend/layoutFile :: layout(~{::title}, ~{::section})}"
 xmlns:th="http://www.thymeleaf.org">
<head>
	<title>메인 페이지 타이틀</title>
</head>
<body>
<section>
	<p>메인 페이지 컨텐츠</p>
	<div>메인 페이지 포함 내용</div>
</section>
</body>
</html>

 

결과

<!DOCTYPE html>
<html>
<head>
<title>메인 페이지 타이틀</title>
</head>
<body>
<h1>레이아웃 H1</h1>
<section>
<p>메인 페이지 컨텐츠</p>
<div>메인 페이지 포함 내용</div>
</section>
<footer>
레이아웃 푸터
</footer>
</body>
</html>

 

※ 결과
  -  layout.html의 <html> 태그에 th:fragment 속성 정의
  -  main.html의 <html> 자체를 th:replace 를 사용하여 필요 내용을 부분적으로 전달하며 사용

 

 

 

 

 

 

'Thymeleaf' 카테고리의 다른 글

[Thymeleaf] 입력 폼 처리  (0) 2023.03.10
[Thymeleaf] 타임리프 스프링 통합  (0) 2023.03.10
[Thymeleaf] 템플릿 조각  (0) 2023.02.01
[Thymeleaf] 자바스크립트 인라인  (0) 2023.01.31
[Thymeleaf] 블록  (0) 2023.01.30

웹 페이지 개발 시, 상하단 영역이나 좌측 카테고리 등 여러 페이지에 걸쳐 함께 사용하는 공통 영역이 많이 있다. 이러한 부분들을 하나씩 코드를 복사하여 개발한다면, 향후 수정 시에 각 페이지마다 수정하는 시간 소모를 필요로 한다. 이것은 상당히 비효율적이며, 해당 문제를 해결하기 위해 타임리프의 템플릿 조각과 레이아웃 기능 사용한다.

 

 

Controller

@Controller
@RequestMapping("/template")
public class TemplateController {
	@GetMapping("/fragment")
	public String template() {
	return "template/fragment/fragmentMain";
	}
}

 

Footer

<footer th:fragment="copy">
 푸터 자리 입니다.
</footer>

<footer th:fragment="copyParam (param1, param2)">
	<p>파라미터 자리 입니다.</p>
	<p th:text="${param1}"></p>
	<p th:text="${param2}"></p>
</footer>

th:fragment 가 있는 태그는 다른 곳에 포함되는 코드 조각의 개념으로 이해

 

 

1. HTML (부분 포함 insert)

<h2>부분 포함 insert</h2>
<div th:insert="~{template/fragment/footer :: copy}"></div>

1. 결과 (부분 포함 insert)

<h2>부분 포함 insert</h2>
<div>
<footer>
푸터 자리 입니다.
</footer>
</div>
<div th:insert="~{template/fragment/footer :: copy}"></div>
  • th:insert 를 사용하여 현재 태그 <div> 내부에 추가

 

 

 

2. HTML (부분 포함 replace)

<h2>부분 포함 replace</h2>
<div th:replace="~{template/fragment/footer :: copy}"></div>

2. 결과 (부분 포함 replace)

<h2>부분 포함 replace</h2>
<footer>
푸터 자리 입니다.
</footer>
<div th:replace="~{template/fragment/footer :: copy}"></div>
  • th:replace 를 사용하면 현재 태그 <div> 를 대체

 

 

 

3. HTML (부분 포함 단순 표현식)

<h2>부분 포함 단순 표현식</h2>
<div th:replace="template/fragment/footer :: copy"></div>

3. 결과 (부분 포함 단순 표현식)

<h2>부분 포함 단순 표현식</h2>
<footer>
푸터 자리 입니다.
</footer>
<div th:replace="template/fragment/footer :: copy"></div>
  • ~{ . . . } 사용이 원칙이지만, 템플릿 조각을 사용하는 코드가 단순하면 해당 부분을 생략해도 무관

 

 

4. HTML (파라미터 사용)

<h1>파라미터 사용</h1>
<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></div>

4. 결과 (파라미터 사용)

<h1>파라미터 사용</h1>
<footer>
<p>파라미터 자리 입니다.</p>
<p>데이터1</p>
<p>데이터2</p>
</footer>
<div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></ div>
  • 다음 코드와 같이 파라미터 전달을 통해 조각 렌더링 가능

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Thymeleaf' 카테고리의 다른 글

[Thymeleaf] 타임리프 스프링 통합  (0) 2023.03.10
[Thymeleaf] 템플릿 레이아웃  (0) 2023.03.09
[Thymeleaf] 자바스크립트 인라인  (0) 2023.01.31
[Thymeleaf] 블록  (0) 2023.01.30
[Thymeleaf] 주석  (0) 2023.01.29

자바스크립트에서 타임리프 사용을 위해, 다음과 같은 자바스크립트 인라인 기능을 활용

<script th:inline="javascript">

 

Controller

@GetMapping("/javascript")
public String javascript(Model model) {
	model.addAttribute("user", new User("userA", 10));
	addUsers(model);
	return "basic/javascript";
}

 

1. HTML (자바스크립트 인라인 사용 전)

<!-- 자바스크립트 인라인 사용 전 -->
<script>
 var username = [[${user.username}]];
 var age = [[${user.age}]];
 //자바스크립트 내추럴 템플릿
 var username2 = /*[[${user.username}]]*/ "test username";
 //객체
 var user = [[${user}]];
</script>

 

1. 결과 (자바스크립트 인라인 사용 전)

<script>
	var username = userA;
	var age = 10;
	//자바스크립트 내추럴 템플릿
	var username2 = /*userA*/ "test username";
	//객체
	var user = BasicController.User(username=userA, age=10);
</script>

 

2. HTML (자바스크립트 인라인 사용 후)

<!-- 자바스크립트 인라인 사용 후 -->
<script th:inline="javascript">
	var username = [[${user.username}]];
	var age = [[${user.age}]];
	//자바스크립트 내추럴 템플릿
	var username2 = /*[[${user.username}]]*/ "test username";
	//객체
	var user = [[${user}]];
</script>

 

2. 결과 (자바스크립트 인라인 사용 후)

<script>
	var username = "userA";
	var age = 10;
	//자바스크립트 내추럴 템플릿
	var username2 = "userA";
	//객체
	var user = {"username":"userA","age":10};
</script>

 

 

텍스트 렌더링

var username = [ [ ${user.username} ] ];
    → 인라인 사용 전 : var uesrname = userA;
    → 인라인 사용 후 : var username = "userA";    
  • 인라인 사용 전의 경우 userA 라는 변수 명이 그대로 남음
  • 타임리프 입장에서는 정확하게 렌더링이 진행된 것이 맞음
  • 여기서 인라인을 사용하여 기존의 변수 명에 " ... " 을 포함하여 문자로 변환
  • 해당 과정에서 문제가 야기될만한 특수 문자가 있다면, 이스케이프 처리를 추가로 진행 ( " → \" )

 

자바스크립트 내추럴 템플릿

var username2 = /* [ [ ${user.username} ] ] */  "test username" ;
    → 인라인 사용 전 : var uesrname2 = /* userA */  "test username";
    → 인라인 사용 후 : var username2 = "userA";    
  • 타임리프는 HTML 파일을 직접 열어도 동작하는 내추럴 템플릿 기능 제공
  • 인라인 기능 사용 시, 주석을 활용하여 해당 기능 사용 가능
  • 인라인 사용 전의 경우 내추럴 템플릿 기능이 동작하지 않았고, 렌더링 내용 또한 주석 처리
  • 여기서 인라인 기능을 사용하여 주석 부분이 제거되고, 기댓값인 "userA" 적용

 

객체

var user = [ [ ${user} ] ];
    → 인라인 사용 전 : var user = BasicController.User(username=userA, age=10);
    → 인라인 사용 후 : var user = { "username" : "userA", "age : 10 };
  • 인라인 기능을 통해 객체를 JSON으로 자동 변환
  • 인라인 사용 전의 경우 toString() 의 결과를 출력
  • 인라인 사용을 통해 JSON으로 변환된 것을 확인

 

 

'Thymeleaf' 카테고리의 다른 글

[Thymeleaf] 템플릿 레이아웃  (0) 2023.03.09
[Thymeleaf] 템플릿 조각  (0) 2023.02.01
[Thymeleaf] 블록  (0) 2023.01.30
[Thymeleaf] 주석  (0) 2023.01.29
[Thymeleaf] 조건부 평가  (0) 2023.01.01

HTML 태그가 아닌, 타임리프의 유일한 자체 태그 'th:block'

<th:block>

 

Controller

@GetMapping("/block")
public String block(Model model) {
	addUsers(model);
	return "basic/block";
}

 

HTML

<th:block th:each="user : ${users}">
	<div>
	사용자 이름1 <span th:text="${user.username}"></span>
	사용자 나이1 <span th:text="${user.age}"></span>
	</div>
	
    <div>
	요약 <span th:text="${user.username} + ' / ' + ${user.age}"></span>
	</div>
</th:block>

 

결과

<div>
	사용자 이름1 <span>userA</span>
	사용자 나이1 <span>10</span>
</div>

<div>
	요약 <span>userA / 10</span>
</div>

<div>
	사용자 이름1 <span>userB</span>
	사용자 나이1 <span>20</span>
</div>

<div>
	요약 <span>userB / 20</span>
</div>

<div>
	사용자 이름1 <span>userC</span>
    사용자 나이1 <span>30</span>
</div>

<div>
	요약 <span>userC / 30</span>
</div>

 

블록

  • HTML 태그 속에 기능을 정의하여 사용하는 타임리프의 특성상, 다음과 같은 일부 상황에 맞게 사용하기 용이
  • <th:block> 은 렌더링 시 제거

'Thymeleaf' 카테고리의 다른 글

[Thymeleaf] 템플릿 조각  (0) 2023.02.01
[Thymeleaf] 자바스크립트 인라인  (0) 2023.01.31
[Thymeleaf] 주석  (0) 2023.01.29
[Thymeleaf] 조건부 평가  (0) 2023.01.01
[Thymeleaf] 반복  (0) 2022.12.31

 

Controller

@GetMapping("/comments")
public String comments(Model model) {
	model.addAttribute("data", "Spring!");
	return "basic/comments";
}

 

1. HTML (표준 HTML 주석)

<h1>예시</h1>
<span th:text="${data}">html data</span>

<h1>1. 표준 HTML 주석</h1>
<!--
<span th:text="${data}">html data</span>
-->

 

1. 결과 (표준 HTML 주석)

<h1>예시</h1>
<span>Spring!</span>
<h1>1. 표준 HTML 주석</h1>
<!--
<span th:text="${data}">html data</span>
-->

 

1. 표준 HTML 주석

  • 타임피르가 렌더링 하지 않고, 그대로 출력

 

 

2. HTML (타임리프 파서 주석)

<h1>2. 타임리프 파서 주석</h1>
<!--/* [[${data}]] */-->
<!--/*-->
<span th:text="${data}">html data</span>
<!--*/-->

 

2. 결과 (타임리프 파서 주석)

<h1>2. 타임리프 파서 주석</h1>

 

2. 타임리프 파서 주석

  • 렌더링에서 주석 부분을 제거

 

 

3. HTML (타임리프 프로토타입 주석)

<h1>3. 타임리프 프로토타입 주석</h1>
<!--/*/
<span th:text="${data}">html data</span>
/*/-->

 

3. 결과 (타임리프 프로토타입 주석)

<h1>3. 타임리프 프로토타입 주석</h1>
<span>Spring!</span>

 

3. 타임리프 프로토타입 주석

  • HTML 파일을 웹 브라우저에서 직접 열어보면,  HTML 주석에 의해 해당 파트를 웹 브라우저가 렌더링하지 않음
  • 타임리프 렌더링을 거치면 해당 파트가 정상 렌더링

 

'Thymeleaf' 카테고리의 다른 글

[Thymeleaf] 자바스크립트 인라인  (0) 2023.01.31
[Thymeleaf] 블록  (0) 2023.01.30
[Thymeleaf] 조건부 평가  (0) 2023.01.01
[Thymeleaf] 반복  (0) 2022.12.31
[Thymeleaf] 속성 값 설정  (0) 2022.12.28

Thymeleaf의 조건식에는 th:if="${...}" 와 그 반대인 th:unless="${...}" 가 있다.

th:if = "${ . . . }"
th:unless = "${ . . . }"

 

 

Controller

@GetMapping("/condition")
public String condition(Model model) {
    addUsers(model);
    return "basic/condition";
}

 

1. .HTML (if, unless)

<table border="1">
 <tr>
 <th>count</th>
 <th>username</th>
 <th>age</th>
 </tr>
 <tr th:each="user, userStat : ${users}">
 <td th:text="${userStat.count}">1</td>
 <td th:text="${user.username}">username</td>
 <td>
 <span th:text="${user.age}">0</span>
 <span th:text="'미성년자'" th:if="${user.age lt 20}"></span>
 <span th:text="'미성년자'" th:unless="${user.age ge 20}"></span>
 </td>
 </tr>
</table>

 

1. 결과 (if, unless)

if, unless

  • 타임리프에서는 주어진 조건이 부합할 시 태그를 렌더링하지 않음
  • 아래와 같이 조건의 결과가 false 인 경우 <span> . . . <span> 부분에 대한 렌더링 진행 X
<span th:text=" '미성년자' " th:if=" ${user.age lt 20} "> </span>

 

 

2. .HTML (Switch)

<table border="1">
 <tr>
 <th>count</th>
 <th>username</th>
 <th>age</th>
 </tr>
 <tr th:each="user, userStat : ${users}">
 <td th:text="${userStat.count}">1</td>
 <td th:text="${user.username}">username</td>
 <td th:switch="${user.age}">
 <span th:case="10">10살</span>
 <span th:case="20">20살</span>
 <span th:case="*">기타</span>
 </td>
 </tr>
</table>

 

 

2. 결과 (Switch)

Switch

  • * : 만족 조건이 없을 시 사용되는 기본값

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Thymeleaf' 카테고리의 다른 글

[Thymeleaf] 블록  (0) 2023.01.30
[Thymeleaf] 주석  (0) 2023.01.29
[Thymeleaf] 반복  (0) 2022.12.31
[Thymeleaf] 속성 값 설정  (0) 2022.12.28
[Thymeleaf] URL 링크  (0) 2022.12.27

+ Recent posts