[Spring Boot Tutorial] 5. Thymeleaf 템플릿 엔진 이용하기

2019. 10. 21. 14:25Spring/Spring Boot Tutorial

반응형

Thymeleaf template engine?

spring 기반 웹 애플리케이션의 뷰 페이지에서 html, xml, javascript, css, text 처리 후 웹 브라우저에 표시할 때 이용되는 템플릿 엔진의 일종으로 JSP보다 빠르다는 장점이 있습니다.

이전과정에 이어서 진행됩니다.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
	<groupId>org.thymeleaf.extras</groupId>
	<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

이번 과정에서 타임리프 템플릿 이용을 위해 필요한 의존성 라이브러리는 위와 같습니다.
thymeleaf 스타터 폼과 springsecurity 사용을 위한 라이브러리를 추가합니다.

Layout Dialect는 이번 과정에서 다루지 않기 때문에 추가하지 않았습니다.


spring:
  mvc:
    static-path-pattern: /static/**
  thymeleaf:
    mode: HTML
    encoding: UTF-8
    check-template-location: true
    prefix: classpath:/templates/
    suffix: .html
    cache: false
  resources:
    static-locations:
    - classpath:/static/
    cache:
      period: 0

타임리프와 관련된 프로퍼티 키 값입니다.
위에 설정된 값들 중, sprirng.mvc.static-path-patternspring.thymeleaf.cache, spring.resources.static-locations 그리고 spring.resources.cache.period를 제외한 나머지 요소들은 모두 디폴트 값입니다.


타임리프 템플릿 관련 설정

  • ln 6: 기본 인코딩 값을 바꾸고 싶을 경우 설정
  • ln 8: 템플릿 문서 경로를 변경하고 싶을 경우. 설정한다. 디폴트는 classpath:/templates/이기 때문에, 아래 [그림 7-1] 문서 구조에서 main.html 문서를 열고자 할때에는 classpath:/templates/content/main.html 에서 prefix와 suffix를 제외하고 content/main 이라고 작성하면 됩니다. 만약, [그림 7-2]의 위치에 템플릿을 위치시키고 싶다면, WEB-INF/templates 라고 설정해주면 됩니다.
    39 [그림 7-1]
    40 [그림 7-2]
  • ln 10: 타임리프 템플릿 문서의 캐시 사용 유무 설정. 로컬 테스트 환경에서는 false로 설정하고 실서버에서는 true로 설정하는 것을 권장합니다.

정적파일(js, css, images) 관련 설정

  • ln 3: 정적파일로 인식할 prefix 패턴. default값은 /** 입니다.
  • ln 12~13: 정적파일 위치. 만일 정적파일의 위치를 변경하고자 한다면 이 값을 변경해 주면 됩니다.
  • ln 15: 정적파일의 캐시 유효기간을 설정합니다.

템플릿 관련 설정을 모두 마쳤다면, 이젠 템플릿 문서를 만들러 가야겠네요.

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

먼저, 문서의 root 요소인 <html>에 타임리프를 사용하기 위해 필요한 xml namespace를 설정합니다.

우리는 th와 sec xml 네임스페이스를 추가했습니다.


html로 문서를 만들거에요.
<body> 내에 <header>, <nav>, <section>, <footer>를 성질에 따라 구분을 하여 레이아웃 디자인을 할 예정입니다.

41


그 틀안에서 컨텐츠가 들어갈 부분은 아래와 같습니다.
웹 디자인은 개발자가 새로이 지식을 얻는것은 어려움이 많기 때문에 저는 부트스트랩을 이용하여 그리드 디자인을 할 것입니다.

만약, 부트스트랩에 대한 지식이 필요하다면, w3schools.com 사이트를 참고해주세요.

42


  • 맨위에는 로고와 회원정보 및 로그아웃 버튼이 위치할 header
  • 네비게이션 바(메뉴)가 위치할 nav
  • 페이지에 따라 내용이 바뀔 section
  • 웹페이지 정보 및 기타 링크가 위치할 footer

1. header

<header>
	<a th:href="@{/v}"><img th:src="@{/static/img/like.png}" width="50" height="auto" alt="demo page" id="btn_home" /></a>
	<ul>
		<li sec:authorize="isAuthenticated()">
			<span sec:authentication="principal.username"></span> 님 반가워요!
		</li>
		<li><form id="logoutFrm" th:action="@{/logout}" method="post" style="display: inline-block;">
				<a href="#" onclick="document.getElementById('logoutFrm').submit()" data-toggle="tooltip"
					data-placement="logout" title="Logout"><i class="fa fa-power-off"></i></a>
			</form></li>
	</ul>
</header>

ln 2: 정적파일인 이미지파일의 경로는 classpath:/static 폴더 아래의 /img에 위치합니다. 타임리프 템플릿에서는 contextpath를 th:src="@{}" 와 같이 th xml 네임스페이스를 이용해서 주소를 자동 세팅할 수 있습니다. 기존의 src를 이용하면 자동을 설정하지 못하지 주의 해야합니다.

ln 4~5: sec xml 네임스페이스를 이용하여 spring security 인가 정보를 이용할 수 있습니다. 인증된 사용자의 경우 username을 가져와서 표기하도록 설정했습니다.
만일, 인증되지 않은 사용자의 경우 이 li태그 포함 하위 태그는 페이지에 표시되지 않습니다.


2. nav

<nav class="navbar navbar-expand-lg navbar-light" id="nav_area">
	<button class="navbar-toggler" type="button" data-toggle="collapse"
		data-target="#nav_menu_area">
		<span class="navbar-toggler-icon"></span>
	</button>
	<div class="collapse navbar-collapse" id="nav_menu_area">
		<ul class="navbar-nav" id="n_menu">
			<li class='nav-item'  th:classappend="${currentPage eq 'home'} ? 'active' : ''"
				th:attr="data-href=@{/v}"></li>
			<li sec:authorize="hasRole('ROLE_ADMIN')" class='nav-item'
				th:classappend="${currentPage eq 'user'} ? 'active' : ''"
				th:attr="data-href=@{/v/users}">회원</li>
		</ul>
	</div>
</nav>

ln 8~9: 네비게이션 바에 있는 메뉴를 클릭 할 시, 선택된 메뉴를 active 처리하기 위해 ${currentPage} 속성을 이용한다. th:classappend를 이용하여 만일 currentPage가 home일 경우 active 클래스를 추가한다.

ln 10: 로그인한 사용자가 'ROLE_ADMIN' 롤을 가지고 있을 경우에만 회원 메뉴가 생성되도록 인가처리를 했다.


<section>
	<div class="container">
		<div class="d-title">
			<h2>thymeleaf tutorial 과정</h2>
		</div>
		<div class="d-content">
			<div class="row">
				<ul>
					<li>thymeleaf 관련 configuration 설정하기</li>
					<li>thymeleaf로 웹 페이지 구성하기</li>
					<li>JSP vs thymeleaf 비교하기</li>
					<li>thymeleaf layout 구성하기</li>
				</ul>
			</div>
		</div>
	</div>
</section>
<footer>
	<p>
		JINI WORLD - DEMO
	</p>
</footer>

section 영역에는 컨텐츠를 입력하고, footer에 바닥글을 설정합니다.
(타임리프 기능을 사용한 것이 없기에 별도의 추가 설명은 생략합니다.)


위의 코드들에 css를 적용한 결과입니다.

43

이번에는, 회원 페이지를 만들어볼까요?
회원 페이지의 경우, <header>, <nav>, <footer> 태그 내용은 그대로 사용하고, <section> 태그만 새로 정의하면 됩니다.

d-title에 'user 조회' 라고 설정하고, d-content에 회원리스트 5개를 테이블 안에 담아서 보여줄 건데요.
나머지 부분은 생략하고, d-content 내용을 살펴보도록 할거에요.

<div class="d-content">
	<div class="row">
		<div class="table-responsive">
			<table class="table table-striped">
				<thead>
					<tr class="text-center">
						<th width="10%">아이디</th>
						<th width="6%">유형</th>
						<th width="10%">이름</th>
						<th width="15%">전화번호</th>
						<th width="10%">생일</th>
						<th width="7%">성별</th>
						<th width="25%">가입일시</th>
						<th width="7%">탈퇴여부</th>
					</tr>
				</thead>
				<tbody>
					<tr class="text-center" th:object="${user}" th:each="user: ${users}" th:attr="data-href=@{/v/users/__${user.id}__}">
						<td th:text="*{ id }"></td>
						<td th:text="*{ type eq '0' ? '일반' : eq '1' ? '사업자' : '미설정' }"></td>
						<td th:text="*{ name }"></td>
						<td th:text="*{ phoneNumber }"></td>
						<td th:text="*{ birthDate }"></td>
						<td th:text="*{ sex eq '1' ? '남자' : sex eq '2' ? '여자' : '미설정' }"></td>
						<td th:text="*{ createTimestamp }"></td>
						<td th:text="*{ del ? 'Y' : 'N' }"></td>
					</tr>
				</tbody>
			</table>
		</div>
	</div>
</div>

ln 18: users 객체에는 user 엔티티 5개가 들어있습니다. 그 리스트를 th:each로 tr 태그를 하나씩 생성하는데요. 이 공통적인 user 객체를 th:object로 정의하면 하위 <td> 태그에서는 ${user.id} 에서 user. prefix를 생략할 수 있어서 가독성을 높일 수 있습니다.

ln 20: type이 '0'일 경우 '일반', '1' 일 경우 '사업자', 그 외의 경우에는 '미설정' 으로 표기하도록 했습니다. null값이 들어있을 경우에도 '미설정'으로 출력됩니다.

44


상세한 코드를 확인해보고 싶다면, 아래의 GitHub 주소에서 소스코드를 다운받아서 확인해보세요.
참고로 이번 과정에서는 레이아웃 설정하는 방법을 다루지 않았기 때문에
home 과 user 페이지에서 공통적으로 사용하고 있는 <header>, <nav>, <footer> 태그 내의 내용을 중복해서 작성했습니다.
다음 과정에서는 이런 중복되는 정보를 레이아웃으로 설정하는 방법을 알아볼 것입니다.

GitHub에서 demo 프로젝트를 다운받아 볼 수 있습니다.

728x90
반응형