728x90
타임리프 기본 기능
“네츄럴 템플릿”
순수 HTML 코드를 최대한 유지할 수 있다. 타임리프로 작성한 파일은 HTML을 유지하기 때문에 브라우저에서 직접 열어도 내용이 확인되고, 서버를 통해 뷰 템플릿을 거쳐 동적 웹 화면도 확인할 수 있다.
JSP를 포함한 다른 뷰 템플릿들은 해당 파일을 열면 소스코드들이 섞여 정확한 HTML 코드를 파악할 수 없다. 오직 서버를 통해서 렌더링 되기 때문이다.
반면에 타임리프로 작성된 파일은 해당 파일 그대로 웹 브라우저에서도 정상적인 HTML 결과를 확인할 수 있다.
“타임리프 사용 선언”
@Controller
@RequestMapping("/basic")
public class BasicController {
@GetMapping("text-basic")
public String textBasic(Model model) {
model.addAttribute("data", "Hello Spring");
return "basic/text-basic";
}
}
- 기본 표현식
<li>th:text 사용<span th:text="${data}"></span></li>
<li>컨텐츠 내부에서 직접 출력하기 = [[${data}]]</li>
- 이스케이프
- <b> 태그를 사용하여 Spring 이라는 단어를 진하게 나오도록 사용하고 싶을 경우 "model.addAttribute("data", "Hello Spring");" 해당 코드에 <b> 태그를 사용해서 볼드처리를 기대했다.
그러나 실행결과는 Hello <b>Spring!</b>라는 결과가 도출된다.
- <b> 태그를 사용하여 Spring 이라는 단어를 진하게 나오도록 사용하고 싶을 경우 "model.addAttribute("data", "Hello Spring");" 해당 코드에 <b> 태그를 사용해서 볼드처리를 기대했다.
- “HTML 엔티티”
- 타임리프가 태그를 출력할 경우 HTML 엔티티라는 것으로 변환 후 출력한다. 웹 브라우저는 <를 HTML의 태그의 시작이라고 인식하기 때문인데, 태그의 시작이 아니라 문자로 표현할 수 있는 방법이 따로 필요한데 이것을 HTML 엔티티라고 한다.-
- 그리고 이렇게 HTML에서 사용하는 특수 문자 HTML 엔티티로 변경하는 것을 이스케이프 라고 한다. 타임리프가 제공하는 th:text, [[…]]는 기본적으로 이스케이프가 적용된다.
- 우리가 원했던 것은 이런 HTML 엔티티가 아니라 <b> 태그의 기능인 볼드처리를 하려는 것이다. 이럴경우 타임리프에서는 Unescape 기능을 사용할 수 있다.
- th:text → th:utext
- [[…]] → [(…)]
- 변수 - SpringEL 표현식
<li>${user.username} = <span th:text="${user.username}"></span></li>
<li>${user['username']} = <span th:text="${user['username']}"></span></li>
<li>${user.getUsername()} = <span th:text="${user.getUsername()}"></span></li>
</ul>
<ul>List
<li>${users[0].username} = <span th:text="${users[0].username}"></span></li>
<li>${users[0]['username']} = <span th:text="${users[0]['username']}"></span></li>
<li>${users[0].getUsername()} = <span th:text="${users[0].getUsername()}"></span></li>
</ul>
<ul>Map
<li>${userMap['userA'].username} = <span th:text="${userMap['userA'].username}"></span></li>
<li>${userMap['userA']['username']} = <span th:text="${userMap['userA']['username']}"></span></li>
<li>${userMap['userA'].getUsername()} = <span th:text="${userMap['userA'].getUsername()}"></span></li>
User userA = new User("userA", 19);
User userB = new User("userB", 23);
List<User> list = new ArrayList<>();
list.add(userA);
list.add(userB);
Map<String, User> map = new HashMap<>();
map.put("userA", userA);
map.put("userB", userB);
model.addAttribute("user", userA);
model.addAttribute("users", list);
model.addAttribute("userMap", map);
타임리프에서 Spring의 변수 값을 출력하는 표현식들이다. object,List, Map 세 가지 타입의 데이터를 출력하는 방법들이다.
- user.username : user의 username을 프로퍼티로 접근 → user.getUsername()
- user['username'] : 위와 같음 user.getUsername()
- user.getUsername() : user의 getUsername() 을 직접 호출
유틸리티 객체와 날짜
- 타임리프는 문자, 숫자, 날짜, URI 등을 편리하게 다루는 다양한 유틸리티 객체가 제공된다.
- #message : 메시지/국제화 처리
- #uris : URI 이스케이프 지원
- #dates : java.util.Date 서식 지원
- #calendars : java.util.Calendar 서식 지원
이런 유틸리티 객체들은 수가 많고 사용법이 존재하기 때문에, 알아두기만 하고 필요할 때 찾아 사용하면 된다.
- 타임리프 유틸리티 객체
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#expression-utilityobjects - 유틸리티 객체 예시
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#appendix-b-expressionutility-object
- 자바 8 날짜
- 타임리프에서 자바8 날짜인 LocalDate, LocalDateTime, Instant를 사용하기 위해서는 추가 라이버르리가 필요하다. 자동으로 추가되고 통합된다.
- URL 링크
- 타임리프에서 URL을 생성할 경우는 **@{…}** 문법을 사용할 수 있다.
- 단순한 URL
- @{/hello} → /hello
- 쿼리 파라미터
- @{/hello{param1=${param1}, param2=${param2})} → /hello?param1=data¶m2=data2
- 경로 변수
- @{/hello/{param1}/{param2}{param1=${param1}, param2=${param2})} → /hello/data1/data2
- 경로 변수 + 쿼리 파라미터
- **@{/hello/{param1}{param1=${param1}, param2=${param2})}** → /hello/data1?param2=data2
- 리터럴
- 리터럴은 소스 코드상에서 고정된 값을 뜻하는 용어이다. 타임리프는 다음과 같은 리터럴이 존재한다.
- 문자 : 'hello'
- 숫자: 10
- 불린 : true,false
- null : null
- |…| 리터럴 대체 문법을 사용하여 마치 템플릿을 사용하는 것 처럼 이용이 가능하다.
- “리터럴 대체”
- 연산
- Elvis 연산자
- 데이터가 없을때 사용할 수 있다. <span th:text="${data}?: '데이터가없습니다.'"> 데이터가 있다면 ${nullData}를 표현하고, 데이터가 없다면 ? 후미의 ‘데이터가 없습니다’ 가 표현된다.
- No-Operation
- _ 인 경우 마치 타임리프가 실행되지 않는 것 처럼 동작한다. 이것을 잘 사용하면 HTML 의 내용 그대로 활용할 수 있다. 마지막 예를 보면 데이터가 없습니다. 부분이 그대로 출력된다
- Elvis 연산자
- 타임리프 태그 속성
- 속성 추가
- attrappend : 속성의 뒤에 값을 추가한다.
- attrprepend : 속성의 앞에 값을 추가한다.
- classappend : class 속성에 자연스럽게 추가된다.
- 속성 추가
- <h1>속성 추가</h1> - th:attrappend = <input type="text" class="text" th:attrappend="class='large'" /><br/> - th:attrprepend = <input type="text" class="text" th:attrprepend="class='large'" /><br/> - th:classappend = <input type="text" class="text" th:classappend="large" /><br/> <h1>checked 처리</h1> - checked o <input type="checkbox" name="active" th:checked="true" /><br/> - checked x <input type="checkbox" name="active" th:checked="false" /><br/> - checked=false <input type="checkbox" name="active" checked="false" /><br/>
- 반복
- “th:each”를 사용한다. 이 반복중에 사용가능한 여러 속성이 존재한다.
<table border="1"> <tr> <th>username</th> <th>age</th> </tr> **<tr th:each="user : ${users}">** <td th:text="${user.username}">username</td> <td th:text="${user.age}">0</td> </tr>
- th:each=”user : ${users}”
- user에 컬렉션 ${users}의 값을 하나씩 꺼내서 왼쪽 변수에 담아 태그를 반복한다.
- List뿐 아니라 배열, 객체에도 사용 가능하다. Map도 사용가능하지만 Map.Entry 를 이용해야한다.
**<tr th:each="user, userStat : ${users}">** <td th:text="${userStat.count}">username</td> <td th:text="${user.username}">username</td> <td th:text="${user.age}">0</td> <td> index = <span th:text="${userStat.index}"></span> count = <span th:text="${userStat.count}"></span> size = <span th:text="${userStat.size}"></span> even? = <span th:text="${userStat.even}"></span> odd? = <span th:text="${userStat.odd}"></span> first? = <span th:text="${userStat.first}"></span> last? = <span th:text="${userStat.last}"></span> current = <span th:text="${userStat.current}"></span> </td> </tr>
- 반복 상태 유지에서의 기능
- th:each 의 두번째 파라미터 Stat 을 설정해서 반복의 상태를 확인 할 수 있다.
- index : 0부터 시작하는 값
- count : 1부터 시작하는 값
- size : 전체 사이즈
- even, odd : 홀수, 짝수 여부 (boolean값)
- first, last : 처음, 마지막 여부 (boolean값)
- current : 현재 객체
- 조건부 평가
- 타임리프에서의 조건식 if, unless (if의 반대)
조건이 맞지 않는다면 th:if / unless 태그가 적용되지 않는다. 렌더링 자체가 되지 않고 사라진다.switch문도 사용할 수 있다. case=”*” 는 만족하는 조건이 없을 경우 사용하는 디폴트 값이다.<span th:text="'미성년자'" th:if="${user.age lt 20}"></span> <span th:text="'미성년자'" th:unless="${user.age ge 20}"></span>
- <td th:switch="${user.age}"> <span th:case="10">10살</span> <span th:case="20">20살</span> <span th:case="*">기타</span> </td>
- 블록
th:each 를 이용하여 반복출력하는 대신에, block 자체를 반복하여 표현하고 싶을때 사용한다. 렌더링할때는 제거된다. 보통 잘 사용되지 않는 속성이지만 일반적인 반복이 힘든 애매한 경우에 사용된다.<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>
- th:block은 HTML 태그가 아닌 타임리프의 유일한 자체 태그이다. 속성으로 작동하는 타임리프중에서는 유일하다.
- 자바스크립트 인라인
- <script th:inline=”javascript”>
- 타임리프는 자바스크립트에서 편리하게 사용할 수 있는 인라인 기능을 제공한다. 다음과 같이 인라인 기능을 적용하면 된다
- 템플릿 조각
- th:insert
- <div> 태그사이에 th:insert=”~{경로 :: copy}” 의 내용을 추가한다.
<h2>부분 포함 insert</h2> <div> <footer> 푸터 자리 입니다. </footer> </div>
- th:replace
- <div> 태그를 th:replace 경로 :: copy의 내용으로 대체한다.
<h2>부분 포함 replace</h2> <footer> 푸터 자리 입니다. </footer>
- 파라미터 사용
<h1>파라미터 사용</h1> <div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></div>
- th:insert
- <h1>부분 포함</h1> <h2>부분 포함 insert</h2> <div th:insert="~{template/fragment/footer :: copy}"></div> <h2>부분 포함 replace</h2> <div th:replace="~{template/fragment/footer :: copy}"></div> <h2>부분 포함 단순 표현식</h2> <div th:replace="template/fragment/footer :: copy"></div> <h1>파라미터 사용</h1> <div th:replace="~{template/fragment/footer :: copyParam ('데이터1', '데이터2')}"></div>
- 템플릿 레이아웃
</ script> <!-- 추가 --> <th:block th:replace="${links}" /> </head>
- 공통으로 사용되는 레이아웃
메인 컨텐츠
- 핵심
- <head th:replace="template/layout/base :: common_header(~{::title},~{::link})">
- 나의 코드조각을 레이아웃에 넘기는 기능
- 템플릿 레이아웃 - 확장
- head에 한정 된 것이 아닌 html 코드 그 전체를 적용시킬 수도 있다.
728x90
'SpringBoot > Spring 김영한님' 카테고리의 다른 글
김영한 Spring - selectbox, 메시지, 국제화 (0) | 2023.04.20 |
---|---|
김영한 Spring - Thymeleaf (2) (0) | 2023.04.19 |
김영한 Spring MVC (7) - 메시지 컨버터, 정적 리소스, JSON (0) | 2023.04.10 |
김영한 Spring MVC (6) (1) | 2023.04.06 |
김영한 Spring MVC (5) (0) | 2023.03.29 |