반응형
- HTML, Thymeleaf, BootStrap, JavaScript, JQuery를 사용하여 화면 제작
- 디자인이나 화면에 대해 자세히는 정리하지는 않고 핵심적인 부분만 설명
- 화면 관련 코드에 전체 코드가 있으니 참고
- Thyemleaf와 관련된 것은 [Spring Boot] Thymeleaf 기능 정리, [Spring Boot] Thymeleaf - form 관련 기능 정리 참고
printMessage.html
- 이 페이지는 내용을 출력하기 위한 페이지가 아닌 javascript를 통해 메세지를 출력해주고, 바로 다음 페이지로 이동시켜주는 페이지
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>게시판</title>
</head>
<body>
<script th:inline="javascript">
window.onload = function () {
if([[${message}]] != null) {
alert([[${message}]])
}
window.location.href = [[${nextUrl}]]
}
</script>
</body>
</html>
header.html
- 화면 상단의 nav-bar와 Bootstrap, css 등을 import 해주는 페이지
- 모든 페이지에 같은 nav-bar가 들어가기 때문에 한번 만들어 놓고 thymeleaf의 fragment를 통해 다른 페이지에서는 <div th:replace="fragments/header.html :: header ('admin')"/> 이런 식으로 불러서 사용하기만 하면 됨
- thymeleaf-extras-springsecurity5 라이브러리를 설치하고, html 태그에 xmlns:sec="http://www.thymeleaf.org/extras/spring-security"를 추가해주면 sec:authorize="isAuthenticated()", sec:authorize="hasAuthority('ADMIN')" 와 같이 인증, 인가를 쉽게 할 수 있음
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head th:fragment="head">
<meta charset="utf-8">
<title>게시판</title>
<!-- Bootstrap 5.2.3 Version -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
<!-- My CSS -->
<link rel="stylesheet" href="/css/custom.css">
<!-- JQuery -->
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<div th:fragment="header (pageName)">
<!-- Bootstrap 5.2.3 Version -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
<nav class="navbar navbar-expand-lg" style="background-color: limegreen; margin-bottom: 60px;">
<div class="container-fluid">
<a class="navbar-brand" href="/">Basic Board</a>
<div class="collapse navbar-collapse" id="navbarNavDropdown">
<!--<ul class="navbar-nav">-->
<ul class="navbar-nav">
<li class="nav-item">
<a th:if="${pageName == 'home'}" class="nav-link active" aria-current="page" href="/">Home</a>
<a th:unless="${pageName == 'home'}" class="nav-link" aria-current="page" href="/">Home</a>
</li>
<li class="nav-item">
<a th:if="${pageName == 'greeting'}" class="nav-link active" aria-current="page" href="/boards/greeting">가입인사</a>
<a th:unless="${pageName == 'greeting'}" class="nav-link" aria-current="page" href="/boards/greeting">가입인사</a>
</li>
<li class="nav-item">
<a th:if="${pageName == 'free'}" class="nav-link active" aria-current="page" href="/boards/free">자유게시판</a>
<a th:unless="${pageName == 'free'}" class="nav-link" aria-current="page" href="/boards/free">자유게시판</a>
</li>
<li class="nav-item">
<a th:if="${pageName == 'gold'}" class="nav-link active" aria-current="page" href="/boards/gold">골드게시판</a>
<a th:unless="${pageName == 'gold'}" class="nav-link" aria-current="page" href="/boards/gold">골드게시판</a>
</li>
</ul>
</div>
<div class="justify-content-between" id="navbarNav" style="padding: 5px 30px">
<!-- 로그인 안 했을때 -->
<ul class="navbar-nav" sec:authorize="isAnonymous()">
<li class="nav-item">
<button class="btn nav-btn" onclick="location.href = '/users/login'">로그인</button>
</li>
<li class="nav-item">
<button class="btn nav-btn" onclick="location.href = '/users/join'">회원가입</button>
</li>
</ul>
<!-- 로그인 했을때 -->
<ul class="navbar-nav" sec:authorize="isAuthenticated()">
<li class="nav-item" sec:authorize="hasAuthority('ADMIN')">
<button class="btn nav-btn" onclick="location.href = '/users/admin'" style="width: fit-content">관리자 페이지</button>
</li>
<li class="nav-item">
<button class="btn nav-btn" onclick="location.href = '/users/myPage/board'">마이페이지</button>
</li>
<li class="nav-item">
<button class="btn nav-btn" onclick="location.href = '/users/logout'">로그아웃</button>
</li>
</ul>
</div>
</div>
</nav>
</div>
</body>
확인 메세지 출력
function clickDelete() {
if (confirm("해당 글을 삭제 하시겠습니까?") == true) {
location.href = "/boards/" + [[${category}]] + "/" + [[${boardDto.id}]] + "/delete";
}
}
- boards/detail.html에서 위와 같은 코드가 있음
- 이 코드는 버튼을 눌렀을 때, 바로 GET 요청을 보내지 않고 확인 메세지를 출력해 줌
- 확인 메세지에서 확인 버튼을 눌러야 if 문 안의 내용이 실행됨
페이지 기능 구현
window.onload = function () {
let nowPage = [[${boards.getNumber()}]] + 1; // 현재 페이지
let totalPage = [[${boards.getTotalPages()}]]; // 전체 페이지 수
let firstPage; // 화면에 출력될 첫 페이지
for (let i = nowPage ; i >= 1 ; i --) {
if(i % 5 == 1) {
firstPage = i;
break;
}
}
let lastPage; // 화면에 출력될 마지막 페이지
let nextButton; // 다음 버튼 출력 여부
if (firstPage + 4 >= totalPage) {
lastPage = totalPage;
nextButton = false;
} else {
lastPage = firstPage + 4;
nextButton = true;
}
// HTML 생성
let pageHtml = "";
pageHtml += "<li><a class='page-link' href='" + makeUrl(1) + "'>«</a></li>";
if (firstPage != 1) {
pageHtml += "<li><a class='page-link' href='" + makeUrl(firstPage - 1) + "'>‹</a></li>";
}
for (let i = firstPage; i <= lastPage; i++) {
if (i == nowPage) {
pageHtml += "<li class='page-item active'><a class= 'page-link'>" + i + "</a></li>";
} else {
pageHtml += "<li class='page-item'><a class= 'page-link' href='" + makeUrl(i) + "'>" + i + "</a></li>";
}
}
if (nextButton) {
pageHtml += "<li><a class='page-link' href='" + makeUrl(lastPage + 1) + "'>›</a></li>";
}
pageHtml += "<li><a class='page-link' href='" + makeUrl(totalPage) + "'>»</a></li>";
$("#paging-ul").html(pageHtml);
}
function makeUrl(page) {
let category = [[${category}]];
let url = "/boards/" + category + "?page=" + page;
// 검색 했으면 다음 URL에도 추가
let sortType = [[${boardSearchRequest.sortType}]];
let searchType = [[${boardSearchRequest.searchType}]];
let keyword = [[${boardSearchRequest.keyword}]];
if (sortType != null) {
url += "&sortType=" + sortType;
}
if (searchType != null) {
url += "&searchType=" + searchType + "&keyword=" + keyword;
}
return url;
}
- boards/list.html에서 위와 같은 코드가 있음
- 이 코드는 해당 html 파일이 open 될 때 페이지 버튼을 만드는 코드
- 이 사이트에서는 1 ~ 5, ^ ~ 10과 같이 5개의 페이지 단위로 버튼을 생성함
- Model로 넘어온 값들을 Thymeleaf 문법을 사용해 받고, 현재 페이지와 전체 페이지를 통해 첫번째 페이지, 마지막 페이지를 계산하고, 버튼을 생성함
- 또한, 정렬, 검색을 했다면 sortType, searchType, keyword가 넘어오게 되는데, 이 값들을 url에 추가해줌으로써, 검색, 정렬 후 페이지 이동을 했을 때, 검색, 정렬 결과가 유지될 수 있게 해줌
파일 입력
<form class="offset-3 col-6 form-group" th:object="${boardCreateRequest}" th:method="post"
th:action="|@{/boards/{category} (category = ${category})}|" enctype="multipart/form-data">
<div>
<label th:for="title">제목 : </label>
<input type="text" th:field="*{title}" style="width: 50%">
</div>
<br/>
<div>
<label th:for="body">내용 : </label>
<textarea rows="10" style="width: 100%;" th:field="*{body}"/>
</div>
<br/>
<div>
<label th:for="uploadImage">이미지 첨부 : </label>
<input type="file" th:field="*{uploadImage}" style="width: 50%">
</div>
<br/>
<div align="center">
<button class="btn post-btn" type="submit">등록</button>
</div>
</form>
- 위 코드는 제목, 내용, 이미지를 입력받는 코드
- form을 통해 text를 입력 받는것 외에도 파일을 입력 받아야 하기 때문에 form 태그에 enctype="multipart/form-data"를 추가해주고, input type file로 파일을 입력받을 수 있음
- 이전에 작성한 글 ([Spring Boot] 게시판 만들기 4 - 게시판 기능의 BoardController와 [Spring Boot] 게시판 만들기 5 - 댓글, 좋아요, 파일 업로드 기능의 UploadService에서 파일 입력을 받으니 참고)
반응형
'Spring Boot > 프로젝트' 카테고리의 다른 글
[Spring Boot] 게시판 만들기 5 - 댓글, 좋아요, 파일 업로드 관련 기능 (0) | 2023.04.17 |
---|---|
[Spring Boot] 게시판 만들기 4 - 게시판 기능 (0) | 2023.04.17 |
[Spring Boot] 게시판 만들기 3 - 유저 기능 (0) | 2023.04.17 |
[Spring Boot] 게시판 만들기 2 - 라이브러리 설치, ERD, Entity 생성 (1) | 2023.04.17 |
[Spring Boot] 게시판 만들기 1 - 설계 & 결과 (2) | 2023.04.16 |