DAY59 - 타임리프(Thymeleaf)

은나현·2023년 4월 18일
0

📌 1. 타임리프의 특성

📍 1-1. 서버사이드 HTML 렌더링

  • 타임리프는 백엔드 서버에서 (jsp처럼) HTML을 동적으로 렌더링하는 용도로 사용된다.

📍 1-2. 네츄럴 템플릿

  • 타임리프에는 순수 HTML을 최대한 유지하는 특징이 있다.
    • 타임리프로 작성한 파일은 HTML을 유지하기 때문에 웹 브라우저에서 파일을 직접 열어도 내용을 확인할 수 있다.
    • 서버를 통해 뷰 템플릿을 거치면 동적으로 변경된 결과를 확인할 수 있다.

📍 1-3. 스프링 통합 지원

  • 타임리프는 스프링과 자연스럽게 통합되고, 스프링의 다양한 기능을 편리하게 사용할 수 있게 지원한다.

📌 2. 기본 표현식

📍 2-1. 간단한 표현

  • ${...} : 변수 표현식
  • *{...} : 선택 변수표현식
  • #{...} : 메시지 표현식
  • @{...} : 링크 URL 표현식
  • ~{...} : 조각 표현식

📍 2-2. 리터럴

  • 텍스트 : 'one text', ...
  • 숫자 : 0, 34, 3.0
  • 불린 : true, false
  • 널 : null

📍 2-3. 문자, 산술, 불린 연산

  • 문자 연산
    • 문자 합치기 : +
    • 리터럴 대체 : |The Name is ${name}|
  • 산술 연산 : + , - , * , / , %
  • 불린 연산 : and , or , ! , not

📍 2-4. 비교와 동등, 조건 연산

  • 비교 : > , < , >= , <= (gt, lt, ge, le)
  • 동등 : == , != (eq,ne)
  • 조건 연산 : if-then , if-then-else

📌 3. 텍스트 출력

📍 3-1. 텍스트 출력

  • 데이터를 텍스트로 출력
    • 텍스트 안에서 직접 출력 : [[${data}]]
    • 태그 출력 : <span th:text="${data}"></span>
  • 데이터를 unescaped 태그 언어로 출력
    • 텍스트 안에서 직접 출력 : [(${data})]
    • 태그 출력 : <span th:utext="${data}"></span>
      • 출력 예시

📌 4. 표준 표현식 구문

📍 4-1. Spring EL

  • 단일 객체
    • ${객체이름.필드이름}
      • 예) <span th:text="${user.username}"></span>
    • ${객체이름['필드이름']}
      • 예) <span th:text="${user['username']}"></span>
    • ${객체이름.필드 게터 메서드()}
      • 예) <span th:text="${user.getUsername()}"></span>
  • List 객체 : 인덱스로 접근
    • 예) <span th:text="${users[0].username}"></span>
  • Map 객체 : 키값으로 접근
    • 예) <span th:text="${userMap['userA'].username}"></span>

  • 지역변수 : th:with으로 지정하고 태그 내에서 사용
    	<div th:with="first=${users[0]}">
    		<p>처음 사람의 이름은 <span th:text=${first.username}></span></p>
    	</div>
    	<div th:with="second=${users[1]}">
    		<p>처음 사람의 이름은 <span th:text=${second.username}></span></p>
    	</div>	

📍 4-2. 타임리프 기본 객체들

  • request, response, session, servletContext, locale 이 있다.
  • 접근법 : ${#객체명} (예 : ${#request})
    • 출력 예시
    	<h1>식 기본 객체</h1>
    	<ul>
    		<li>request = <span th:text="${#request}"></span></li>
    		<li>response = <span th:text="${#response}"></span></li>
    	//	session은 컨트롤러에서 HttpSession을 파라미터로 전달받아야 생성됨	
      		<li>session = <span th:text="${#session }"></span></li>
    		<li>servletContext = <span th:text="${#servletContext}"></span></li>
    		<li>locale = <span th:text="${#locale}"></span></li>
    	</ul>

📍 4-3. 편의 객체

  • 파라미터 출력
    • 리퀘스트에서 getParameter로 가져와서 출력
      : <span th:text="${#request.getParameter('paramData')}"></span>
    • 타임리프 예약어 param으로 출력
      : <span th:text="${param.paramData}"></span>
  • 세션 출력
    • 세션.속성명
      : <span th:text="${session.sessionData}"></span>
  • @Component 객체 연동
    • 컴포넌트로 등록된 객체 예시
      @Component("helloBean")
      public class HelloBean {
      	public String hello(String data) {
      		return "spring data " + data;
      	}
      }
    • 메서드 연동 출력

      <span th:text="${@helloBean.hello('user')}"></span>

📍 4-4. 날짜/시간 출력

  • 컨트롤러에서 생성한 localDateTime 단순 출력
    : <span th:text="${localDateTime}"></span>
  • 포맷에 따른 출력
    : <span th:text="${#temporals.format(localDateTime,'yyyy-MM-dd HH:mm:ss')}"></span>
  • 기타 출력 가능한 값들

📍 4-5. 링크 URL 생성

  • 단순한 URL
    : @{/hello}
    • 결과 URL -> /hello
  • 쿼리 파라미터
    : @{/hello(param1=${param1}, param2=${param2})}
    • ()에 있는 부분은 쿼리 파라미터로 처리된다.
      결과 URL -> /hello?param1=data1&param2=data2
  • 경로 변수
    : @{/hello/{param1}/{param2}(param1=${param1},param2=${param2})}
    • URL 경로상에 변수가 있으면 ()부분은 경로 변수로 처리된다.
      결과 URL -> /hello/data1/data2
  • 경로 변수 + 쿼리 파라미터
    : @{/hello/{param1}(param1=${param1},param2=${param2})}
    • 경로 변수와 쿼리 파라미터를 함께 사용할 수 있다.
      결과 URL -> /hello/data1?param2=data2

📍 4-6. 리터럴

  • 리터럴은 소스 코드상에 고정된 값을 말하는 용어이다.
    • 타임리프에서는 문자 리터럴은 항상 ''(작은 따옴표)로 감싸야 한다.
      <span th:text="'hello'">
    • 문자를 항상 ''로 감싸는 것은 번거롭기 때문에 공백 없이 이어진다면 하나의 의미 있는 토큰으로 인지하여 다음과 같이 작은 따옴표를 생략 가능하다.
      <span th:text="hello">
    • 중간에 공백이 있으면 하나의 의미있는 토큰으로 인식되지 않아 오류가 발생한다.
      <span th:text="hello world"> (오류)
      <span th:text="'hello world'"> (정상동작)
  • 문자열과 데이터를 결합할 때는 +연산자를 사용할 수도 있지만, 리터럴 대체||를 사용하면 생략 가능하다.
    • + : <span th:text="'hello ' + ${data}"></span>
    • || : <span th:text="|hello ${data}|"></span>

📍 4-7. 연산

  • 산술 연산
    • <span th:text="10 + 2"> -> 결과 : 12
  • 비교 연산
    • <span th:text="1 gt 10"></span> -> 결과 : false
  • 삼항연산자
    • <span th:text="(10 % 2 == 0)?'짝수':'홀수'"></span> -> 결과 : 짝수
  • Elvis 연산자
    • <span th:text="${Data}?: '데이터가 없습니다.'"></span>
      -> 결과 : 데이터가 있으면 데이터값 / 없으면 '데이터가 없습니다.'
  • No-Operation
    • <span th:text="${Data}?:_">데이터가 없습니다.</span>
      -> 결과 : 데이터가 있으면 데이터값 / 없으면 '데이터가 없습니다.'

📌 5. 타임리프 기능

📍 5-1. 속성 설정

  • th:* 속성을 지정하면 타임리프는 기존 속성을 해당 속성으로 대체한다. 기존 속성이 없다면 새로 만든다.
    • <input type="text" name="mock" th:name="userA">
      -> 결과 : name="userA"로 렌더링
  • 기존 속성을 조작하거나 추가할 수 있다.
    • th:attrappend : 속성 값 뒤에 값을 추가
    • th:attrprepend : 속성 값 앞에 값을 추가
    • th:classappend : class 속성에 추가
        	<input type="text" class="text" th:attrappend="class='large'">
          // 결과 class="textlarge"
      	<input type="text" class="text" th:attrprepend="class='large'">
          // 결과 class="largetext"
      	<input type="text" class="text" th:classappend="large">
          // 결과 class="text large"
  • checked 속성은 본래 존재 유무로 확인하지만 th:checked 속성은 true, false로 설정한다.
    • <input type="checkbox" name="active" th:checked="false">

📍 5-2. 반복문

  • th:each로 반복문을 구현한다.
    // users - 객체들로 이루어진 list
    		<tr th:each="user: ${users}">
    			<td th:text="${user.username}">username</td>
    			<td th:text="${user.age}">0</td>
    		</tr>	
  • 반복문에서 두번째 파라미터를 설정해 반복 상태를 확인할 수 있다.
    • 두번째 파라미터는 생략 가능한데, 생략하면 지정한 변수명+Stat가 된다.
      <tr th:each="user, userStat : ${users}">
          <td th:text="${userStat.count}">0</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>
          </td>
      </tr>

📍 5-3. 조건부 평가

  • if / unless
    : if는 조건 충족, unless는 조건 불충족 시에 실행된다.

    • 타임리프는 해당 조건이 맞지 않으면 태그 자체를 렌더링하지 않는다.

      <tr th:each="user,userStat : ${users}">
          <td th:text=${userStat.count}>count</td>
          <td th:text=${user.username}>username</td>
          <td>
            <span th:text="${user.age}"></span>
            // if, unless 평가
            <span th:text="'미성년자'" th:if="${user.age lt 20}"></span>
            <span th:text="'성인'" th:unless="${user.age lt 20}"></span>
          </td>
      </tr>	

  • switch

    • 조건을 만족하지 않으면 해당 tag가 조회되지 않는다.

      			<tr th:each="user,userStat : ${users}">
      				<td th:text=${userStat.count}>count</td>
      				<td th:text=${user.username}>username</td>
      				<td th:switch="${user.age}">
      					<span th:case="10">10살</span>
      					<span th:case="20">20살</span>
      					<span th:case="*">기타</span>
      				</td>						
      			</tr>	

📍 5-4. 주석 및 블록

  • 표준 HTML 주석 형태는 다음과 같다.
    <!-- <span th:text="${data}"></span> -->
  • 타임리프 파서 주석을 이용하면 화면뿐 아니라 소스코드에서도 렌더링되지 않게 만들 수 있다.
    • 한 줄 주석 : <!--/* [[${data}]] */-->
    • 여러 줄 주석
      		<!--/*-->
      		<span th:text="${data}"></span>
      		<!--*/-->
        // 또는
      		<!--/*
      		<span th:text="${data}"></span>
      		*/-->	
      • 타임리프 파서 주석은 렌더링 시 보이지 않는다.
  • th: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>

📍 5-5. 자바스크립트 인라인

  • 자바스크립트 영역에 자바스크립트 인라인을 사용하면 타임리프가 관여해서 도와준다. 변수값을 바로 가져와서 사용할 수 있다.
    	<script th:inline="javascript">
    		let username = [[${user.username}]];
    		let age = [[${user.age}]];
    	</script>
  • 자바스크립트 내추럴 템플릿 표기법을 사용하면 서버에 올려 실행했을 때는 타임리프 값, html을 바로 실행했을 때는 기본 값으로 보여 주어 오류가 나지 않게 할 수 있다.
    let username2 = /*[[${user.username}]]*/ "test username";
  • 객체를 그대로 담으면 JSON 형태로 받아온다.
    let user = [[${user}]];
  • 자바스크립트 내에서 자바단 변수를 사용해 반복문을 실행할 수 있다.
    	<script th:inline="javascript">
    		[# th:each="user, stat : ${users}"]
    			let user[[${stat.count}]] = [[${user}]];
    		[/]
    	</script>

📍 5-6. 템플릿 레이아웃

  • th:fragment를 태그에 붙여 다른 html파일에서 가져와 사용할 수 있다.
    <footer th:fragment="copy">footer 자리입니다.</footer>

    • th:insert의 경우 태그 안에 가져온 태그를 삽입하고, th:replace의 경우 태그를 대체한다.

    • 문법은 ~{가져올 태그가 있는 파일 경로 :: fragment이름}이다.

      	<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>
      • 소스코드에서 삽입과 대체의 차이
  • fragment에 파라미터를 붙여 메서드처럼 가져와 사용할 수 있다.

    	<footer th:fragment="copyParam(param1,param2)">
    		<p>파라미터 자리입니다.</p>
    		<p th:text="${param1}"> </p>
    		<p th:text="${param2}"> </p>
    	</footer>	
    • <h1>파라미터 사용</h1>
      <div th:replace="template/fragment/footer :: copyParam('data1','data2')"></div>	

0개의 댓글