Web/Thymeleaf

[Thymeleaf] 02. 표준 표현식

  • -

이번 포스트에서는 표준 표현식을 이용해서 값을 출력하는 방법에 대해 알아보자.

 

표준 표현식

 

th:text

th:text는 메시지를 출력하기 위해 사용되는 속성이다.

표현식 설명
th:text="'some text'" 주어진 some text로 대체
th:text="#{property}" message source에 설정된 property를 활용하며 일반적으로 국제화(i18N)을 지원하기 위해 사용
th:text="${attribute}" request scope에 저장된 attribute를 출력

th:text와 유사하게 th:utext (utext: unescaped text)도 사용할 수 있다. text는 html 태그가 escape 처리되서 그냥 단순 문자열로 처리된다. 반면 utext는 html 태그가 escape 되지 않기 때문에 파서에 의해 파싱되서 출력된다.

@GetMapping("/text")
public String text(Model model) {
  model.addAttribute("name", "홍길동"); 
  model.addAttribute("strongName","<strong>hong</strong>");
  return "text";
}
<ul>
  <li th:text="'normal text'">some text</li>
  <li th:text="#{home.title}">property</li>
  <li th:text="${name}">attribute: text</li>
  <li th:utext="${strongName}">attribute: utext</li>
</ul>

 

객체의 출력과 OGNL

때로는 단순 scalar value 뿐 아니라 객체가 attribute로 전달될 수도 있다. 이때는 OGNL(Object Graph Navigation Languate)표현식을 사용해서 객체의 속성의 getter나 일반 메서드에 접근할 수 있다.

예를 들어 다음의 User 객체를 생각해보자.

@Data
@AllArgsConstructor
public class User {
    private String name;
    private int age;
    private Address addr;

    @Data
    @AllArgsConstructor
    static class Address {
        private String zip;
        private String detail;
    }
}

User는 멤버 변수들에 모두 private 접근 제한자가 설정되어있다. 따라서 이런 속성에 접근할 때에는 @Data에 의해 제공되는 public 한 getter/setter 들이 사용되어야 한다. 귀찮지만  encapsulation을 위해 어쩔 수 없다. 하지만 OGNL에서는 미리 약속된 몇가지 규칙들(private 한 mamber에 public한 setter/getter가 제공된다 등)에 의해 좀더 간단히 이런 멤버에 접근할 수 있게 한다.

<li th:text="${user.getName()}">사용자 정보: getter</li>
<li th:text="${user.name}">사용자 정보: property</li>
<li th:text="${user.addr.zip}">주소: . 연결</li>
<li th:utext="${user['addr']['zip']}">주소:대괄호 표현법</li>

이 방법은 객체의 getter를 통한 접근 외에도 Map 구조의 key를 이용하거나 배열 또는 List의 index를 통한 접근에도 사용된다.

model.addAttribute("contacts", Map.of("phone", "010-1234"));
model.addAttribute("friends", List.of("hong", "jang", "lim"));
        
<li th:text="${contacts.phone}">연락처: map</li>
<li th:utext="${friends[0]}">친구: list</li>

 

표현식 기본 객체1 - Base Object

Thymeleaf에서는 데이터 출력을 돕기 위해 표현식 기본 객체(Expression Basic Objects)를 지원하는데 이들도 당연히 OGNL형태로 사용된다.

먼저 Base Object로 #ctx와 #locale이 지원된다. (#{property}와 다르다.)

  • #ctx: org.thymeleaf.context.WebEngineContext를 나타내는 객체로 #vars와 같은 의미인데 #ctx가 권장 사항이다.
  • #locale: java.util.Locale에 대한 객체

사실 앞서 #{attribute}로 접근했던 속성들은 모두 #ctx의 속성을 좀 더 편하게 사용한 것들이다.

<li th:text="${user.name}">사용자 정보: property</li>
<!--같은 표현-->
<li th:text="${#ctx.user.name}">${#ctx.user.name}</li>
문서에는 #ctx를 통해 request, response, session, servletContext등 servlet의 주요 객체에 접근할 수 있다고 나와있다면 session을 제외하고는 모두 null이 반환된다. 또한 session도 더 쉽게 얻어오기 때문에 사실상 쓸 일은 없어보인다.

servlet을 사용하다보면 request의 parameter나 session/application의 attribute에 접근하는 경우가 정말 빈번하다. 이들은 각각 #ctx.param, #ctx.session, #ctx.application으로 접근 가능하다. 하지만 워낙 자주 사용되기 때문에 param, session, application 같은 예약어를 사용 할 수도 있다.(Thymeleaf에서는 이들을 namespace라고 부른다.) 

이들의 활용 예를 정리하면 다음과 같다.

표현식 구현 객체 및 설명 활용 예
#ctx
= 생략
org.thymeleaf.context.WebEngineContext
: request scope의 attribute
${#ctx.user.name}
${user.name}
#ctx.param
= param
org.thymeleaf.context.WebEngineContext.RequestParameterMap
: request의 parameter
${#ctx.param.addr}
${param.addr}
#ctx.session
= session
org.thymeleaf.context.WebEngineContext.SessionAttributeMap
: session scope의 attribute
${#ctx.session.sesAttr}
${session.sesAttr}
#ctx.application
= application
org.thymeleaf.context.WebEngineContext.ApplicationAttributeMap
: application scope의 attribute
${#ctx.application.appAttr}
${application.appAttr}

 

표현식 기본 객체2 - Expression Utility Object

추가로 Thymeleaf에서는 표현식에서 부가적인 메서드 작업들(숫자나 날짜의 포멧, 컬렉션 활용 등)을 위한 다양한 유틸리티 객체도 제공한다. 대부분 자바 코드를 웹에서 편하게 사용하기 위한 것들이므로 자바를 사용해본 경우라면 편하게 이해할 수 있는 내용들이다. 대충 이런게 있구나 살펴보고 필요하다면 전문을 찾아보자.

https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html#appendix-b-expression-utility-objects

 

Tutorial: Using Thymeleaf

1 Introducing Thymeleaf 1.1 What is Thymeleaf? Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text. The main goal of Thymeleaf is to provide a

www.thymeleaf.org

 

여기서는 자주 사용되는 몇 가지에 대해서 살펴보자. 참고로 모두 다 복수형이다.

객체 설명 활용 예
#messages message source의 property를 가져오기 위한 것으로 #{...}과 동일 ${#messages.msg('msg')}
#{msg}
#dates
#calendars
#temporals
각각 java.util.Date, java.util.Calendar, java.time 패키지의 기능 제공을 제공하며 사용법은 비슷 ${#dates.format(now, 'yyyy-MM-dd(E)')}
${#dates.day(now)}
#numbers 주로 숫자의 형식화 관련 기능 제공 ${#numbers.formatInteger(num, 3, 'COMMA')}
${#numbers.formatCurrency(num)}
#strings isEmpty, defaultString, contains, startsWith, escapeXml 등 문자열과 관련된 다양한 메서드 제공 ${#strings.isEmpty(str)}
${#strings.defaultString(notExist,'default')}
${#strings.contains(str, 'll')}
${#strings.escapeXml(str)}
#arrays length, isEmpty, contains, containsAll 등 배열과 관련된 메서드 제공 ${#arrays.isEmpty(arr)}
${#arrays.contains(arr,'A')}
${#arrays.length(arr)}
#lists size, isEmpty, contains, sort 등 List와 관련된 메서드 제공 ${#lists.isEmpty(list)}
${#lists.contains(list,'A')}
${#lists.sort(list)}

 

선택된 객체의 활용과 *{...}

Thymeleaf에서는 상위 태그에 특정 객체를 선택해 놓고 하위 태그에서 선택된 객체의 내용에 손쉽게 접근하는 방법을 제공한다. 이때 객체를 선택할 때에는 th:object를 사용하고 *{property}는 선택된 객체의 속성에 접근할 수 있도록 한다. 따라서 좀 더 간결히 코드를 작성할 수 있다. 참고로 선택된 객체를 참조하기 위해서는 #objct를 사용할 수 있다.

<li th:text="*{name}">*{name}</li>
<li th:text="${user.name}">${user.name}</li>
<li th:text="${#object.name}">#object.name</li>

 

URL 작성

 

th:href=@{경로표현}

URL을 작성할 때 Thymeleaf는 @{...} 형태를 사용한다.

일단 URL은 어디서나 그렇듯 절대경로와 상대경로가 존재한다.

경로구분 설명 예시
절대경로 container root를 나타내는 경로 http://www.thymeleaf.org 
http://192.168.0.1:8080
상대경로 context 상대 경로 현재 context의 이름이 자동으로 삽입되는 경로 /itemdetails?id=3
페이지 상대 경로 현재 페이지를 기준으로하는 경로 user/login.html
./user/login.html
서버 상대 경로 동일 서버의 다른 context의 리소스를 호출하는 경로 ~/billing.processInvoice
일반적으로는 http:// 형태의 경로는 외부경로, /로 시작하는 경로를 절대경로라고 하는데 Thymeleaf는 좀 독특하다.?

URL을 만들면서 일반적으로 파라미터를 구성할 때 &key=value 형태의 쿼리스트링을 구성하지만 Thymeleaf에서는 (key1=value,key2=value)형태를 사용한다. 또한 path variable 을 구성하는데도 동일한 방식을 사용할 수 있다.

간단하니까 바로 예를 살펴보자.

<!-- 생성결과, 최종경로: http://www.google.com/?q=thymeleaf -->
<a href="test.html" th:href="@{http://www.google.com(q=thymeleaf)}">thymeleaf</a>

<!-- 생성결과: /ognl?addr=seoul&amp;lang=ko_KR, 최종경로: http://localhost:8080/ognl?addr=seoul&lang=ko_KR -->
<a href="test.html" th:href="@{/ognl(addr=${user.addr},lang=ko_KR)}">context-relative</a>

<!-- 생성결과: ./text, 최종경로: http://localhost:8080/text -->
<a href="test.html" th:href="@{./text}">page relative</a>

<!-- 생성결과: /seoul?lang=ko_KR, 최종경로: http://localhost:8080/seoul?lang=ko_KR -->
<a href="test.html" th:href="@{/{addr}(addr=seoul,lang=ko_KR)}">path variable</a>

 

기타

 

${...}, *{...}, #{...}과 리터럴의 결합

특히 문자열과 ${...}, *{..}, #{...}을 결합해서 문장을 구성해야 하는 경우 홑따옴표와 겹따옵표를 지저분하게 사용해야 하는 경우가 많다.

<span th:text="'Welcome to our application, ' + ${user.name} + '!'">literal</span>

이런 경우 '|'를 이용하면 format 문자를 사용하는 것처럼 편리하게 문장을 완성할 수 있다.

<span th:text="|Welcome to our application, ${user.name}!|">| 활용</span>

 

Elvis 연산자(?:)를 이용한 기본값 표현

Thymeleaf에서는 참조하는 대상이 null인 경우는 아무런 값도 출력하지 않는다. 그래서 원래 값이 null인지 아니면 빈 값인지 판단하기 위해서 3항연산자등을 써야하는 번거로움이 있다. 이때 Elvis 연산자(?:)를 사용하면 쉽게 기본 값을 할당 할 수 있다.

<li th:text="${hobby}">표현 안됨</li>
<li th:text="${#strings.isEmpty(hobby)?'아직 취미 없음':hobby}">3항연산자</li>
<li th:text="${hobby}?:'아직 취미 없음'">Elvis Operator</li>
<li th:text="${#strings.defaultString(hobby,'아직 취미 없음')}">#strings</li>
<li th:text="${hobby}?:(${user.name}?${user.name}:'아직 취미 없음')">Elvis Operator</li>

 

속성값 설정

 

th:attr

Thymeleaf에서 속성값을 설정할 때는 th:attr="attr_name=attr_value"를 사용한다. 그리고 attr_value는 속성의 타입에 따라 ${...}, #{...}, @{...} 등을 사용할 수 있다.

예를 들어 다음과 같은 form을 만들어야 한다고 생각해보자.

<form action="/login">
  <fieldset>
    <input type="text" name="email" value="admin@quietjun.com"/>
    <input type="password" name="pass" />    
    <input type="submit" value="로그인"/>
  </fieldset>
</form>

여기서 action 속성은 URL 설정이 필요하고 submit의 value에는 국제화가 적용된 property가 좋을것 같다. 그리고 email의 value에는 현재 로그인한 사용자의 email을 보여준다고 생각하면 다음과 같이 작성해볼 수 있다.

<form action="login.html" th:attr="action=@{'/login'}">
  <fieldset>
    <input type="text" name="email" value="abc@def.net" th:attr="value=${session.loginEmail}"/>
    <input type="password" name="pass" />    
    <input type="submit" value="submit" th:attr="value=#{login}"/>
  </fieldset>
</form>

 

좀 더 편한 설정

하나의 태그에 th:attr은 하나만 사용할 수 있다. 따라서 만약 여러 개의 속성을 설정해야 한다면 comma(,)로 연결해서 사용 가능하다.

<a href="product/list.html" th:attr="href=@{/product/list},title=#{list}">제품목록</a><br>

매번 th:attr="key=value"형태를 사용할 수도 있지만 축약형으로 th:key="value"형태를 사용할 수도 있다. 

<a href="product/list.html" th:href="@{/product/list}" th:title="#{list}">제품목록</a>

특히 class 속성의 경우 고정적인 값을 가지고 있고 동적으로 추가해야하는 경우가 있는데 이때는 th:attrappend 또는 th:attrprepend를 사용할 수 있다. 그리고 이런 일은 거의 class에서만 발생하므로 th:classappend 같은 전용의 속성도 제공된다.

<span class="red" th:attrappend="class=${' '+session.loginEmail}">span</span>

<span class="red" th:classappend="${session.loginEmail}">span</span>

 

'Web > Thymeleaf' 카테고리의 다른 글

[Thymeleaf] 06. inline style  (1) 2024.01.14
[Thymeleaf] 05.레이아웃 재사용  (0) 2024.01.14
[Thymeleaf] 04. 레이아웃  (0) 2023.12.14
[Thymeleaf] 03. 제어문  (0) 2023.12.14
[Thymeleaf] 01. Hello Thymeleaf  (0) 2023.12.09
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.