스프링 MVC 2편 - 타임리프 기능

hyuk·2024년 1월 26일
0
post-thumbnail

📌 타임리프 소개

타임리프의 특징으로 크게 3가지가 있다.

  1. 서버 사이드 HTML 렌더링 (SSR)
    : 백엔드 서버에서 HTML을 동적으로 렌더링하는 용도로 타임리프가 사용된다.
  1. 내츄럴 템플릿
    : 타임리프는 순수 HTML을 최대한 유지하려는 특성을 가지고 있어, 웹 브라우저에서 파일을 열어도 내용을 확인할 수 있으며, 서버를 통해 뷰 템플릿을 거치면 동적으로 변경된 결과를 확인할 수 있다.

    순수 HTML을 유지하면서 뷰 템플릿도 사용할 수 있는 타임리프의 특징을 내츄럴 템플릿이라 한다.

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

📌 타임리프 기본기능

기본 표현식

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

  • 리터럴
    ◦ 텍스트 : 'one text', 'Another one!',…
    ◦ 숫자 : 0, 34, 3.0, 12.3,…
    ◦ 불린 : true, false
    ◦ 널 : null
    ◦ 리터럴 토큰 : one, sometext, main,…

  • 문자 연산 :
    ◦ 문자 합치기 : +
    ◦ 리터럴 대체 : |The name is ${name}|

  • 산술 연산 :
    ◦ Binary operators : +, -, *, /, %
    ◦ Minus sign (unary operator) : -

  • 불린 연산 :
    ◦ Binary operators : and, or
    ◦ Boolean negation (unary operator) : !, not

  • 비교와 동등 :
    ◦ 비교 : >, <, >=, <= (gt, lt, ge, le)
    ◦ 동등 연산 : ==, != (eq, ne)

  • 조건 연산 :
    ◦ If-then : (if) ? (then)
    ◦ If-then-else : (if) ? (then) : (else)
    ◦ Default : (value) ?: (defaultvalue)

  • 특별한 토큰 :
    ◦ No-Operation : _
    ex) <span th:text="${data}?: _">데이터가 없습니다.</span>

참고 : www.thymeleaf.org/doc/tutorials


텍스트 - text, utext

HTML 콘텐츠에 데이터 출력 시 - th:text

  • <span th:text="${data}">

HTML 태그의 속성이 아닌 HTML 콘텐츠 영역 안에서 데이터 출력 시 - [[. . .]]

  • 컨텐츠 안에서 직접 출력하기 = [[${data}]]
<ul>
 <li>th:text 사용 <span th:text="${data}"></span></li>
 <li>컨텐츠 안에서 직접 출력하기 = [[${data}]]</li>
</ul>

Escape, Unescape

HTML 문서는 <, > 같은 특수문자를 기반으로 정의된다.

"Hello <b>Velog</b>", <b> 태그를 사용하여 굵은 글씨로 표현하였을 때 ,

  • 웹 브라우저 : Hello <b>Spring!</b>
  • 소스보기 : Hello &lt;b&gt;Spring!&lt;/b&gt;

<b> 태그를 사용하여 강조하는 것이 목적이었지만, 그대로 출력되고 소스를 보면 < 부분이 &lt;로 변경됨을 확인할 수가 있다.

HTML에서 사용하는 특수 문자를 HTML 엔티티로 변경하는 것을 이스케이프(escape) 라고 한다. 그리고 타임리프가 제공하는 th:text, [[...]] 는 기본적으로 이스케이스(escape)를 제공한다.


그렇다면 이스케이프(escape) 기능을 사용하지 않으려면 어떻게 해야할까?

타임리프는 다음과 같은 Unescape 기능을 제공한다.

  • th:text → th:utext

  • [[...]][(...)]

1. text vs utext

<ul>
 <li>th:text = <span th:text="${data}"></span></li>
 <li>th:utext = <span th:utext="${data}"></span></li>
</ul>

2. [[...]] vs [(...)]

<ul>
 <li><span th:inline="none"> </span>[[${data}]]</li>
 <li><span th:inline="none"> </span>[(${data})]</li>
</ul>

th:inline="none" : 타임리프는 [[...]]를 해석하기에 해석하지 말라는 옵션

HTML 엔티티
: 웹 브라우저는 <를 HTML의 태그의 시작으로 인식하여, 문자로 표현할 수 있는 방법을 필요로 하는데 이를, HTML 엔티티라 한다.


변수 - SpringEL

타임리프에서 변수를 사용할 때, 변수 표현식을 사용한다.

SpringEL 다양한 표현식 사용

Object

  • "${user.username}" : user의 username을 프로퍼티 접근 → user.getUsername()

  • "${user['username']}" : 위와 동일 → user.getUsername()

  • "${user.getUsername()}" : user의 getUsername() 을 직접 호출

List

  • "${users[0].username}"
    : List에서 회원을 찾고 username 프로퍼티 접근 → list.get(0).getUsername()

  • "${users[0]['username']}" : 위와 동일

  • "${users[0].getUsername()}" : List에서 첫 번째 회원을 찾고 메서드 직접 호출

Map

  • "${userMap['userA'].username}"
    : Map에서 userA를 찾고, username 프로퍼티 접근 → map.get("userA").getUsername()

  • "${userMap['userA']['username']}" : 위와 동일

  • "${userMap['userA'].getUsername()}" : Map에서 userA를 찾고 메서드 직접 호출


유틸리티 객체

  • #message : 메시지, 국제화 처리
  • #uris : URI 이스케이프 지원
  • #dates : java.util.Date 서식 지원
  • #calendars : java.util.Calendar 서식 지원
  • #temporals : 자바8 날짜 서식 지원
  • #numbers : 숫자 서식 지원
  • #strings : 문자 관련 편의 기능
  • #objects : 객체 관련 기능 제공
  • #bools : boolean 관련 기능 제공
  • #arrays : 배열 관련 기능 제공
  • #lists, #sets , #maps : 컬렉션 관련 기능 제공
  • #ids : 아이디 처리 관련 기능 제공

유틸리티 객체들은 필요할 때 찾아서 사용하면 된다.


URL 링크

타임리프에서 URL을 생성할 때는 @{...} 문법을 사용

단순한 URL

  • @{/hello}/hello

쿼리 파라미터

  • @{/hello(param1=${param1}, param2=${param2})}
    • /hello?param1=data1&param2=data2
    • () 에 있는 부분은 쿼리 파라미터로 처리된다.

경로 변수

  • @{/hello/{param1}/{param2}(param1=${param1}, param2=${param2})}
    • /hello/data1/data2
    • () 에 있는 부분은 쿼리 파라미터로 처리된다.

경로 변수 + 쿼리 파라미터

  • @{/hello/{param1}(param1=${param1}, param2=${param2})}
    • /hello/data1?param2=data2
    • 경로 변수와 쿼리 파라미터를 함께 사용할 수 있다.

리터럴

리터럴은 소스 코드상에 고정된 값을 말하는 용어이다.

타임리프에서 문자 리터럴은 항상 ' (작은 따옴표)로 감싸야 하지만, 항상 ' (작은 따옴표)로 감싸는 것은 쉽지 않은 일이다.

이 때, 리터럴 대체 문법을 사용하면 템플릿을 사용하는 것과 같이 편리하다.

<!-- 주석 부분, 예외 발생 코드 -->
 <!--<li>"hello world!" = <span th:text="hello world!"></span></li>-->
 
 <li>'hello' + ' world!' = <span th:text="'hello' + ' world!'"></span></li>
 
 <li>'hello world!' = <span th:text="'hello world!'"></span></li>
 
 <li>'hello ' + ${data} = <span th:text="'hello ' + ${data}"></span></li>

 <li>리터럴 대체 |hello ${data}| = <span th:text="|hello ${data}|"></span></li>

속성 값 설정

타임리프는 주로 HTML 태그에 th:* 속성을 지정하는 방식으로 동작한다. th:* 로 속성을 적용하면 기존 속성을 대체한다. 기존 속성이 없으면 새로 만든다

th:checked = "false"/"true"

th:attrappend : 속성 값의 뒤에 값을 추가한다.

th:attrprepend : 속성 값의 앞에 값을 추가한다.

th:classappend : class 속성에 자연스럽게 추가한다.

HTML에서 checked 속성은 checked 속성의 값과 상관없이 checked 라는 속성만 있어도 체크가 된다. 타임리프의 th:checked의 값이 false일 경우, 속성 자체를 제거 !


자바스크립트 인라인

자바스크립트에서 타임리프를 편리하게 사용할 수 있는 자바스크립트 인라인 기능을 제공한다.

<script th:inline="javascript">

1. 텍스트 렌더링

var username = [[${user.username}]];

  • 인라인 사용 전 - var username = userA;
  • 인라인 사용 후 - var username = "userA";

인라인 사용 전 렌더링 결과는 우리가 기대한 "userA"가 아닌 userA 변수 이름이 그대로 남아있다. 결과적으로 userA가 변수명으로 사용되어 자바스크립트 오류가 발생한다.

인라인 사용 후 렌더링 결과는 문자 타입인 경우" 포함해주며, 문제가 될 수 있는 문자가 포함되어 있을 경우, 이스케이프 처리도 해준다.

2. 객체 렌더링

var user = [[${user}]];

  • 인라인 사용 전 - var user = Controller.User(username=userA, age=10);
  • 인라인 사용 후 - var user = {"username":"userA","age":10};

인라인 사용 전은 객체의 toString()이 호출된 값이며, 인라인 사용 후는 객체를 JSON으로 변환해준다

3. 자바스크립트 내추럴 템플릿

자바스크립트 인라인 기능을 사용하면 주석을 활용해서 이 기능을 사용할 수 있다

var username2 = /*[[${user.username}]]*/ "test username";

  • 인라인 사용 전 - var username2 = /*userA*/ "test username";
  • 인라인 사용 후 - var username2 = "userA";

인라인 사용 전 결과는 내추럴 템플릿 기능이 동작하지 않고, 심지어 렌더링 내용이 주석처리 되어 버린다.

인라인 사용 후 결과를 보면 주석 부분이 제거되고, 기대한 "userA"가 정확하게 적용된다.

템플릿 조각

웹 페이지에는 공통 영역들이 존재한다. 상단 영역, 하단 영역 그리고 카테고리를 구현한 영역들이 이에 해당한다고 할 수 있다. 이런 영역들은 매번 사용하여 구현한다면 비효율적이기에 타임리프는 템플릿 조각과 레이아웃 기능을 지원한다.

<footer th:fragment="copy">
  Footer Area. 
</footer>
  • th: fragment : 다른 영역에 포함되는 코드 조각

1. 부분 포함 insert

<div th:insert="~{template/fragment/footer :: copy}"></div>

<h2>부분 포함 insert</h2>
<div>
<footer>
  Footer Area. 
</footer>
</div>

th:insert를 사용하면 현재 태그(div) 내부에 추가한다.


2. 부분 포함 replace

<div th:replace="~{template/fragment/footer :: copy}"></div>

<h2>부분 포함 replace</h2>
<footer>
  Footer Area. 
</footer>

th:replace를 사용하면 현재 태그(div)를 대체한다.

3. 부분 포함 단순 표현식

<div th:replace="template/fragment/footer :: copy"></div>

<h2>부분 포함 단순 표현식</h2>
<footer>
  Footer Area. 
</footer>

~{...} 사용하는 것이 원칙이다. 템플릿 조각을 사용하는 코드가 단순하면 이 부분을 생략할 수 있다


📌 본 포스트는 스프링 MVC 2편 - 백엔드 웹 개발 핵심 기술 통해 학습한 내용을 요약 및 정리한 것입니다.

profile
차곡차곡쌓아가는학습노트

1개의 댓글

comment-user-thumbnail
2024년 12월 5일

타임리프까지... 웹프 100점 예약~

답글 달기