📍 서론
JSP는 예전 키보드 쇼핑몰 프로젝트에서 썻던 템플릿 엔진이고,
mustache는 null값 처리와 프론트 단에서 불편한 점이 조금 있어서,
타임리프를 사용해보았다.
📍 Thymeleaf (타임리프) 사용하기
Gradle - build.gradle
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
Maven - pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
위 설정을 추가 후 빌드하면 application.properties에 아래 코드가 자동으로 추가된다.
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
📍 Thymeleaf 문법
대부분의 html 속성을 th:xxx 로 변경할 수 있다.
ex: th:text="${변수명}"
표현 | 설명 | 예제 |
@{ ... } | URL 링크 표현식 | th:href="@{/css/bootstrap.min.css}" th:href="@{/{itemId}/edit(itemId=${item.id})}" |
| ... | | 리터럴 대체 | th:text="|Hi ${user.name}!|" (= th:text="'Hi '+${user.name}+'!'" |
${ ... } | 변수 | th:text=${user.name} |
th:each | 반복 출력 | <tr th:each="item: ${items}"> <td th:text="${item.price}">100</td> </tr> |
*{ ... } | 선택 변수 | <tr th:object="${items}"> <td th:text="*{price}">100</td> </tr> |
#{ ... } | 메시지. properties 같은 외부 자원에서 코드에 해당하는 문자열 get. | th:text="#{member.register}" |
📍 Thymeleaf Layout
Header, Footer와 같이 공통적으로 반복되는 코드를 화면마다 작성하지 않고 레이아웃 처리를 통해 일괄 적용한다
Gradle - build.gradle
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
fragments/header.html - 상단
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<th:block th:fragment="headerFragment">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- 제이쿼리 -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- CSS -->
<link rel="stylesheet" href="../css/index.css">
<link rel="stylesheet" href="../css/lostark.css">
<!-- Boxicons CSS -->
<link href='https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css' rel='stylesheet'>
<title>Minhyeok Personal Web</title>
</head>
</th:block>
</html>
fragments/footer.html - 하단
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<th:block th:fragment="footerFragment">
<footer>
</footer>
</th:block>
</html>
layouts/default_layout.html - 기본 레이아웃 (메뉴바포함)
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<th:block th:replace="fragments/header :: headerFragment"></th:block>
<body>
<nav class="sidebar">
<header>
<div class="image-text">
<span class="image">
<img src="../resources/logo.png" alt="logo">
</span>
<div class="text header-text">
<span class="name">Minhyeok</span>
<span class="profession">Personal Web</span>
</div>
</div>
<i class="bx bx-chevron-right toggle"></i>
</header>
<div class="menu-bar">
<div class="menu">
<li class="search-box">
<i class="bx bx-search icon"></i>
<input id="igdb_id" type="text" placeholder="IGDB ID Search..." onkeyup="enterKey();"/>
</li>
<ul class="menu-links">
<li class="nav-link">
<a href="/lostark" class="nav-link-item">
<img class="img-icon" src="../resources/lostark_icon.jpeg" alt="lostark_icon">
<span class="text nav-text">로스트아크</span>
</a>
</li>
<div class="nav-dropdown">
<li>
<a href="/lostark/character" class="nav-link-item">
<i class='bx bx-right-arrow icon'></i>
<span class="text nav-text">내 캐릭터</span>
</a>
</li>
</div>
<li class="nav-link">
<a href="/restaurant">
<i class='bx bx-food-menu icon'></i>
<span class="text nav-text">점심식사</span>
</a>
</li>
<div class="nav-dropdown">
<li>
<a href="/restaurant/seohyunData" class="nav-link-item">
<i class='bx bx-data icon'></i>
<span class="text nav-text">서현 데이터</span>
</a>
</li>
</div>
<li class="nav-link">
<a href="/IGDB">
<i class="bx bx-bar-chart-alt-2 icon"></i>
<span class="text nav-text">IGDB</span>
</a>
</li>
<li class="nav-link">
<a href="">
<i class="bx bx-bell icon"></i>
<span class="text nav-text">Notifications</span>
</a>
</li>
<li class="nav-link">
<a href="">
<i class="bx bx-pie-chart-alt icon"></i>
<span class="text nav-text">Analytics</span>
</a>
</li>
<li class="nav-link">
<a href="">
<i class="bx bx-heart icon"></i>
<span class="text nav-text">Likes</span>
</a>
</li>
<li class="nav-link">
<a href="">
<i class='bx bx-data icon'></i>
<span class="text nav-text">Datas</span>
</a>
</li>
</ul>
</div>
<div class="bottom-content">
<!--
<li class="">
<a href="">
<i class="bx bx-log-out icon"></i>
<span class="text nav-text">Logout</span>
</a>
</li>
-->
<li class="mode">
<div class="moon-sun">
<i class="bx bx-moon icon moon"></i>
<i class="bx bx-sun icon sun"></i>
</div>
<span class="mode-text text">Dark Mode</span>
<div class="toggle-switch">
<span class="switch"></span>
</div>
</li>
</div>
</div>
</nav>
<section class="home">
<th:block layout:fragment="content"></th:block>
</section>
</body>
<th:block th:replace="fragments/footer :: footerFragment"></th:block>
<script src="../js/index.js"></script>
<script src="../js/lostark.js"></script>
</html>
본문 적용
lostark/lostarkCharacter.html - 로스트아크 오픈 Api를 이용하여 대표캐릭터를 가져와서 출력하는 영역
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="layouts/default_layout">
<th:block layout:fragment="content">
<div class="text">로스트아크 > 내 캐릭터(${mainServer} - ${mainCharacter})</div>
<div class="lostark wrap">
<div class="lostark-character">
<div th:each="character : ${characters}" class="lostark-character-wrap">
<div class="lostark-character-image">
<img th:src="${character.characterImage}">
</div>
<div class="lostark-character-text">
<div class="lostark-character-textbox" style="background: #695CFE;">
<p th:text="${character.characterName}"></p>
<p>캐릭터 클래스 : <span th:text="${character.characterClassName}"></span></p>
<p>아이템 레벨 : <span th:text="${character.itemMaxLevel}"></span></p>
</div>
<div class="lostark-character-textbox" style="background: #FA8072;">
<p>카오스 던전 : <span th:text="${character.chaosName}"></span></p>
<p>가디언 토벌 : <span th:text="${character.guardianName}"></span></p>
<p>카오스 던전 휴게 : 개발중</p>
<p>가디언 토벌 휴게 : 개발중</p>
</div>
</div>
</div>
</div>
</div>
</th:block>
</html>
기존 jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@include file="../common/header.jsp" %>
<div class="text">로스트아크 > 내 캐릭터(${mainServer} - ${mainCharacter})</div>
<div class="lostark wrap">
<div class="lostark-character">
<c:forEach var="character" items="${characters}">
<div class="lostark-character-wrap">
<div class="lostark-character-image">
<img src="${character.characterImage}">
</div>
<div class="lostark-character-text">
<div class="lostark-character-textbox" style="background: #695CFE;">
<p>${character.characterName}</p>
<p>캐릭터 클래스 : ${character.characterClassName}</p>
<p>아이템 레벨 : ${character.itemMaxLevel}</p>
</div>
<div class="lostark-character-textbox" style="background: #FA8072;">
<p>카오스 던전 : ${character.chaosName}</p>
<p>가디언 토벌 : ${character.guardianName}</p>
<p>카오스 던전 휴게 : 개발중</p>
<p>가디언 토벌 휴게 : 개발중</p>
</div>
</div>
</div>
</c:forEach>
</div>
</div>
<%@include file="../common/footer.jsp" %>
결과는 똑같이 출력된다
이제... 다 바꾸면된다...
참고