07_Thymeleaf

송지윤·2024년 4월 2일
0

Spring Framework

목록 보기
7/65

템플릿 엔진

  • 템플릿 양식과 특정 데이터 모델에 따른 입력 자료를 합성하여 결과 문서(응답 화면)를 출력하는 것
    -> 만들어둔 화면(html)에 데이터를 추가하여 하나의 html로 만들어서 응답
    (JSP도 템플릿 엔진)
    https://www.thymeleaf.org/

Thymeleaf(타임리프)

  • 웹 및 독립 실행형 환경 모두를 위한 최신 서버 측 Java 템플릿 엔진
  • HTML 파일에서 th(Thymeleaf) 속성을 이용해 컨트롤러로부터 전달받은 데이터를 이용해 동적 페이지를 만들 수 있음.
  • Spring Boot에서는 JSP가 아닌 Thymeleaf 사용을 권장하고 있음.

Thymeleaf 사용 준비

[Maven] - pom.xml

[Gradle] - build.gradle

application.properties

thymeleaf 접두사, 접미사 설정
미작성 시 기본값
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

Thymeleaf 문법 (표현식)

<html lang="ko" xmlns:th="http://www.thymeleaf.org">

Spring EL

${key} : 변수, Model 등으로 전달된 데이터의 key 입력 시 value 출력

<p text=${member}></p>
<p text=${member.memberNo}</p>

${key} : 선택 변수, 객체에 포함된 필드의 출력

th:object 속성과 같이 사용

<!-- member 객체의 필드 memberNo와 memberPw 등을 출력 -->
<div obejct="${member}">
<span text="*{memberNo}"></span>
<span text="*{memberId}"></span>
</div>

#{key} : Message Expression

  • src/main/resoources/messages.properties에 작성된 값을 얻어와 출력

messages.properties

app.name=Board Project - boot
user.default.image=/images/user.png

html 파일

<p th:text="#{app.name}">프로젝트 이름</p>
<img th:src="#{user.default.image}" id="memberProfile">

@{url} : 링크 URL, URL 형식 출력

<!-- 단순 url-->
<a href="@{/board}">단순 url</a>
<!-- 쿼리스트링 포함 url-->
<a href="@{/board(key=${key}, queyr=${query})}">쿼리스트링 포함</a>
<!-- PathVariable 포함 url-->
<a href="@{/board/{boardCode}/{boardNo}(boardCode=${boardCode}, boardNo=${boardNo})}">PathVar

th:href, th:src, th:action : @{url} 표기를 적용하는 속성

  • 일반 href, src, action은 처음 작성되어 있는 지정된 주소로만 요청 가능
  • th가 붙은 속성은 서버측에서 작성되어지기 때문에 동적으로 주소를 변경할 수 있음.

th:text, th:value

  • 지정된 내용을 Text 형식으로 내용, input의 value로 출력
<!-- request scope에 key="memberName" value="홍길동" 이 있을 경우-->
<p text="${memberName}">회원이름</p>
<input type="text" value="${memberName}">
<!-- 랜더링 결과 -->
<p>홍길동</p>
<input type="text" value="홍길동">

th:text / th:utext

[[...]] / [(...)]

  • Thymeelaf의 텍스트를 HTML에 출력한다는 공통점이 있지만
    출력하려는 텍스트에 HTML 태그가 있을 경우
    이를 해석 할 것인지, 말 것인지를 구분하는 방법을 제공
  • th:text , [[ ... ]] : HTML 태그를 해석 X (JS의 innerText와 비슷)
  • th:utext , [[ ... )] : HTML 태그를 해석 O (JS의 innerHTML과 비슷)
<!-- key="memberName" value="<b1>홍길동</b1>" 이 있을 경우-->
<h3> text 와 utext의 차이점 </h3>
<p text="${memberName}">회원이름</p>
<p utext="${memberName}">회원이름</p>
<h3> [[...]] 와 [(...)]의 차이점 </h3>
<p>[[${memberName}]]</p>
<p>[(${memberName})]</p>

|문자열| : 리터럴 대체

  • th:text, th:value에 원하는 형식의 Text를 표기하는 방법
<!-- request scope에 key="memberName" value="홍길동" 이 있을 경우-->
<p text="|이름 : ${memberName} 님|">회원이름</p>
<!-- 랜더링 결과 -->
<p>이름 : 홍길동 님</p>

th:with : 지역 변수 선언

  • th:with가 선언된 태그 내에서 사용할 변수명을 지정
  • ,로 구분하여 여러 변수 선언 가능
  • th:with=”변수명=값, 변수명=값”
<ul with="num=100, name1=#{app.name}, name2=${memberName}">
<li text="${num}">num</li>
<li text="${name1}">name1</li>
<li text="${name2}">name2</li>
</ul>

<th:block>

  • Thymeleaf의 유일한 자체 태그
  • Thymeleaf는 HTML 태그 내에 th 속성을 작성하여 기능을 정의하는게 일반적이지만
    마땅한 HTML 태그가 없을 경우에 사용

th:if="${조건}" / th:unless="${조건}" : if / else문

  • th:if == if문, th:unless == else문
  • th:unless 조건에 if와 같은 조건을 작성해야됨 (조건 자동으로 반대로 바꿔줌)
<th:block th:if="${session.loginMember == null}">
	<a href="/">메인 페이지</a> | <a href="/member/login">로그인</a>
</th:block>

<th:block unless="${session.loginMember == null}">
	<label for="header-menu-toggle"> 닉네임
		<i class="fa-solid fa-caret-down"></i>
	</label> <input type="checkbox" id="header-menu-toggle">
	<div id="header-menu">
		<a href="/member/info">내정보</a> <a href="/member/logout">로그아웃</a>
	</div>
</th:block>

<!-- if 조건식에 값만 작성한 경우 (있으면 true, 없으면 false)-->
<th:block unless="${session.loginMember}">
	로그인 x
</th:block>

<th:block if="${session.loginMember}">
	로그인 O
</th:block>

th:switch="${값}" / th:case="switch의값" : switch - case 문

  • th:switch에 작성된 값에 따라 일치하는 th:case가 실행
  • th:case=”*” 은 case중 마지막에 작성되며, 앞선 case를 제외한 모든 경우(default)를 의미
<th:block switch="${num}">
  <p th:case="100">A</p>
  <p th:case="200">B</p>
  <p th:case="*">C</p>
</th:block>

비교 연산자 : lt(<), gt(>), le(<=), ge(>=), eq(==), ne(!=), not(!)

  • 마크업 언어 태그 기호 <, >로 인해 오류가 발생할 가능성이 있으므로 부등호가 포함된 연산자는 대체 연산자 사용

삼항 연산자 : 조건식 ? true일때 : false일 때

<h3>삼항 연산자</h3>
<p text="${memberName} ? ${memberName} : '데이터가 없습니다'">삼항 연산자</p>

Elvis 연산자 : 값 ?: 값이 없을 때

  • 삼항 연산자에서 조건식에 값만 작성한 후 값이 있다면 작성한 값, 없으면 없을 경우에 대한 값이 출력
<h3>Elvis 연산자</h3>
<p text="${memberName} ?: '데이터가 없습니다'">Elvis 연산자</p>
<p th:text="${std} ?: '학생 데이터 없음'"></p>
    <!-- std.toString() 한 결과 값 나옴 -->
    <!-- Student(studentNo=67890, name=잠만보, age=22) -->

No-Operation : 값 ?: _

  • 값이 없을 경우 Thymelaef가 실행이 안된 것 만듦

  • html 태그 내부에 작성된 값이 그대로 출력됨

  • 조건식의 값이 없을 경우 (== null)
    타임리프 코드를 해석하지 않는 연산자

  • 타임리프 코드 해석 X
    -> 일반 HTML 태그로 동작
    -> HTML 태그 사이 내용(content)이 화면에 출력

<h3>No-Operation</h3>
<p text="${memberName222} ?: _">데이터가 없습니다</p>
<!-- 랜더링 결과 -->
<h3>No-Operation</h3>
<p>데이터가 없습니다</p>

<p th:text="${std} ?: _">학생 데이터 없음</p>
    <!-- Student(studentNo=67890, name=잠만보, age=22) -->
    <!-- 학생 데이터 있으니까 타임리프 코드 해석해서 p 태그 내용 없어지고 std.toString 나옴 -->

th:each="item : ${list 또는 배열}"

  • 해당 HTML 요소를 list 또는 배열의 길이 만큼 반복
  • list에 저장된 요소를 순차접근하여 item에 저장
  • 매 반복시 마다 List 또는 배열의 요소를 차례대로 꺼내 item 변수에 저장 (item 변수명은 자유롭게 작성)
<h4>th:each 사용</h4>
    <ul>
        <th:block th:each="fruit : ${fruitList}">
            <li th:text="${fruit}">과일명</li>
        </th:block>
    </ul>

HTML 화면

  • 사과
  • 딸기
  • 바나나

Controller에서 fruitList 값 사과, 딸기, 바나나로 setting 해놓음

List<String> fruitList = new ArrayList<>();
fruitList.add("사과");
fruitList.add("딸기");
fruitList.add("바나나");
		
model.addAttribute("fruitList", fruitList);

Model 객체 이용
Model 객체는 Spring 에서 사용하는 데이터 전달용 객체 (request scope)

fragment(조각)을 이용한 공통 영역 처리

th:fragment="조각 이름"

  • html 중 공통된 부분(반복되는 코드)을 조각(fragment)으로 지정

[templates/fragments/common.html]

<nav fragment="nav"> <!-- 해당 요소에 nav라는 조각 이름을 지정-->
<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>
<li><a href="/chatting">채팅</a></li>
</ul>
</nav>

th:insert="~{조각파일경로 :: 조각이름}"

  • insert : 삽입하다
  • 해당 속성이 작성된 요소 내부에 지정된 조각을 삽입하는 속성
<!-- 해당 요소를 조각으로 지정된 속성으로 변경-->
<div insert="~{fragments/commons :: nav}"></div>

<!-- 랜더링 결과 -->
<!-- div 요소 안에 nav라는 이름의 조각이 삽입됨 -->
<div>
<nav>
<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>
<li><a href="/chatting">채팅</a></li>
</ul>
</nav>
</div>

th:replace="~{조각파일경로 :: 조각이름}"

  • replace : 바꾸다
  • 해당 속성이 작성된 요소를 지정된 조각으로 바꾸는 속성
    [templates/common/main.html]
<!-- 해당 요소를 조각으로 지정된 속성으로 변경-->
<div replace="~{fragments/commons :: nav}"></div>

<!-- 랜더링 결과 -->
<!-- div 요소가 nav라는 이름의 조각으로 변환됨 -->
<nav>
<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>
<li><a href="/chatting">채팅</a></li>
</ul>
</nav>

th:replace="~{html 파일 경로}"

  • ::조각이름을 지정하지 않으면 html의 모든 내용을 얻어와 지정된 요소와 바꿈

[templates/common/main.html]

<!-- common/header.html의 모든 내용을 읽어와 th:block 태그와 바꿈 -->
<th:block replace="~{common/header}"></th:block>
<!-- common/footer.html의 모든 내용을 읽어와 th:block 태그와 바꿈 -->
<th:block replace="~{common/footer}"></th:block>

  • script 태그 내에 thymeleaf 문법을 작성 가능하도록 함
  • thymeleaf 문법으로 출력된 내용을 타입에 따라 알맞은 JS 형태로 변환

JS Inline - Text Rendering : [[...]]

  • 출력하고자하는 값을 그대로 출력

JS Inline - Natural Template : /[[...]]/ "설명"

  • HTML을 직접 열어도 동작하는 탬플릿
  • 주석을 통해서 thymelaef 컴파일 오류 해결
// message에 100 이라는 값이 저장되어 있을 경우
// message2에 "abc" 이라는 값이 저장되어 있을 경우

<script th:inline="javascript">
const message = [[${message}]];
const message2 = /*[[${message2}]]*/ "값";
</script>
// 랜더링 결과
// message 가 100으로 치환됨
<script th:inline="javascript">
const message = 100;
const message2 = "abc";
// HTML을 직접 열었을 때
const message2 = "값";
</script>

${#numbers.sequence(시작, 끝 [,step])}

  • th:each문에서 컬렉션 반복 접근이 아닌
    시작, 끝을 지정해서 반복할 때 사용
<p th:text=${member}></p>
<p th:text=${member.memberNo}</p>

th:classappend : 클래스를 동적으로 추가

<!-- child-comment 클래스 추가 -->
<li class="comment-row" classappend="child-comment">
<!-- 랜더링 결과 -->
<li class="comment-row child-comment">

<!-- 조건이 true일 경우 child-comment 클래스 추가 -->
<li class="comment-row" classappend="${comment.parentNo} != 0 ? child-comment">
<!-- 랜더링 결과 -->
<!-- true인 경우 -->
<li class="comment-row child-comment">
<!-- false인 경우 -->
<li class="comment-row">

${객체?.필드} : 안전 탐색 연산자(Safe Navigation Operator)

객체가 null인지 판별해서 null이 아닌 경우에 수행

0개의 댓글