백엔드/스프링 부트로 게시판 만들기

블로그 화면 구성하기 - 타임리프1 (기초)

study_yeon 2023. 8. 7. 17:54

2023.08.01 ~ 08.03 수업

교재 : 스프링부트3 백엔드 개발자 되기

 

jsp을 사용하여 개발하는 방법 : 모델1
서블릿을 사용하여 개발하는 방법 : 모델2
스프링을 사용하여 개발하는 방법 : MVC


▶ 타임리프 (템플릿 엔진)

▷ 템플릿 엔진이란?
- 스프링 서버에서 데이터를 받아 웹 페이지(HTML)상에 데이터를 넣어 보여주는 도구
- 지정된 템플릿 양식과 데이터가 합쳐져 HTML 문서를 출력하는 소프트웨어
- 웹사이트 화면을 어떤 형태로 만들지 도와주는 양식
- HTML과 템플릿엔진 문법(th:속성)을 섞어 사용

-> 보통 jquery를 사용하거나 javascript를 사용하여 HTML에 가공된 데이터를 보여주는 것이 정석이다.
- 쉬운 표현으로 서버에서 받아온 데이터를 효과적으로 보여줄 중간 매개체인 템플릿 엔진을 사용한다
- 템플릿 엔진을 사용하면 비교적 간단한 표현 (조건문, 변수, 반복문) 을 통해 효과적으로 데이터를 가공하여 웹 페이지를 보여줄 수 있다

○ 템플릿 엔진 종류 
Mustache : 로직이 없다, EL, {{}}
타임리프 : 엣 기호로 로직을 준다, EL + JSTL, ${}
JSP : 비즈니스 로직, EL + JSTL + 서블릿태그, ${}, 스프링부트에서 사용을 권고하지 않음

프리마커 


※ 스프링은 타임리프를 권고하기 때문에 타임리프를 사용할 것(JSP에서 로직이 없는 ver)
(참고) 머스타치로는 화면에 여러개의 글 구현(반복문) 불가
jsp는 서블릿의 도움 없이 자바코드 마음껏 사용


▷ 타임리프의 표현식
${ }     변수의 값
#{ }     속성 파일 값
@{ }    URL 표현식
*{ }      선택한 변수의 표현식. th:object에서 선택한 객체에 접근
-> {...}는 " "로 묶어주기 "${ }"

 

* 서버에서 받아온 데이터를 ${ } 등을 이용하여 표기

▷ 타임리프의 문법
th:text          텍스트를 표현할 때 사용

th:utext        태그를 태그로 인식할 때 사용
th:each        컬렉션을 반복할 때 사용
th:if              조건이 true일 때만 표시
th:unless     조건이 false일 때만 표시
th:href         이동경로

th:block       여러 태그를 반복할 경우 사용
th:with         변수값으로 지정
th:object      선택한 객체로 지정

 

※ 약식으로 대체하기

* 문자로 인식
기본식
th:text="${ }"
대체식 
[[ ${ } ]]

* 태그로 인식 (CSS 적용 등)
기본식
th:utext="${ }"
대체식 
[( ${ } )]

 

* th:text=${ } : 타임리프가(서버가) 만든다
- <div>Hi</div>

- <div th:text="' Hi '"></div>
->Hi자리를 대체하는 것

 

※ 타임리프와 궁합이 좋은 확장프로그램
크롬웹스토어 들어가서 검색하여 추가하기
LiveReload++
- 화면(View)에 변화를 주면, 자동으로 리빌드하고 브라우저에서 리로드
- 프로젝트를 Buile/Restart 해주면 열려있는 웹페이지에 변경 내용이 자동으로 적용되는 확장 프로그램

* 사용하는 법
상단에 고정하고 누르면 초록색 체크가 생김
체크를 하면 한번은 새로 실행해주기

실행


▶ 타임리프 설정하기

 

▷ Spring Stater Project 만들기
name : HiThymeleaf
group : net.choongang.ewha
artifact : hithymeleaf
package : net.choongang.ewha.hithymeleaf

 

* 생성 후 settings.gradle의 내용이 프로젝트 이름과 같은지 비교하기

 

(참고) war는 외장
스프링부트는 내장 was를 사용한다. main으로부터 실행이 된다


▷ 의존성 추가(build.gradle)
- 타임리프 사용을 위한 라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
-> 프로젝트 만들때 설정해주기(자동 작성)
-> snippets에 저장하기

 

▷ HTML파일에 코드 추가

- 타임리프를 적용할 HTML문서에 네임스페이스 추가
xmlns:th="http://www.thymleaf.org" 
-> 타임리프의 th속성을 사용하기 위해 선언된 네임스페이스


xmlns="" : url에 따라서 해석
(xml name space의 약어)

 

(참고)
HTML : Hyper Text Markup Language
XML  : eXtensible Markup Language
SGML : Standard Generalized Language

 

▷ Help > 이클립스 마켓 > 타임리프 검색하여 설치하기

trust selected


▷ 작동확인

* 시작페이지 만들기
HiThymeleaf/src/main/resources/static에
index.html
-> index가 보이면 서버에러가 아님을 확인 가능

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>시작페이지</h3>
</body>
</html>

▶ 문법 실습

- 순수 HTML페이지에 'th:text' 등의 속성이 발견되면 타임리프 템플릿 기능이 추가된다
- 평상시에 순수 HTML페이지로 작동하다가 'th:...' 속성이 발견되면 서버쪽 뷰 템플릿기능을 추가하는 식으로 동작
->  사이에 작성되던 문구가 'th:...' 속성에서 나타남

 

▷ User객체 만들기

- 문법 실습에 활용하기 위한 객체 

- 컨트롤러에서 User타입의 user객체를 전송하면 타임리프로는 ${ user }로 객체를 받는다
패키지 : net.choongang.ewha.hithymeleaf.domain
클래스 : User 

package net.choongang.ewha.hithymeleaf.domain;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
public class User {
	private String name;
	private int age;
}


▷ 타임리프 문법을 연습하기위한 컨트롤러와 html파일 만들기

 

* html 

   경로 : HiThymeleaf/src/main/resources/templates/pages/greeting

   파일명 : hiPage.html

* 컨트롤러 

   패키지 : net.choongang.ewha.hithymeleaf.controller

   클래스 : HiController


▷1. 텍스트 관련

 

1) th:text 

- 텍스트를 표현할 때 사용

- ${ username }이 컨트롤러에서 전송한 값(홍길동)으로 바뀌어 화면에 출력됨 

- 약식 활용 : [[${ username }]]

-  태그를 String으로 인식

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<!-- 1. 뷰템플릿에 데이터 전달 -->
	<h1 th:text="'Hi ' + ${ username }"></h1>
	<h2 th:text="${ username }"></h2>
	<h3 th:text="${ username }"></h3>
	<h4 th:text="${ username }"></h4>
	<h5 th:text="${ username }"></h5>
	<h6 th:text="${ username }"></h6>
	
	<!--/* th:text => [[ ]] 으로 대체 */-->
	<p>
	<h6>[[${ username }]]</h6>
	<h5>[[${ username }]]</h5>
	<h4>[[${ username }]]</h4>
	<h3>[[${ username }]]</h3>
	<h2>[[${ username }]]</h2>
	<h1>[[${ username }]]</h1>
	<hr>

</body>
</html>

* Model : 모델 객체는 뷰(HTML) 쪽으로 값을 넘겨주는 객체

- 인자로 선언하기만 하면 스프링이 알아서 객체를 생성해줌

- addAttribute()메서드로 모델에 값을 저장

- username이라는 로 홍길동이라는 을 받음 

- 컨트롤러는 모델을 통해 데이터를 설정하고, 모델은 뷰로 이 데이터를 전달해 키에 맞는 데이터를 뷰에서 조회  

package net.choongang.ewha.hithymeleaf.controller;

// 뷰 템플릿(HTML코드를 서버에서 생성(SSR)하는 컨트롤러
@Controller
@Slf4j
public class HiController {
	// 라우터 매핑 메소드
	@GetMapping("/hi")
	public String hello(Model model) {
		// 1. 뷰템플릿에 데이터 전달
		model.addAttribute("username", "홍길동");
}

약식표현으로도 동일하게 출력

 

1-2) 객체 받기

- 컨트롤러에서 User타입의 user객체를 전송하면 타임리프로는 ${ user }로 객체를 받는다

- user의 name가져오기

 

[객체 가져오는 3가지 방법]

th:text="${객체명.멤버변수}"
th:text="${객체명.get속성메소드()}" 
th:text="${객체['속성명']}"

	<!-- 2. 객체 전달 -->
	<div th:text="${ user.name }"></div>
	<div th:text="${ user.getName() }"></div>
	<div th:text="${ user['name'] }"></div>
	<!-- 약식 -->
	<p>
	<div>[[ ${ user.name } ]]</div>
	<div>[[ ${ user.getName() } ]]</div>
	<div>[[ ${ user['name'] } ]]</div>	
	<hr>

- User.java로 객체 생성하여 활용하기

package net.choongang.ewha.hithymeleaf.controller;

// 뷰 템플릿(HTML코드를 서버에서 생성(SSR)하는 컨트롤러
@Controller
@Slf4j
public class HiController {
	// 라우터 매핑 메소드
	@GetMapping("/hi")
	public String hello(Model model) {
	// 2. User객체 생성
		User user = new User("황진이", 20);
		// user객체를 model에 추가
		model.addAttribute(user);

1-3) 날짜 출력(#temporals : 타임리프 내장객체)

- 컨트롤러에서 전송받은 날짜값을 타임리프 내장객체를 활용하여 받기

- year(년도), month(월), day(일), hour(시)

- dayOfWeek : 요일을 수로 리턴(일요일 : 1 ~ 토요일 : 7)

	<!-- 기존 방식 :localDateTime -->
	<div th:text="${ localDateTime }"></div>
	<!-- 약식 -->
	<div>[[ ${localDateTime} ]]</div>
	
	<!--/* #temporals : 타임리프 내장객체 */-->
	<div th:text="${ #temporals.format(localDateTime, 'yyyy-MM-dd HH:mm:ss')}"></div>
	<div th:text="${ #temporals.year(localDateTime) }"></div>
	<div th:text="${ #temporals.month(localDateTime) }"></div>
	<div th:text="${ #temporals.day(localDateTime) }"></div>
	<div th:text="${ #temporals.dayOfWeek(localDateTime) }"></div>
	<div th:text="${ #temporals.hour(localDateTime) }"></div>
	<!-- 약식 & css 적용 -->
	<p>
	<div style="" th:style="${ 'color:red; border: 2px solid blue; width:5%;' }">
		[[ ${#temporals.year(localDateTime)} ]]
	</div>
	<div style="background-color: orange; width: 5%;">
		[[ ${#temporals.month(localDateTime)} ]]
	</div>
	<div>[[ ${#temporals.day(localDateTime)} ]]</div>
	<div>[[ ${#temporals.dayOfWeek(localDateTime)} ]]</div>
	<div>[[ ${#temporals.hour(localDateTime)} ]]</div>
	<hr>

- 컨트롤러에서 LocalDateTime.now();를 사용하여 HTML에 날짜 값 전송

package net.choongang.ewha.hithymeleaf.controller;

// 뷰 템플릿(HTML코드를 서버에서 생성(SSR)하는 컨트롤러
@Controller
@Slf4j
public class HiController {
	// 라우터 매핑 메소드
	@GetMapping("/hi")
	public String hello(Model model) {
		// 날짜 자료형 다루기
		LocalDateTime localDateTime = LocalDateTime.now();
		model.addAttribute("localDateTime", localDateTime);
		
		return "pages/greeting/hiPage.html";
						
	}

}

 

1-4) 연산기능

- HTML페이지에서 바로 사용 가능

- 산술연산 : +, -, *, /, %
- 대소연산, 비교연산 : <, >, <= lt, >= gt, != ... 
- 문자열 : ' ', 만일 속성의 따옴표가 작은따옴표면 큰따옴표로 묶어주기

- 문자열 사용시에는 따옴표 사용 필수

-> 숫자를 " ' ' "으로 묶어준다면 문자열로 인식
-> 공백이 허용됨(문자열로 출력하고 싶다면 " ' ' "을 권고)

* 약식형 내부에 따옴표를 사용하면 문자열로 인식을 함( " ' ' " 를 사용하지 않아도 됨)

	<div th:text="100 + 200"></div>
	<div th:text='100 + 200'></div>
	<div th:text="'100 + 200'"></div>
	<div th:text=" 문자열입니다. "></div>
	<div th:text=' 문자열입니다. '></div>
	<div th:text="' 문자열 입니다. '"></div>
	<!-- 약식 -->
	<!--/* 
		약식형안에 따옴표를 사용하면 문자열이 된다
	*/-->
	<p>
	<div>[[ ${ 100 + 200 } ]]</div>
	<div>[[ ${ 100 + 200 } ]]</div>
	<div>[[ ${ "100 + 200" } ]]</div>
	<div>[[ ${" 문자열입니다. "} ]]</div>
	<div>[[ ${' 문자열입니다. '} ]]</div>
	<div>[[ ${"' 문자열 입니다. '"} ]]</div>
	<hr>

1-5) 리터럴( || )

- " "로 묶여있는 문자열
- 메모리상에 임의의 위치에 고정된 문자열의 값으로 하드코딩 됨
- | ... |를 사용, 따옴표와 '+연산자'(연결연산자)를 사용하지 않아도 되어 간결(따옴표의 역할)


* 상수문자열(const literal)과 표현식 동시에 출력하기

	<!-- 기존 방식 -->
	<div th:text="'hello ' + ${ user.name } + '!' "></div>
	<!-- 리터럴 활용 -->
	<div th:text="|hello ${ user.name }!|"></div>
	<!-- 약식 -->
	<p>
	<div>[[ ${ 'hello '+ user.name + '!' } ]]</div>
	<div>[[ |hello ${ user.name } !| ]]</div>
	<br>

- 컨트롤러에 있는 1-2)에서 만든 객체 활용하기

package net.choongang.ewha.hithymeleaf.controller;

// 뷰 템플릿(HTML코드를 서버에서 생성(SSR)하는 컨트롤러
@Controller
@Slf4j
public class HiController {
	// 라우터 매핑 메소드
	@GetMapping("/hi")
	public String hello(Model model) {	
		// User객체 생성
		User user = new User("황진이", 20);
		// user객체를 model에 추가
		model.addAttribute(user);

}


2) th:utext

- 태그를 태그로 인식할 때 사용

- css를 적용하는 등에 활용

- th:text 를 사용하면 태그를 String으로 인식하기 때문에 th:utext 활용

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<!-- 2. 자막자리(element 속성)에 텍스트 출력하기 - HTML태그 적용 가능 -->
	<h1 th:utext="${ username2 }"></h1>
	<h2 th:utext="${ username2 }"></h2>
	<h3 th:utext="${ username2 }"></h3>
	<h4 th:utext="${ username2 }"></h4>
	<h5 th:utext="${ username2 }"></h5>
	<h6 th:utext="${ username2 }"></h6>
	<!--/* th:utext => [()] 으로 대체 */-->
	<p>
	<h1>[( ${ username2 } )]</h1>
	<h2>[( ${ username2 } )]</h2>
	<h3>[( ${ username2 } )]</h3>
	<h4>[( ${ username2 } )]</h4>
	<h5>[( ${ username2 } )]</h5>
	<h6>[( ${ username2 } )]</h6>
	<hr>	
</body>
</html>

- username2이라는 로 심청이이라는 을 받음 

- 값에 css style 적용하기

package net.choongang.ewha.hithymeleaf.controller;

// 뷰 템플릿(HTML코드를 서버에서 생성(SSR)하는 컨트롤러
@Controller
@Slf4j
public class HiController {
	// 라우터 매핑 메소드
	@GetMapping("/hi")
	public String hello(Model model) {
		// 2. 뷰템플릿에 화면 데이터 전달
		model.addAttribute("username2", 
			"<i style='color: orange; border-bottom: 3px solid blue;'>심청이</i>");
}

css 적용, 약식표현으로도 동일하게 출력


▷ 2. URL이동

1) th:href

- a태그 작성법 : th:href="@{}" 을 이용

<!-- 특정 url로 이동 -->
<a th:href="@{https://naver.com}">네이버</a>

* k1=${ k1 } 
-> k1 : 변수, ${ k } : 실제 값
- path variable : 변수로 작용할 수 있다
-> 컨트롤러에서 배웠음 /api/articles/{id}

* list(pid=100, page=3)
? => (쿼리파라미터)로 둘러싼다
& => ,로 바꾼다.

* 경로변수(/path/{id})식으로 경로 마지막 값이 변수인 경우)를 표현하는 법
예: list()/{p1}/{p2} : p1, p2...값이 변수이고 p2도 값이 변하는 변수라면 컨트롤러에서
라우터 매핑 메소드와 똑같은 표현법 사용
@{서비스명/{변수명1}/{변수명2} (변수명1=값1, 변수명2=값2) } 
=> 실제 HTML코드 URL로 바꾸면 '/서비스명/값1/값2'

	<!--Parameter 추가한 URL 이동  -->
	<!-- /* th:href 이용 */ -->
	<a th:href="@{/main}">메인으로</a>
	<br>
	<!-- 주소에 쿼리스트링(서비스명?키1=값1&키2=값2) -->
	<a th:href="@{ /main(k1=${k1}, k2=${k2}) }">파라미터로 전송</a>
	<br>
	<a th:href="@{ /list(pid=100, page=3) }">리스트 서비스의 pid=100이고 page=3인 내용</a>
	<br>
	<a href="@{/service/{p1}/{p2} (p1=${p1}, (p2=${p2})}">path variable</a>


* 새로운 파일에 시작 : viewLogic.html 만들기
* 컨트롤러에서 매핑해주기

- 컨트롤러에서 지정한 모델 형

- 데이터 추가

	@GetMapping("/viewLogic")
	public String viewLogic(Model model) {
		// user테이블로부터 레코드가 리턴됨
		List<User> userList = new ArrayList<>();
		// 레코드를 데모로 만들고 userList에 추가
		userList.add(new User("홍길동", 10));
		userList.add(new User("김철수", 20));
		userList.add(new User("김영희", 15));

		// 화면모델에 담아 뷰로 넘김
		model.addAttribute("users", userList);
		
		return "pages/greeting/viewLogic.html";
	}

▷ 3. 반복문

1) th:each

th:each="리스트 값 하나 : ${리스트}" 

-> for(var [리스트 값 하나] : 리스트)과 유사
- 컨트롤러의 리스트 데이터 타입을 처리
- 레코드 처리할때
- 주로 테이블의 행 데이터베이스의 레코드

<table>
    <!-- 테이블 제목 --> 
    <thead>
        <th>리스트 속성1</th>
        <th>리스트 속성2</th>
        <th>리스트 속성3</th>
    </thad>

    <!-- 테이블 내용들 --> 
    <tbody>
        <tr th:each="요소 : ${리스트 모델이름}"
        <td th:text="${요소.속성1}">
        <td th:text="${요소.속성2}">
        <td th:text="${요소.속성n}">
        </tr>
    </tbody>
</table>

* 사용자 정보 출력하기

	<h3>th:each</h3>
	<table>
		<!-- 제목줄 -->
		<tr>
			<th>index</th>
			<th>username</th>
			<th>age</th>
		</tr>
		<!-- 데이터 -->
		<tr th:each="user : ${ users }">
			<td th:text="${ userStat.index }"><!-- 번호 출력 --></td>
			<td th:text="${ user.name }"><!-- 이름 출력 --></td>
			<td th:text="${ user.age }"><!-- 나이 출력 --></td>
		</tr>
	</table>
	<hr>


▷ 4. 화면이 없는 루프로직  

1) th:block

- 구분이 되어 나옴
- HTML 태그가 아닌 타임리프의 유일한 자체 태그

- 별도의 태그 없이 제어문 사용

- 렌더링시 제거되는 태그 

- 화면에 HTML태그 없이 블럭으로 만들어서 그 블럭속에 HTML태그를 여러개 추가할 수 있게 해주는 그룹태그(화면에 표시가 없음)
- 주로 타임리프의 화면로직에 관련된 태그들과 같이 작업

 

* <th:block></th:block> 사이의 내용이 반복해서 만들어짐

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


▷ 5. 조건문

- 화면에 출력할지를 결정

5-1) th:if="${조건식}" : 조건식이 참이면 화면출력

5-2) th:unless="${조건식}" : 조건식을 만족하지 않으면(거짓) 화면 출력

 

* 두 조건식을 비교하여 화면에 출력하기(나이를 기준으로 성인 / 미성년자 구분)

	<h3>th:조건문</h3>
	<th:block th:each="user : ${ users }">
		<div>
			사용자 이름 : <span th:text="${ user.name }">데이터</span>
			사용자 나이 : <span th:text="${ user.age }">데이터</span>
		</div>
		<div>
		요약 : <span th:text="${ user.name } + '/' + ${ user.age }"></span>
			  <span th:text="'미성년자'" th:if="${ user.age < 20 }"></span>
			  <span th:text="'성인'" th:unless="${ user.age < 20 }"></span>
		</div>
	</th:block>
	<hr>


▷ 6. 인라인

1) th:inline

- 타임리프를 편리하게 사용할 수 있는 자바스크립트 인라인 기능을 제공

- 자바스크립트에 변수를 담거나 객체를 담는 경우에 활용 

- JSON과 같이 서버에서 생성된 데이터를 직접 자바스크립트의 변수에 일직선으로 대입하여 객체를 생성할 수 있다

 

* 자바스크립트 인라인 each - 자바스크립트안에서 반복을 해야하는 경우에 사용 

	<h3>th:inline</h3>
	<th:block th:each="user : ${ users }">
		<div>
			사용자 이름 : <span th:text="${ user.name }">데이터</span>
			사용자 나이 : <span th:text="${ user.age }">데이터</span>
		</div>
		<div>
		요약 : <span th:text="${ user.name } + '/' + ${ user.age }"></span>
			  <span th:text="'미성년자'" th:if="${ user.age < 20 }"></span>
			  <span th:text="'성인'" th:unless="${ user.age < 20 }"></span>
		</div>
		<!-- JSON과 같이 서버에서 생성된 데이터를 직접 자바스크립트의 변수에 일직선으로
			대입하여 객체를 생성할 수 있다 -->
		<script th:inline="javascript">
			let username = [[${ user.name }]];
			let age = [[${ user.age }]];
			
			// 객체를 직접대입
			let user = [[${ user }]];
		</script>
	</th:block>

 

페이지 소스에서 확인 가능

 


▷ 컨트롤러 작성 - 타임리프 연습

- 로그로 출력하여 작동 확인하기

패키지 : net.choongang.ewha.hithymeleaf.controller
클래스 : HelloController

package net.choongang.ewha.hithymeleaf.controller;

@Controller
@Slf4j // 스프링에서 로그를 사용하기 쉽게 만든 인터페이스
	   // log4j2 logback 어떤 로그라이브러리라도 slf4j규격을 사용하면 동일 사용법 적용
public class HelloController {

	@GetMapping("/hello")
	// 라우터 주소매핑 메소드의 리턴값을 void라고 하면
	// 요청 주소와 뷰템플릿 파일의 이름이 같아야한다
	// Model은 UI용 화면과 화면 사이 또는 화면과 컨트롤러사이에 
	// 임시데이터를 저장하기 위한 버퍼나 캐쉬같은 임시메모리 공간이다. (MVC의 데이터 모델이 아님)
	// 서블릿 JSP에서 RequestDispatcher와 같은 역할
	// Request객체 레벨의 공유공간
	public void hello(Model model) {
		log.info("{}", "'/hello'로 요청이 들어왔습니다."); // 콘솔에서 로그로 확인
		
		// 웹 프로토콜의 상태관리(유지)를 위한 상태 트래킹용 공유공간에
		// 뷰로 전달할 데이터 임시보관
		// model.addAttribute("키변수", "키와 대응하는 값")
		model.addAttribute("username", "황진이");
		model.addAttribute("age", 20);
		
		// 덧셈
		int num1 = 100;
		int num2 = 200;
		int result = num1 + num2;
		model.addAttribute("num1", num1);
		model.addAttribute("num2", num2);
		model.addAttribute("oper", "+");
		model.addAttribute("result", result);
		
		model.addAttribute("data1", "안녕하세요");
		model.addAttribute("data2", "<h1 style='color : green;'> 안녕하세요 </h1>");
	}
}

* Model 

- 화면 작성시 필요한 데이터
- 컨트롤러와 뷰를 연결해주는 모델 
- ui패키지에 들어있음


▷ 뷰 작성하기

* view Template(동적파일)의 위치 : src/main/resources/templates

경로 : HiThymeleaf/src/main/resources/templates
파일명 : hello.html 

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 기본주석 -->
<!--/* [[]] */-->
<!--/*--> [[]] <!--*/-->
<h3>이름 : <span th:text="${username}" ></span></h3>
<h3>나이 : <span th:text="${age}"></span></h3>
<hr>
<h3>
	<span th:text="${num1}"></span>
	<span th:text="${oper}"></span>
	<span th:text="${num2}"></span>
	<span th:text="${'='}"></span>
	<span th:text="${result}"></span>	
</h3>
<h5> 대체표현식 사용하기 </h5>
<h3>
	<span>[[${num1}]]</span>
	<span>[[${oper}]]</span>
	<span>[[${num2}]]</span>
	<span>[[${'='}]]</span>
	<span>[[${result}]]</span>
</h3>
<hr>
<div th:text="${ data1 }">Hi</div>
<div>[[${ data1 }]]</div>
<hr>
<div th:utext="${ data2 }">Hello</div>
<div>[(${ data2 })]</div>

</body>
</html>

* 타임리프 주석
<!-- --> : 기본주석
<!--/*--><!--*/--> : 멀티주석, 특수 기호 등이 포함된 주석문
<!--/*  */-->