스프링부트(Spring Boot) MyBatis 게시판 만들기5 - Thymeleaf [Mysql, DBeaver, IntelliJ, Maven]

예림·2024년 6월 17일
2
post-thumbnail

지난 시간에 정리했던 jsp와 Thymeleaf 동시에 사용하기를 토대로 기존 jsp게시판의 화면을 thymeleaf로 동일하게 보여줄 수 있도록 수정해보겠다.

우선 기존 board/main.jsp안의 내용을 그대로 복사해서 resources/templates/thymeleaf 안에 board.html 파일 생성 후 붙여넣기를 해준다.

그 다음 타임리프를 사용하기 위해 다음 작업을 해준다.

  • 아래의 jstl 코드 삭제
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
  • thymeleaf 코드 추가
<html lang="ko" xmlns:th="http://www.thymeleaf.org">

컨트롤러에서 리턴될 페이지 위치를 변경해준다.

  • BoardConroller.java

    @RequestMapping("/main")
        public String main(Board board, Model model, @RequestParam(defaultValue = "1") int page){
    
            // 게시글 총 개수
            int total = boardService.cntBoard();
            model.addAttribute("cntBoard", total);
            // 페이징
            PaginateDto paginate = new PaginateDto(5, 3);
            paginate.setPageNo(page);
            paginate.setTotalSize(total);
    
            board.setPageNo(page);
            board.setPageSize(paginate.getPageSize());
            board.setPageOffset(paginate.getPageOffset());
    
            model.addAttribute("paginate", paginate);
            model.addAttribute("board", boardService.getBoardlist(board));
            //return "board/main";
            return "thymeleaf/main";
        }

수정된 main.html 코드 전체이다.

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>게시판 메인 - 타임리프</title>
    <style>
        .button-container {
            margin-top: 20px;
        }
        .button-container input {
            margin-right: 10px;
        }
        table {
            width: 100%;
            border-collapse: collapse;
        }
        th, td {
            border: 1px solid #ddd;
            padding: 8px;
        }
        th {
            background-color: #f2f2f2;
        }
    </style>
</head>
<body>
<h1>게시글 목록</h1>
게시판 총 <span th:text="${cntBoard}">0</span><table>
    <thead>
    <tr>
        <th class="one wide">번호</th>
        <th class="ten wide">글제목</th>
        <th class="two wide">작성자</th>
        <th class="three wide">작성일</th>
        <th class="four wide">조회수</th>
    </tr>
    </thead>

    <tbody>
    <tr th:each="board : ${board}">
        <td><span th:text="${board.bno}">1</span></td>
        <td><a th:href="@{/board/{bno}(bno=${board.bno})}"><span th:text="${board.title}">제목</span></a></td>
        <td><span th:text="${board.writer}">작성자</span></td>
        <td><span th:text="${board.regdate}">작성일</span></td>
        <td><span th:text="${board.hit}">조회수</span></td>
    </tr>
    </tbody>
</table>

<!-- 로그인 여부에 따라 버튼 표시 -->
<div class="button-container">
    <div th:if="${#httpServletRequest.remoteUser != null}">
        <!-- 사용자가 로그인한 경우 -->
        <input type="button" value="user 목록" onclick="location.href='/user/main'"><br/><br/>
        <input type="button" value="글 작성" onclick="location.href='/write'"><br/><br/>
        <input type="button" value="로그아웃" onclick="location.href='/logout'"><br/><br/>
    </div>
    <div th:if="${#httpServletRequest.remoteUser == null}">
        <!-- 사용자가 로그인하지 않은 경우 -->
        <input type="button" value="로그인" onclick="location.href='/login'"><br/><br/>
        <input type="button" value="회원가입" onclick="location.href='/join'"><br/><br/>
    </div>
</div>

<!-- 페이징 처리 시작 -->
<div class="col-sm-12 col-md-7" style="margin: auto">
    <div class="dataTables_paginate paging_simple_numbers" id="dataTable_paginate">
        <div class="paging">
            <div th:if="${paginate != null}">
                <!-- 이전 페이지 링크 -->
                <div th:if="${paginate.nationBegin > 1}">
                    <a th:href="@{${paginate.params}(${paginate.pageName}=${paginate.nationBegin - 1})}" page="${paginate.nationBegin - 1}" class="prev">이전</a>
                </div>
                <div th:if="${paginate.nationBegin <= 1}">
                    <a href="#" onclick="return false" class="prev">이전</a>
                </div>

                <!-- 페이지 번호 링크 -->
                <span th:each="pageNum : ${#numbers.sequence(paginate.nationBegin, paginate.nationClose)}">
                    <span th:if="${pageNum == paginate.pageNo}">
                        <strong class="current" th:text="${pageNum}">1</strong>
                    </span>
                    <span th:if="${pageNum != paginate.pageNo}">
                        <a th:href="@{${paginate.params}(${paginate.pageName}=${pageNum})}" th:text="${pageNum}">1</a>
                    </span>
                </span>

                <!-- 다음 페이지 링크 -->
                <div th:if="${paginate.nationClose < paginate.totalPage}">
                    <a th:href="@{${paginate.params}(${paginate.pageName}=${paginate.nationClose + 1})}" page="${paginate.nationClose + 1}" class="next">다음</a>
                </div>
                <div th:if="${paginate.nationClose >= paginate.totalPage}">
                    <a href="#" onclick="return false" class="next">다음</a>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- 페이징 처리 끝 -->
</body>
</html>

이제 컨트롤러에서 글 상세, 작성 메서드의 리턴 페이지 위치 또한 변경해준다.

  • BoardController.java

    @GetMapping("/board/{bno}")
        public String boardDetail(@PathVariable Integer bno, Model model, Board board){
            Board boardDetail = boardService.getBoard(bno);
            List<FileDto> file = boardService.getFile(board);
            board.setHit(boardService.hit(bno));
    
            model.addAttribute("board", boardDetail);
            model.addAttribute("getFile", file);
    
            return "thymeleaf/write";
        }
    
        @RequestMapping("/write")
        public String write(Model model, Board board){
    
            // 모델에 사용자 정보 추가
            aop.addUserToModel(model);
    
            if(board.getBno()==null){
                model.addAttribute("getBoard", board);
                model.addAttribute("getFile", boardService.getFile(board));
            }
            return "thymeleaf/write";
        }

board/write.jsp안의 코드를 write.html로 복사 붙여넣기 한 후 thymeleaf를 적용시켜준다.
완성코드는 다음과 같다.

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>글 상세</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<h1>글 상세보기</h1>

<form id="boardForm" method="post" action="/insertBoard">
    <table width="90%">
        <tr width="90%">
            <td width="10%" align="center">작성자</td>
            <td width="50%" th:if="${board.writer}" th:text="${board.writer}">작성자</td>
            <!-- <td width="50%">${user.username}</td> -->
            <td width="50%" th:text="${user}">사용자</td>
        </tr>
        <tr>
            <td align="center">제목</td>
            <td><input type="text" name="title" style="width: 95%;" th:value="${board.title}"></td>
        </tr>
        <tr>
            <td align="center">내용</td>
            <td><textarea name="content" style="width: 95%;height: 200px;" th:text="${board.content}"></textarea></td>
        </tr>
        <tr>
            <td align="center">첨부파일 등록</td>
            <td>
                <input type="file" name="files" id="files" multiple="multiple" onchange="uploadFile(this)">
                <div id="uploadDiv">
                    <div th:each="file, status : ${getFile}" class="uploadResult">
                        <span th:text="${status.index + 1} + '. '">1. </span>
                        <a th:href="@{/downloadFile(uuid=${file.uuid}, fileName=${file.filename})}" th:text="${file.filename}" download>파일명</a>
                        <input type="hidden" name="uuids" th:value="${file.uuid}">
                        <input type="hidden" name="filenames" th:value="${file.filename}">
                        <input type="hidden" name="contentTypes" th:value="${file.contentType}">
                        <label class="delBtn">◀ 삭제</label>
                    </div>
                </div>
            </td>
            <br><br><br>
        </tr>
        <tr>
            <td colspan="2" align="center">
                <input type="hidden" name="bno" th:value="${board.bno}">
                <button type="submit">등록</button>
                <button type="button" onclick="history.back()">뒤로가기</button>
                <button type="button" th:if="${board.bno}" onclick="deleteBoard()">삭제</button>
            </td>
        </tr>
    </table>
</form>

<script>
    // delBtn 개별 삭제 버튼
    $(function () {
        delBtnFile();
    });

    function delBtnFile() {
        $(".delBtn").on("click", function () {
            $(this).parent().remove();
            stCnt();
        });
    }

    // status.count ajax사용시 다시 그려주기
    function stCnt() {
        $('.uploadResult').each(function(index, item){
            $(this).children('span').html(index + 1 + '. ');
        });
    }

    // ajax 첨부파일 업로드
    function uploadFile(obj) {
        let files = obj.files;
        let formData = new FormData();

        for (let i = 0; i < files.length; i++){
            formData.append("files", files[i]);
        }

        $.ajax({
            type: 'post',
            enctype: 'multipart/form-data',
            url: '/ajaxFile',
            data: formData,
            processData: false,
            contentType: false,
            success: function(data) {
                console.log(data);
                let result = "";
                let cnt = $('.uploadResult').length;
                for (let i = 0; i < data.length; i++){
                    result += '<div class="uploadResult">';
                    result += '<span>' + (cnt + i + 1)  +  '. </span><a href="/downloadFile?uuid=' + data[i].uuid + '&filename=' + data[i].filename + '" download>' + data[i].filename + '</a>';
                    result += '<input type="hidden" name="uuids" value="' + data[i].uuid + '">';
                    result += '<input type="hidden" name="filenames" value="' + data[i].filename + '">';
                    result += '<input type="hidden" name="contentTypes" value="' + data[i].contentType + '">';
                    result += '<label type="button" class="delBtn"> ◀ 삭제</label>';
                    result += '</div>';
                }
                $('#uploadDiv').append(result);
                delBtnFile();
            }
        });
    }

    // 글 삭제
    function deleteBoard() {
        const bno = document.getElementsByName("bno")[0].value;
        if (confirm("정말 삭제하시겠습니까?")) {
            fetch(`/delete/${bno}`, {
                method: 'DELETE'
            })
                .then(response => {
                    if (response.ok) {
                        alert("삭제되었습니다.");
                        location.href = "/main";
                    } else {
                        alert("삭제에 실패하였습니다.");
                    }
                })
                .catch(error => {
                    console.error("Error:", error);
                    alert("삭제 중 오류가 발생하였습니다.");
                });
        }
    }
</script>
</body>
</html>

이렇게까지 하면 기존 jsp에서 thymeleaf가 적용된 html파일로 변경된다.
기능 수행 및 화면은 기존과 동일하다.

profile
백엔드 개발하는 사람

1개의 댓글

comment-user-thumbnail
2024년 6월 19일

잘보고갑니다!^^

답글 달기