타임리프
타임리프
•
서버 사이드 HTML 렌더링 (SSR)
•
네츄럴 템플릿 : 순수 HTML을 최대한 유지하는 특징, 웹으로 열면 엥간하면 제대로 뜬다.
•
스프링의 다양한 기능을 편리하게 사용할 수 있게 지원함
•
타임리프 사용 선언
<html xmlns: th="http://www.thymleaf.org">
기본 표현식
1.
간단한 표현
•
변수 표현식: ${}
•
선택 변수 표현식: *{}
•
메시지 표현식: #{}
•
링크 URL표현식: @{}
•
조각 표현식: ~{}
2.
리터럴
•
텍스트: 'one text', 'Another one!'
•
숫자: 0,34,3.0,12.3
•
불린: true, false
•
널: null
•
리터럴 토큰: one,sometext,main
3.
문자 연산
•
문자 합치기: +
•
리터럴 대체: | The name is ${name}|
4.
산술 연산
•
Binary operators: +,-,*,/,%
•
Minus sign (unary operator): -
5.
불린 연산:
•
Binary operators: and, or
•
Boolean negation (unary operator): !, not
6.
비교와 동등:
•
비교: >,<,≥, ≤
•
동등 연산:==, ≠
7.
조건 연산:
•
If-then: (if) ? (then)
•
If-then-else: (if) ? (then) : (else)
•
Default: (value) ?: (defaultvalue)
8.
특별한 토큰:
•
No-Operation: _
텍스트 - text, utext
타임리프는 기본적으로 HTML 태그의 속성에 기능을 정의해서 동작
HTML의 콘텐츠(content)에 데이터를 출력할 때는 다음과 같이 th:text를 사용하면 된다.
HTML 영역 안에 직접 데이터를 출력하고 싶으면 다음과 같이 [[...]] 를 사용하면 된다.
<ul>
<li>th:text = <span th:text = "${data}"></span></li>
<li>th:utext = <span th:utext = "${data}"></span></li>
</ul>
<h1><span th:inline="none">[[...]] vs [(...)]</span> </h1>
<ul>
<li><span th:inline="none">[[...]] =</span> [[${data}]]</li>
<li><span th:inline="none">[(...)] =</span> [(${data})]</li>
</ul>
HTML
복사
변수 - SpringEL
변수 표현식 : ${...}
지역변수 선언: th:with
<h1>지역 변수 - (th:with) </h1>
<div th:with = "first=${users[0]}">
<p>처음 사람의 이름은 <span th:text = "${first.username}"></span></p>
</div>
HTML
복사
기본 객체들
타임리프는 기본 객체들을 제공한다
•
${#request}
•
${#response}
•
${#session}
•
${#servletContext}
•
${#locale}
편의 객체들
•
${param.paramData}
•
${session.sessionData}
•
${@helloBean.hello('Spring!')}
유틸리티 객체들
타임리프는 문자,숫자,날짜, URI 등을 편리하게 다루는 다양한 유틸리티 객체들을 제공
#message: 메시지, 국제화 처리
#uris: URI 이스케이프 지원
#calendars: java.util.Calendar 서식 지원
#temporals: 자바 8 날짜 서식 지원
#numbers: 숫자 서식 지원
#strings: 문자 관련 편의 기능
#objects: 객체 관련 기능 제공
#bools: boolean 관련 기능 제공
#arrays: 배열관련 기능 제공
#lists,#sets,#maps: 컬렉션 관련 기능 제공
#ids: 아이디 처리 관련 기능 제공, 뒤에서 설명
찾아서 보면 됨
URI 링크
단순한 URL
@{/hello} → /hello
쿼리 파라미터
@{/hello(param1=${param1} , param2=${param2})}
→ /hello?param1=data1¶m2=data2
→ ()에 있는 부분은 쿼리 파라미터로 처리됨
경로 변수
@{/hello/{param1}/{param2}(param1 = ${param1}, param2=${param2})}
→ /hello/data1/data2
경로 변수 + 쿼리 파라미터
@{/hello/{param1}(param1=${param1},param2=${param2})}
→ /hello/data1?param2=data2
경로 변수와 쿼리 파라미터를 함께 사용할 수 있다.
상대경로,절대경로,프로토콜 기준을 표현할 수도 있다.
/hello: 절대경로
hello: 상대경로
리터럴
문자: 'hello'
숫자: 10
불린: true, false,
null: null
문자 리터럴은 항상 작은 따옴표로 감싸야함
Ex) <span th:text = "'hello world!'"></span>
리터럴 대체
<span th:text = "|hello ${data}|">
연산
비교연산: HTML엔티티를 사용해야하는 부분을 주의
>(gt) , <(lt)m ≥(ge), ≤(le), !(not) ==(eq) ≠(neq)
조건식: 자바 조건식과 유사
Elvis: 연산자: 조건식의 편의 버전
No-operation: _인 경우 마치 타임리프가 실행되지 않는 것처럼 동작한다.
속성
th:* 로 속성을 지정하면 기존 속성을 th:* 로 지정한 속성으로 대체함. 기존 속성이 없다면 새로 만듬
속성 추가
•
th:attrappend
•
th:attrprepend
•
th:classappend
checked 처리
HTML에서는 checked라는 속성만 있어도 checked 처리가 되어버림
타임리프의 th:checked는 값이 false인 경우 checked 속성 자체를 제거함
반복
th:each를 사용함
반복에서 사용할 수 있는 여러 상태 값을 지원
<tr th:each = "user, userStat : ${users}">
조건문
타임리프는 해당 조건이 맞지 않으면 태그 자체를 렌더링하지 않는다.
만약 다음 조건이 false인 경우 <span></span>이 아예 렌더링이 안됨
<span th:text = "'미성년자'" th:if="${user.age lt 20}"></span>
switch
*은 만족하는 조건이 없을 때 사용하는 디폴트이다.
주석
1.
표준 HTML 주석
a.
<!— —>
2.
타임리프 파서 주석
a.
<!—/*—>
<!—*/—>
3.
타임리프 프로토타입 주석
a.
<!—/*/ ~~~~ /*/—>
HTML 파일을 웹에서 열면 HTML주석이라 보이지 않음
타임리프 렌더링을 거치면 이 부분이 정상 렌더링이 됨
블록
<th:block>은 HTML 태그가 아닌 타임리프의 유일한 자체 태그이다.
<th:block th:each = "user: ${users}">
<div>
사용자 이름1 <span th:text="${user.name}"></span>
사용자 나이1 <span th:text="${user.age}"></span>
</div>
<div>
요약<span th:text="${user.name} + '/' + ${user.age}"></span>
</div>
</th:block>
HTML
복사
자바스크립트 인라인
1.
텍스트 렌더링
문자 타입인 경우 "를 포함해줌. 문제가 될만한 이스케이프 처리도 알아서 해줌
2.
내츄럴 템플릿
파일을 그냥 열어도 안 깨진다.
var username2 = /*[[${user.username}]]*/ "test username";
파일을 그냥 열면 test username이라는 문자열이 들어가고, 렌더링하면 ${}값이 들어간다.
3.
객체
객체를 JSON으로 자동으로 변환해준다.
4.
인라인 each
<script th:inline = "javascript">
[# th:each = "user, userStat: ${users}"]
var user[[${stat.count}]] = [[${user}]];
[/]
</script>
HTML
복사
템플릿 조각
여러 페이지들을 조각 내놓고 그걸 여러 군데 반복적으로 쓰는 것
<footer th:fragment="copy">
푸터 자리입니다.
</footer>
<footer th:fragment="copyParam (param1,param2)">
<p> 파라미터 자리입니다.</p>
<p th:text = "${param1}"></p>
<p th:text = "${param2}"></p>
</footer>
HTML
복사
<div th:insert="~{template/fragment/footer :: copy}"></div>
<div th:replace="~{template/fragment/footer :: copy}"></div>
<div th:replace = "template/fragmenet/footer :: copy}"></div>
<div th:replace = "~{template/fragment/footer :: copyParam('데이터1', '데이터2')}"></div>
HTML
복사
템플릿 레이아웃1
코드 조각을 레이아웃에 넘겨서 사용하는 방법
common_header(~{::title}, ~{::link} ) 이 부분이 핵심
::title은 현재 페이지의 title 태그들을 전달
::link는 현재 페이지의 link 태그들을 전달
템플릿 레이아웃2
템플릿 레이아웃 확장
앞서 이야기한 개념을 <head> 정도에만 적용하는게 아니라 <html>전체에 적용할 수도 있다.
<html>에 th:fragment 속성이 정의되어있다.
이 레이아웃 파일을 기본으로 하고 여기에 필요한 내용을 전달해서 부분부분 변경하는 것
타임리프 - 스프링 통합과 폼
추가되는 기능들
•
SpringEL문법 통합
•
편리함 폼관리를 위한 추가 속성
•
폼 컴포넌트 기능
•
스프링의 메시지, 국제화 기능의 편리한 통합
•
스프링의 검증, 오류 처리 통합
•
스프링의 변환 서비스 통합
입력 폼 처리
th:object → 커맨드 객체를 지정한다
*{...} → 선택 변수 식이라고 한다. th:object에서 선택된 객체에 접근
th:field → HTML 태그의 id,name,value 속성을 자동으로 처리해준다.
<input type = "text" th:filed = "*{itemName}"/>
—> <input type = "text" id = "itemName" name = "itemName" th:value = "*{itemName}"/>
th:object를 적용하려면 먼저 해당 오브젝트 정보를 넘겨줘야한다.
등록 폼이기 때문에 데이터가 비어있는 오브젝트를 만들어서 뷰에 전달
<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 = "form-control"
placeholder = "이름을 입력하세요">
</div>
HTML
복사
체크 박스 - 단일
체크 박스를 선택하지 않았을 때는 open이라는 필드 자체가 서버로 전송되지 않는다.
즉, 체크 됬는지, 안됬는지에 대한 값 자체가 넘어가지 않는다.
이를 위해 스프링 MVC는 히든 필드를 인식할 수 있도록 하는데,
open이라고 했을 때 _open이라는 히든 필드가 넘어오면, 체크를 해제했다고 인식할 수 있다. 히든 필드는 항상 전송되는데, 따라서 체크를 해제했으면 open은 전송X, _open만 전송 → 이걸 인식해서 false를 뱉는다.
이 때 타임리프가 제공하는 폼 기능을 사용해보자
th:field="*{~~}"
이 속성을 사용하면 체크박스의 히든 필드도 자동으로 생성되있다.
th:field를 사용하면 값이 true인 경우 체크를 자동으로 처리해줌
체크 박스 - 멀티
ModelAttribute의 특별한 사용법
등록 폼, 상세화면, 수정 폼에서 모두 서울,부산,제주라는 체크박스를 반복해서 보여줘야함.
@ModelAttribute를 따로 빼서 사용하면 해당 컨트롤러가 반환하는 Model에 값을 다 담아서 준다.
<div>
<div>등록 지역</div>
<div th:each="region : ${regions}" class="form-check form-check-inline">
<input type="checkbox" th:field="*{regions}" th:value="${region.key}" class="form-check-input">
<label th:for="${#ids.prev('regions')}"
th:text="${region.value}" class="form-check-label">서울</label>
</div>
</div>
JavaScript
복사
멀티 체크박스는 같은 이름의 여러 체크박스를 만들 수 있다.
반복해서 생성할 때 id는 모두 달라야함. 체크박스를 each루프 안에서 반복해서 만들 때 임의로 1,2,3숫자를 뒤에 붙여준다.
ids.prev('regions')를 이용해서 동적으로 생성되는 id값을 이용하도록 함.
타임리프의 체크 확인
•
멀티 체크 박스에서 등록 지역을 선택해서 저장하면, 조회 시에 checked 속성이 추가된 것을 확인할 수 있다
•
타임리프는 th:field에 지정한 값과 th:value의 값을 비교해서 체크를 자동으로 처리해준다.
라디오 박스
여러 선택지 중에 하나를 선택할 때 사용할 수 있다.
Enum → item.values() :해당 Enum의 정보를 배열로 반환
체크 박스는 수정 시 체크를 해제하면 아무 값도 넘어가지 안힉 때문에 별도의 히든 필드로 해결
라디오 버튼은 이미 선택되어있다면, 수정 시에도 항상 하나를 선택해야함. 따라서 별도의 히든 필드를 사용할 필요가 없다.
셀렉트 박스
뭐 비슷하다
<select> <option>사용하는 거 빼면 다 똑같음