@GetMapping("ex3")
public String ex3(Model model) {
// Model : 데이터 전달용 객체 (request scope)
model.addAttribute("boardNo", 10);
model.addAttribute("key", "제목");
model.addAttribute("query", "검색어");
return "example/ex3";
}
/* @PathVariable
* - 주소 중 일부분을 변수 값 처럼 사용
* + 해당 어노테이션으로 얻어온 값은 request scope에 세팅
*
*/
@GetMapping("ex3/{number}")
public String pathVariableTest(@PathVariable("number") int number) {
// 주소 중 {number} 부분의 값을 가져와 매개변수에 저장
// + request scope에 세팅
log.debug("number : " + number);
return "example/testResult";
}
<!DOCTYPE html>
<html lang="en" xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>타임리프 예제 3</title>
</head>
<body>
<h1>타임리프 예제 3</h1>
<h3>링크(URL) 조합에 사용되는 Spring EL( @{} ) </h3>
<ul>
<li>
<a href="/board">게시판(/board)</a>
</li>
<li>
<a href="/board?key=제목&query=검색어">게시판 검색</a>
</li>
<li>
<!-- 타임리프를 이용한 쿼리스트링 작성법 -->
<a th:href="@{/board(key=${key}, query=${query})}">@{} 사용</a>
<!-- /board?key=제목&query=검색어 -->
</li>
<li>
PathVariable : 주소 중 일부분을 변수 값 처럼 사용
<ul>
<li>
<a href="/example/ex3/1">/example/ex3/1</a>
</li>
<li>
<a href="/example/ex3/2">/example/ex3/2</a>
</li>
<li>
<a href="/example/ex3/3">/example/ex3/3</a>
</li>
<li>
<a th:href="@{/example/ex3/{number}(number=${boardNo})}">
@{/example/ex3/{number}(number=${boardNo})}
</a>
</li>
</ul>
</li>
</ul>
</body>
</html>





@GetMapping("ex4")
public String ex4(Model model) {
Student std = new Student("67890", "잠만보", 22);
// 테스트에 필요한 객체 및 값 생성 후 전달
model.addAttribute("std", std);
model.addAttribute("num", 100);
return "example/ex4";
}
<h3>타임리프 조건문 [1] - th:if / th:unless</h3>
<pre>
th:if = ${조건식}
- 조건식이 **true**인 경우에만
해당 속성이 작성된 요소를 화면에 출력
th:unless = ${조건식}
- 조건식이 **false**인 경우에만
해당 속성이 작성된 요소를 화면에 출력
</pre>
<th:block th:if="${std == null}">
<!-- request scope에 std 속성이 없을 경우 -->
<h4>std 없음</h4>
</th:block>
<th:block th:unless="${std == null}">
<!-- std != null -->
<!-- request scope에 std 속성이 있을 경우 -->
<h4>std 있음</h4>
</th:block>
<h3>th:if / th:unless 조건식에 변수명/값 하나만 작성한 경우</h3>
<pre>
if문에 작성된 값이 있으면(값 != null) -> true
if문에 작성된 값이 없으면(값 == null) -> false
</pre>
<h4 th:if="${std}">std 있음!!!</h4>
<!-- 출력 X -->
<h4 th:if="${temp}">temp 없음</h4>

<h3>타임리프 조건문 [2] - th:switch / th:case</h3>
<!-- request 속성 "num" 값에 따라서 알맞은 case 실행 -->
<th:block th:switch="${num}">
<h4 th:case="100">AAAAAAAAAAAAAAAA</h4>
<h4 th:case="200">BBBBBBBBBBBBBBBB</h4>
<h4 th:case="300">CCCCCCCCCCCCCCCC</h4>
<!-- th:case="*" == default (나머지인 경우) -->
<h4 th:case="*">나머지...</h4>
</th:block>

💡 삼항연산자는 이후 React에서 자주 나오기 때문에 익혀두는 것이 좋음
<h3>삼항 연산자</h3>
<pre>
- 타임리프 속성(th:)에
삼항연산자 ( 조건식 ? 참인경우 : 거짓인경우 ) 작성 가능
</pre>
<h4 th:text="${std.age == 30} ? '서른' : '서른아님'">삼항연산자</h4>

<h3>Elvis 연산자( ?: )</h3>
<pre>
[작성법]
값 ?: 값이 없을 때
- 삼항 연산자에서 조건식 자리에 값(변수명)만 작성
(==, != 등의 연산자 사용 X)
- 우변에는 값이 없을 때에 대한 값만 작성
- 조건식 값이 존재하면 해당 값을 출력
없으면 우변을 출력
-> 해당 값이 있는지 없는지에 따라 동작하는 연산자
</pre>
<!-- 회원 데이터 없음 -->
<p th:text="${member} ?: '회원 데이터 없음'"></p>
<!-- std.toString() 결과 출력 -->
<p th:text="${std} ?: '학생 데이터 없음'"></p>

<h3>No-Operation 연산자 ( 값 ?: _ ) </h3>
<pre>
- 조건식의 값이 없을 경우 (==null)
타임리프 코드를 해석하지 않는 연산자
- 타임리프 코드 해석 X
-> 일반 HTML 태그로 동작
-> HTML 태그 사이 내용(content)이 화면에 출력
</pre>
<!-- 회원 데이터 없음 -->
<p th:text="${member} ?: _">회원 데이터 없음</p>
<!-- std.toString() 결과 출력 -->
<p th:text="${std} ?: _">학생 데이터 없음</p>

@GetMapping("ex5")
public String ex5(Model model) {
// Model : Spring에서 값 전달 역할을 하는 객체
// 기본적으로 request scope + session으로 확장 가능
model.addAttribute("message", "타임리프 + Javascript 사용 연습");
model.addAttribute("num1", 12345);
Student std = new Student();
std.setStudentNo("22222");
model.addAttribute("std", std);
return "example/ex5";
}
<h1>타임리프 예제 5</h1>
<h3>th:inline = "javascript" 속성</h3>
<pre>
- script 태그에 작성하는 속성
- 타임리프 문법으로 출력된 내용/값을
js에 알맞은 타입으로 변환
</pre>
<h3 id="message"></h3>
<h3 id="num1"></h3>
<script th:inline="javascript">
// JS Inline - Natural Template
// 스크립트 태그 내부에서 타임리프의 변수나 연산을 사용할 수 있게함
// - HTML 파일 독립 실행 시
// JS 내부 타임리프 코드 오류를 발생하지 않게함
// + HTML 문법 오류(컴파일 오류)도 해결
const message = /*[[${message}]]*/ "message 값";
const num1 = /*[[${num1}]]*/ 100;
document.querySelector("#message").innerText = message;
document.querySelector("#num1").innerText = num1;
</script>

<h3>th:classappend 속성 : 요소에 class 속성값을 동적으로 추가</h3>
<h4 th:classappend="red">th:classappend 테스트중...</h4>
<h4 class="red" th:classappend="deco">th:classappend 테스트중...</h4>
<!-- num1이 10000보다 클 경우 green 아니면 red -->
<h4 th:classappend="${num1 gt 10000} ? green : red">th:classappend 테스트중...</h4>
<!-- th:class를 이용하면 기존 deco 클래스 덮어 씌워버림 (밑줄 출력 안됨) -->
<h4 class="deco" th:class="${num1 gt 10000} ? green : red">th:classappend 테스트중...</h4>




src/main/resources/templates 하위 경로에 폴더 및 html 생성
<h3>fragment/temp.html 중 temp1 조각 추가하기</h3>
<!-- 타임리프가 제공하는 접두사, 접미사를 제외한 경로를 작성 -->
<th:block th:replace="~{fragments/temp :: temp1}"></th:block>
<th:block th:replace="~{fragments/temp :: temp2}"></th:block>
<!-- HTML 파일 전체를 하나의 조각으로 취급 -->
<th:block th:replace="~{fragments/footer}"></th:block>
<!DOCTYPE html>
<html lang="en" xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>코드 조각 모음 </title>
</head>
<body>
<!--
fragment(조각)
- html 요소를 다른 html 파일에서 재사용 가능하게 함
-->
<div th:fragment="temp1">
<ul>
<li><a href="#">공지사항</a></li>
<li><a href="#">자유 게시판</a></li>
<li><a href="#">질문 게시판</a></li>
<li><a href="#">FAQ</a></li>
<li><a href="#">1:1문의</a></li>
</ul>
</div>
<h1 th:fragment="temp2" style="background-color: yellow; color: red;">Thymeleaf Fragment 확인 중</h1>
</body>
</html>
<hr>
<p>타임리프 연습 프로젝트입니다.</p>
<hr>
<p style="text-align: center;">
<a href="#">고객센터</a>
<a href="#">1:1문의</a>
<a href="#">찾아오시는 길</a>
</p>
