[코드로 배우는 스프링부트 웹 프로젝트] - 스프링 MVC, Thymeleaf

Jongwon·2022년 12월 7일
0

Thymeleaf의 장점

  • ${}를 별도의 처리없이 사용할 수 있다
  • Model에 담긴 객체를 JS로 처리하기 편리하다
  • 연산이나 포맷과 관련된 기능을 추가적인 개발없이 지원가능하다
  • .html파일로 생성하는데 문제가 없고 별도의 확장자를 이용하지 않는다

Dependency에 Thymeleaf를 추가한 프로젝트를 생성한 뒤, application.properties에 아래 문장을 추가합니다.
spring.thymeleaf.cache=false
위의 설정은 타임리프 템플릿을 캐싱해두지 않겠다는 의미입니다. 타임리프는 처음 시작했을때만 템플릿을 파싱하고, 그 내용을 캐시에 저장해두기 때문에 중간에 코드를 수정한다고해서 바로 템플릿에 반영되지는 않습니다.
이 기능은 실제 서비스를 운영할때 불필요한 파싱을 줄여주기 때문에 이득이지만, 개발을 진행하는 중에는 오히려 계속 재시작을 시켜야 변경사항을 볼 수 있게 되어 불편합니다.

기본 사용법

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">  //html 태그에 타임리프 html임을 명시
<head>
...

위와 같이 html 태그에 명시적으로 thymeleaf를 사용할 것임을 적어둔 뒤, th:를 적고 속성값을 넣어 사용할 수 있습니다.

몇가지 예를 들자면

  • 반복문
<body>
    <ul>
        <li th:each="dto: ${list}"> //Controller에 있는 list를 전달 받음, th:each로 반복문 수행
            [[${dto}]]  //인라인표현식, 태그 지정 없이 쓰고싶을 때 사용
        </li>
    </ul>
</body>

또한 반복문에는 state를 사용할 수 있습니다. state를 통해 index를 지정하거나(index), 반복문의 현재까지 반복횟수(count), 반복문 크기(size), 현재 가리키고 있는 객체(current)를 알 수 있습니다.

<li th:each="dto, state : ${list}">
	[[${state.index}]] --- [[${dto}]]
</li>
  • 제어문
<li th:each="dto, state : ${list}" th:if="${dto.sno % 5 == 0}"> //th:if 사용
	[[${dto}]]
</li>

위와 같이 하나의 태그 안에 여러 th: 속성을 넣을수도 있습니다. 반복문은 th:ifth:unless가 있는데, 다른 언어들처럼 if~else로 묶여야만 하는것이 아니라, 별도로 처리할 수 있습니다.

<li th:each="dto, state : ${list}">
	<span  th:if="${dto.sno % 5 == 0}" th:text="${'-----------------'+dto.sno}"></span>
	<span th:unless="${dto.sno % 5 == 0}">[[${dto.first}]]</span>
</li>

삼항연산자도 이용할 수 있습니다. 특이한 점은 삼항연산자의 마지막행을 생략해서 if문처럼 사용도 가능하다는 점입니다.

<li th:each="dto, state : ${list}" th:text="${dto.sno % 5 == 0} ? ${dto.sno} : ${dto.first}">
<li th:each="dto, state : ${list}" th:text="${dto.sno % 5 == 0} ? ${dto.sno}">

위의 방법을 응용해서 특정 상황에서만 클래스를 지정하여 CSS를 넣는 등의 작업을 할 수 있습니다.

<li th:each="dto, state : ${list}" th:class="${dto.sno % 5 == 0} ? 'target'">  //th:class 사용

th:inline=은 인라인 설정인데, 뒤에 text 뿐만이 아니라 javascript도 올 수 있습니다. 인라인 속성에 관한 설명은 아래 링크에서 잘 정리되어 있으니 참고바랍니다.
https://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html
https://www.concretepage.com/thymeleaf/thymeleaf-javascript-inline-example-with-variable

<script th:inline="javascript">
    var msg = [[${result}]];
    var dto = [[${dto}]];
</script>

th:block은 태그 없이 사용할 수 있는 타임리프 속성으로 실제 화면에서는 html로 처리되지 않기 때문에 루프등을 한번에 처리하기에 좋습니다.

<th:block th:each="dto: ${list}">
	<li th:each="dto, state : ${list}" th:text="${dto}">
	</li>
</th:block>

th:href=@{}로 링크 처리를 할 수 있습니다.

<a th:href="@{/sample/exView/{sno}(sno=${dto.sno})}">[[${dto}]]</a>

이 외에도 Thymeleaf는 여러가지 기본 객체와 Expression Utility 객체를 지원합니다. 하나하나 설명하기에는 너무 많기 때문에 아래 공식문서를 참고해주세요.

https://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#appendix-a-expression-basic-objects

교재에는 LocalDateTime관련 처리가 복잡하기 때문에 별도의 API를 추가하라고 되어있지만, 현재에는 Expression Utility 객체 기능에 포함이 되어 있는 것 같아 따로 기술하지 않겠습니다.

th:fragment는 HTML에서 fragment를 나타냅니다. 쉽게 생각하면 CSS를 사용할 때 HTML에 #id를 지정해주는 것과 비슷하다고 볼 수 있습니다.

<div th:replace="~{/fragments/fragment1 :: part1}"></div>

이것을 사용하고 싶다면 th:replaceth:insert를 이용하면 됩니다.

<h1>Layout 1 - 1</h1>
<div th:replace="~{/fragments/fragment1 :: part1}"></div>

<h1>Layout 1 - 2</h1>
<div th:insert="~{/fragments/fragment1 :: part2}"></div>

<h1>Layout 1 - 3</h1>
<th:block th:replace="~{/fragments/fragment1 :: part3}"></th:block>

셋의 차이는 생성된 HTML 소스에서 확인할 수 있습니다.

첫번째와 세번째와 같이 th:replace를 사용하는 경우, 해당 자리의 div를 대체하여 생성하는 반면에, th:insert를 사용하는 경우, div안에 또다시 div를 생성하여 내용을 넣습니다.
th:replaceth:insert를 사용할 때 :: 뒤에 fragment이름 뿐만이 아니라 #id의 선택자를 넣어도 동작합니다.
::이 생략된다면 해당 파일 전체를 의미합니다.

th:fragment의 대상을 target부분에 파라미터를 받을 수 있습니다.

fragment3.html
<div th:fragment="target(first, second)">

exLayout2.html
<th:block th:replace="~{/fragments/fragment3::target(~{this:: #ulFirst},~{this::#ulSecond})}">

~{ } 표시는 fragment 경로를 가리키고 있음을 의미합니다.

profile
Backend Engineer

0개의 댓글