타임리프 프레그먼트 (Fragment) 사용하기
- https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#including-template-fragments
- 프레그먼트 정의
- th:gragement
- 프레그먼트 사용
- th:insert
- th:replace
뷰 중복 코드
- 메인 네비게이션
- 하단 (footer)
- 헤더 (header)
/resources/templates에 fragment.html 생성
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head th:fragment="head">
<meta charset="UTF-8">
<title>StudyOlle</title>
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css"/>
<style>
.container {
max-width: 100%;
}
</style>
</head>
<nav th:fragment="main-nav" class="navbar navbar-expand-sm navbar-dark bg-dark">
<a class="navbar-brand" href="/" th:href="@{/}">
<img src="/images/logo_sm.png" width="30" height="30">
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<form th:action="@{/search/study}" class="form-inline" method="get">
<input class="form-control mr-sm-2" name="keyword" type="search" placeholder="스터디 찾기"
aria-label="Search"/>
</form>
</li>
</ul>
<ul class="navbar-nav justify-content-end">
<li class="nav-item" sec:authorize="!isAuthenticated()">
<a class="nav-link" th:href="@{/login}">로그인</a>
</li>
<li class="nav-item" sec:authorize="!isAuthenticated()">
<a class="nav-link" th:href="@{/sign-up}">가입</a>
</li>
<li class="nav-item" sec:authorize="isAuthenticated()">
<a class="nav-link" th:href="@{/notifications}">알림</a>
</li>
<li class="nav-item" sec:authorize="isAuthenticated()">
<a class="nav-link btn btn-outline-primary"
th:href="@{/notifications}">스터디 개설</a>
</li>
<li class="nav-item dropdown" sec:authorize="isAuthenticated()">
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
프로필
</a>
<div class="dropdown-menu dropdown-menu-sm-right" aria-labelledby="userDropdown">
<h6 class="dropdown-header">
<span sec:authentication="name">Username</span>
</h6>
<a class="dropdown-item" th:href="@{'/profile/' + ${#authentication.name}}">프로필</a>
<a class="dropdown-item">스터디</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" th:href="@{'/settings/profile'}">설정</a>
<form class="form-inline my-2 my-lg-0" th:action="@{/logout}" method="post">
<button class="dropdown-item" type="submit">로그아웃</button>
</form>
</div>
</li>
</ul>
</div>
</nav>
<footer th:fragment="footer">
<div class="row justify-content-center">
<img class="mb-2" src="/images/logo_long_kr.jpg" alt="" width="100">
<small class="d-block mb-3 text-muted">ⓒ2020</small>
</div>
</footer>
</html>
모든 뷰 템플릿에 fragments 적용
index.html
<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<!--head-->
<head th:replace="fragments.html::head"></head> // 적용
<body class="bg-light">
<!--네비게이션 바-->
<div th:replace="fragments.html::main-nav"></div> // 적용
<div class="container">
<div class="py-5 text-center">
<h2>스터디올래</h2>
</div>
<div class="row justify-content-center">
</div>
<!-- footer -->
<div th:replace="fragments.html::footer"></div> // 적용
</div>
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script type="application/javascript" th:fragment="form-validation">
(function () {
}())
</script>
</body>
</html>
sign-up.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments.html::head"></head> // 적용
<body class="bg-light">
<!--네비게이션 바-->
<div th:replace="fragments.html::main-nav"></div> // 적용
<div class="container">
<div class="py-5 text-center">
<h2>계정 만들기</h2>
</div>
<div class="row justify-content-center">
<form class="needs-validation col-sm-6" action="#"
th:action="@{/sign-up}" th:object="${signUpForm}" method="post" novalidate>
<div class="form-group">
<label for="nickname">닉네임</label>
<input id="nickname" type="text" th:field="*{nickname}" class="form-control"
placeholder="whiteship" aria-describedby="nicknameHelp" required minlength="3" maxlength="20">
<small id="nicknameHelp" class="form-text text-muted">
공백없이 문자와 숫자로만 3자 이상 20자 이내로 입력하세요. 가입후에 변경할 수 있습니다.
</small>
<small class="invalid-feedback">닉네임을 입력하세요.</small>
<small class="form-text text-danger" th:if="${#fields.hasErrors('nickname')}" th:errors="*{nickname}">Nickname
Error</small>
</div>
<div class="form-group">
<label for="email">이메일</label>
<input id="email" type="email" th:field="*{email}" class="form-control"
placeholder="your@email.com" aria-describedby="emailHelp" required>
<small id="emailHelp" class="form-text text-muted">
스터디올래는 사용자의 이메일을 공개하지 않습니다.
</small>
<small class="invalid-feedback">이메일을 입력하세요.</small>
<small class="form-text text-danger" th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Email
Error</small>
</div>
<div class="form-group">
<label for="password">패스워드</label>
<input id="password" type="password" th:field="*{password}" class="form-control"
aria-describedby="passwordHelp" required minlength="8" maxlength="50">
<small id="passwordHelp" class="form-text text-muted">
8자 이상 50자 이내로 입력하세요. 영문자, 숫자, 특수기호를 사용할 수 있으며 공백은 사용할 수 없습니다.
</small>
<small class="invalid-feedback">패스워드를 입력하세요.</small>
<small class="form-text text-danger" th:if="${#fields.hasErrors('password')}" th:errors="*{password}">Password
Error</small>
</div>
<div class="form-group">
<button class="btn btn-primary btn-block" type="submit"
aria-describedby="submitHelp">가입하기
</button>
<small id="submitHelp" class="form-text text-muted">
<a href="#">약관</a>에 동의하시면 가입하기 버튼을 클릭하세요.
</small>
</div>
</form>
</div>
<!-- footer -->
<div th:replace="fragments.html::footer"></div> // 적용
</div>
<script src="node_modules/jquery/dist/jquery.min.js"></script>
<script src="node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script type="application/javascript" th:fragment="form-validation">
(function () {
'use strict';
window.addEventListener('load', function () {
// Fetch all the forms we want to apply custom Bootstrap validation styles to
const forms = document.getElementsByClassName('needs-validation');
// Loop over them and prevent submission
Array.prototype.filter.call(forms, function (form) {
form.addEventListener('submit', function (event) {
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated')
}, false)
})
}, false)
}())
</script>
</body>
</html>
checked-email.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments.html::head"></head> // 적용
<body class="bg-light">
<!--네비게이션 바-->
<div th:replace="fragments.html::main-nav"></div> // 적용
<div class="py-5 text-center" th:if="${error}">
<p class="lead">스터디올래 이메일 확인</p>
<div class="alert alert-danger" role="alert">
이메일 확인 링크가 정확하지 않습니다.
</div>
</div>
<div class="py-5 text-center" th:if="${error == null}">
<p class="lead">스터디올래 이메일 확인</p>
<h2>
이메일을 확인했습니다. <span th:text="${numberOfUser}">10</span>번째 회원,
<span th:text="${nickname}">김주혁</span>님 가입을 축하합니다.
</h2>
<small class="text-info">이제부터 가입할 때 사용한 이메일 또는 닉네임과 패스워드로 로그인 할 수 있습니다.</small>
</div>
<!-- footer -->
<div th:replace="fragments.html::footer"></div> // 적용
</body>
</html>
참고: https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-JPA-%EC%9B%B9%EC%95%B1#
'스프링과 JPA 기반 웹 어플리케이션 개발 > 1부 (개발환경, 회원가입, 로그인, 계정설정)' 카테고리의 다른 글
15. 현재 인증된 사용자 정보 참조 (0) | 2020.04.20 |
---|---|
14. 첫 페이지 보완 (0) | 2020.04.20 |
12. 프론트엔드 라이브러리 설정 (0) | 2020.04.19 |
11. 회원 가입: 메인 네비게이션 메뉴 변경 (0) | 2020.04.19 |
10. 회원 가입: 가입 완료 후 자동 로그인 (0) | 2020.04.18 |