카테고리 없음

게시판 기능 구현 4 : 게시판 홈(게시글 목록 페이지) 만들기 - 스프링, jsp, 오라클, mybatis

blogOwner 2024. 3. 24. 20:44

드디어 6개의 데이터를 모두 담아서 boardHome.jsp 파일로 왔다. 

boardHome.jsp 파일에서는 가져온 6개의 데이터를 활용해서, 현재 페이지의 게시글 목록도 보여주고, 페이지네이션도 진행할 것이다. 

 

1단계 : 현재 페이지의 게시글 목록 보여주기. 

위 이미지 중 

이 부분을 구현해볼 것이다. 

grid 를 사용했는데, grid 에 대해서는 https://studiomeal.com/archives/533 여기 참고하면 된다. 아주 잘 정리되어 있는 좋은 글이라 별도로 이 포스팅에서는 다루지 않는다. 

<div class="board">
    <span>글번호</span>
    <span>제목</span>
    <span>작성자</span>
    <span>작성날짜</span>
    <span>조회수</span>
</div>

                    
<div class="board-2">
    <c:forEach var="post" items="${currentPagePosts}">
        <div class="post-row"> <!-- 각 게시물을 감싸는 div 추가 -->
            <span>${post.id}</span>
            <a href="#" class="post-link" data-postid="${post.id}">
                <c:if test="${post.isPrivate == 1}">
                    <i class="fa-solid fa-lock" style="color:red;"></i>
                </c:if>
                ${post.title}
            </a>
            <span>${post.userId}</span>
            <span>${post.writingDate}</span>
            <span>${post.viewNumber}</span>
        </div>
    </c:forEach>
</div>
/* css */
.board{
    display: grid;
    grid-template-columns: 1fr 4fr 2fr 1fr 1fr;
    align-items: center;
    justify-items: center;
    font-size: 12px;
}
.board-2{
    border-bottom: 1px solid lightgray;
}
.post-row{
    display: grid;
    grid-template-columns: 1fr 4fr 2fr 1fr 1fr;
    align-items: center;
    justify-items: center;
    border-bottom: 1px solid lightgray;
    font-size: 12px;
    min-height: 40px; /*각 행의 높이 조절*/
}

boardHome.jsp 전체 코드와 boardHome.css(boardHome.jsp 파일과 연결된 css 파일) 전체 코드는 맨 아래에 두었다.

위 코드는 현재 필요한 부분만 가져왔다. 

어쨋든, 현재 중요한 포인트는 뭐냐면,  우리가 담아줬던 6개의 데이터 중 1. 현재 페이지에 보여질 게시글(PostsDTO)을 담은 List자료구조. 를 <c:forEach> 문으로 반복을 돌리면서, 하나씩 꺼낸다는 것이다. 

중간에 있는 

                <c:if test="${post.isPrivate == 1}">
                    <i class="fa-solid fa-lock" style="color:red;"></i>
                </c:if>

이 코드는 font-awesome 에서 가져온 아이콘인데, 비밀글이라면 빨간 자물쇠를 보여주도록 하였다. 

 

 

2단계 : 페이지네이션 구현하기

아래와 같은 걸 페이지네이션이라고 한다. 

boardHome.jsp 중 아래 부분이 페이지네이션을 하는 부분이다. 

<div id="pagination">
    <!-- 페이지네이션 -->
    <!-- 이전 그룹 링크 -->
    <c:if test="${currentGroupFirstPage != 1}">
        <a href="/board-home?page=${currentGroupFirstPage - pageGroupSize}">&laquo; 이전</a>
    </c:if>

    <!-- 현재 페이지 그룹의 페이지 링크 forEach 문 돌림 -->
    <c:forEach var="i" begin="${currentGroupFirstPage}" end="${currentGroupLastPage}">
        <c:choose>
            <c:when test="${i == currentPage}">
                <span class="current-page" style="color:#e06500; ">${i}</span>
            </c:when>
            <c:otherwise>
                <a href="/board-home?page=${i}">${i}</a>
            </c:otherwise>
        </c:choose>
        &nbsp &nbsp 
    </c:forEach>

    <!-- 다음 그룹 링크 -->
    <c:if test="${currentGroupLastPage != totalPages}">
        <a href="/board-home?page=${currentGroupLastPage + 1}">다음 &raquo;</a>
    </c:if>
</div>

 

    <c:if test="${currentGroupFirstPage != 1}">
        <a href="/board-home?page=${currentGroupFirstPage - pageGroupSize}">&laquo; 이전</a>
    </c:if>

이 부분부터 보자. 

6개의 데이터 중 4. 현재 페이지 그룹의 첫번째 페이지 이거를 이용해서 만약, 현재 페이지 그룹의 첫번째 페이지가 1 이 아니라면, 이전 버튼을 누를 수 있게 해두었다. 참고로, a 태그의 href 속성은 지금 boardHome.jsp 이 jsp파일로 인도했던 그 컨트롤러로 get요청을 보내도록 설정되어 있다. 이때, 쿼리스트링으로는 6개의 데이터 중

4. 현재 페이지 그룹의 첫번째 페이지        에서                 6. 몇개의 페이지를 하나의 그룹으로 묶었는지.  를 뺏다.

예를 들어, 9개의 페이지를 하나의 그룹으로 묶었고, 현재 페이지가 속한 그룹의 첫번째 페이지가 10 페이지인 경우. 

10 - 9 = 1 이 되서, localhost:8080/board-home?page=1 로 get 요청을 하게 되는 것이다. 

---

    <c:if test="${currentGroupLastPage != totalPages}">
        <a href="/board-home?page=${currentGroupLastPage + 1}">다음 &raquo;</a>
    </c:if>

이건, 6개의 데이터 중

5. 현재 페이지 그룹의 마지막 페이지  가 3. 총 페이지 수 와 같지 않다면,

그러면, 뒤에 더 있다는 뜻이므로,  다음 버튼을 보여주도록 하였다.

다음 을 누르면 보내지는 get요청의 url 의 쿼리스트링으로는  5. 현재 페이지 그룹의 마지막 페이지 + 1 로 했다.

---

페이지네이션의 핵심이자 마지막은, 

    <c:forEach var="i" begin="${currentGroupFirstPage}" end="${currentGroupLastPage}">
            <c:choose>
                        <c:when test="${i == currentPage}">
                                     <span class="current-page" style="color:#e06500; ">${i}</span>
                         </c:when>
                         <c:otherwise>
                                     <a href="/board-home?page=${i}">${i}</a>
                         </c:otherwise>
           </c:choose>
           &nbsp &nbsp 
    </c:forEach>

 

<c:forEach> 문으로 반복을 돌리고 있는데, 단순히 i 부터 시작해서 1씩 증가하면서 반복을 하는 코드이다. 

그런데, 이렇게 단순한 코드로 어떻게 현재 그룹에 속한 페이지들을 표현하고, 현재 선택된 페이지는 또 어떻게 표현할 수 있냐면, 우리가 model 에 담아준 6개의 데이터를 잘 활용하면 된다.

var="i" 가 1씩 증가하면서 반복을 할 건데, 

i 의 시작지점이 

6개의 데이터 중 4. 현재 페이지 그룹의 첫번째 페이지 부터 시작하게 되고, +1씩 증가하다가 어디서 멈출거냐면, 5. 현재 페이지 그룹의 마지막 페이지 에서 멈출것이다. 

이때, <c:choose> <c:when> <c:otherwise> 를 사용해서 i 의 값이 현재 페이지(2. 현재 boardHome.jsp 파일에서 렌더링하고 있는 페이지가 "몇 페이지" 인지.)라면 단순히 span 태그가 나오도록 하고, 현재 페이지가 아니라면 a 태그로 하여 클릭하면 get요청이 가도록 하였다.

이렇게 해서 페이징까지 완료해보았다. 

다음 포스팅은 게시판 홈에 있는 게시글 중 하나를 클릭했을 때 게시글의 상세가 보여지는 것을 해볼 것이다. + 좋아요 버튼

 

더보기

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<c:import url="/jsp/bootstrapconfig/index.jsp"/>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %> <!-- 필요한 경우에 추가 -->
<%@ page import="firstportfolio.wordcharger.DTO.WritingDTOSelectVersion" %>

<html>
<head>
    <link rel="stylesheet" href="../../css/board/boardHome.css">
    <script src="https://kit.fontawesome.com/670206db20.js" crossorigin="anonymous"></script>

    <script>
    document.addEventListener('DOMContentLoaded', function(){
        document.querySelectorAll('.post-link').forEach(function(element){
            element.addEventListener('click', function(event){
                event.preventDefault(); // 기본 앵커 태그 동작 방지.

                var postId = this.getAttribute('data-postid');
                console.log('AAAAAAAAAAAAAAAAAAA');

                //우선, 글을 클릭한 사용자가 글을 쓴 사람과 같은 사람인지부터 확인해줘야함.
                //동일인이라면, 굳이 비밀번호를 안물어보기 위함이다.
                fetch('/is-this-you?postId=' + postId)
                    .then(response => {
                        if(!response.ok){
                            throw new Error('Network response was not ok');
                        }
                        return response.text();
                    })
                    .then(data => {
                        console.log(data);
                                        console.log('ABBBBBBBBBBBBBBB');

                        if(data == 'yes'){
                            window.location.href="/show-writing?postId="+postId;
                        }else{
                             //fetch 이용해서 통신. 비밀글이 있는지 여부를 확인해줄거임.
                            fetch('/is-this-secret?postId=' + postId)
                                .then(response => {
                                    if(!response.ok){
                                        throw new Error('Network response was not ok');
                                    }
                                    return response.json();
                                })
                                .then(data => {
                                                                        console.log('ccccccccccccccccccccc');
                                    console.log(data.isThisSecret);
                                    if(data.isThisSecret == 'no'){ // 비밀글이 아닌 경우
                                        window.location.href="/show-writing?postId="+postId;
                                    }else{ //비밀글인 경우
                                                                            console.log('dddddddddddddddddddddddd');

                                        let input = prompt('게시물의 비밀번호를 입력하세요');
                                        if(input == data.writingPassword){ //비밀번호가 일치한다면
                                            window.location.href="/show-writing?postId="+postId;
                                        } else{
                                            alert('비밀번호가 틀렸습니다.');
                                        }
                                    }
                                })
                                .catch(error => console.error('There has been a problem with your fetch operation:', error));
                        }
                    })
                    .catch(error => console.error('There has been a problem with your fetch operation:', error));



            });
        })
    });
    </script>


</head>
<body>

<!--네브 바 -->
    <c:import url="/jsp/common/loginedNavbar2.jsp" />
<!--네브 바 종료 -->

    <!-- 게시물을 수정할 수 없는 사람이 수정을 시도했을 경우에만 띄워질 alert 창 -->
        <% if (request.getAttribute("hecker") != null) { %>
        <script>
            // alert 창으로 메시지 띄우기
            alert("<%= request.getAttribute("hecker") %>");
        </script>
        <% } %>
    <!-- ---------------------------- -->

    <div id="board-container">
        <div id="page-title">
            <div></div>
            <div>게시판</div>
            <div></div>
        </div>
        <div id="page-body">
            <div></div>
                <div>
                    <div></div>
                    <div>
                        <div class="board">
                            <span>글번호</span>
                            <span>제목</span>
                            <span>작성자</span>
                            <span>작성날짜</span>
                            <span>조회수</span>
                        </div>
                    </div>



                    <div>
                        <div class="board-2">
                            <c:forEach var="post" items="${currentPagePosts}">
                                <div class="post-row"> <!-- 각 게시물을 감싸는 div 추가 -->
                                    <span>${post.id}</span>
                                    <a href="#" class="post-link" data-postid="${post.id}">
                                        <c:if test="${post.isPrivate == 1}">
                                            <i class="fa-solid fa-lock" style="color:red;"></i>
                                        </c:if>
                                        ${post.title}
                                    </a>
                                    <span>${post.userId}</span>
                                    <span>${post.writingDate}</span>
                                    <span>${post.viewNumber}</span>
                                </div>
                            </c:forEach>
                        </div>
                        <div id="post-make-div">
                            <a href="/writing-page" class="post-make" >&nbsp;글 작성&nbsp;</a>
                            <a href="/board-home" class="post-make" >&nbsp;최신순&nbsp;</a>
                            <a href="/board-home-order-by-like-num" class="post-make" >&nbsp;좋아요순&nbsp;</a>
                            <a href="/board-home-order-by-view-num" class="post-make" >&nbsp;조회수순&nbsp;</a>

                        </div>
                        <div id="find-posts">
                            <!--제목, 작성자, 내용 으로 게시글 찾기 -->
                            <form action="/find-posts-by-title-writer-content" method="post">
                                <select name="byWhatType">
                                    <option value="title">제목</option>
                                    <option value="writer">작성자</option>
                                    <option value="content">내용</option>
                                </select>

                                <input type="text" name="hintToFind" />
                                <button type="submit"> 찾기 </button>
                            </form>
                        </div>
                        <div id="pagination">
                            <!-- 페이지네이션 -->
                            <!-- 이전 그룹 링크 -->
                            <c:if test="${currentGroupFirstPage != 1}">
                                <a href="/board-home?page=${currentGroupFirstPage - pageGroupSize}">&laquo; 이전</a>
                            </c:if>

                            <!-- 현재 페이지 그룹의 페이지 링크 forEach 문 돌림 -->
                            <c:forEach var="i" begin="${currentGroupFirstPage}" end="${currentGroupLastPage}">
                                <c:choose>
                                    <c:when test="${i == currentPage}">
                                        <span class="current-page" style="color:#e06500; ">${i}</span>
                                    </c:when>
                                    <c:otherwise>
                                        <a href="/board-home?page=${i}">${i}</a>
                                    </c:otherwise>
                                </c:choose>
                                &nbsp &nbsp
                            </c:forEach>

                            <!-- 다음 그룹 링크 -->
                            <c:if test="${currentGroupLastPage != totalPages}">
                                <a href="/board-home?page=${currentGroupLastPage + 1}">다음 &raquo;</a>
                            </c:if>
                        </div>


                    </div>

                </div>
            <div>
            </div>
        </div>

    </div>
</body>
</html>

 

더보기

#board-container{
padding-top: 8vh;
width: 100vw;
height: 150vh;
display:flex;
flex-direction: column;
align-items: center;
font-family: 'S-CoreDream-3Light';
}
#page-title{
display:flex;
width:100%;
flex-basis: 15%;
}
#page-title>div:nth-child(1),
#page-title>div:nth-child(3){
flex-basis:10%;

}
#page-title>div:nth-child(2){
flex-basis:80%;
display:flex;
justify-content:center;
align-items:center;
font-size:30px;
}

#page-body{
display:flex;
width:100%;
flex-basis: 85%;

}
#page-body>div:nth-child(1),
#page-body>div:nth-child(3){
flex-basis:10%;
}
#page-body>div:nth-child(2){
flex-basis:80%;
display:flex;
flex-direction:column;
}
#page-body>div:nth-child(2)>div:nth-child(1){
flex-basis: 2%;
border-top: 3px solid #001942;
font-size: 12px;
}
#page-body>div:nth-child(2)>div:nth-child(2){
flex-basis: 4%;
border-bottom: 1px solid lightgray;
}

#page-body>div:nth-child(2)>div:nth-child(3){
flex-basis: 94%;

}

#page-body>div:nth-child(2)>div:nth-child(4){
flex-basis: 7%;

}
#page-body>div:nth-child(2)>div:nth-child(5){
flex-basis: 7%;

}


.board{
display: grid;
grid-template-columns: 1fr 4fr 2fr 1fr 1fr;
align-items: center;
justify-items: center;
font-size: 12px;
}
.board-2{
border-bottom: 1px solid lightgray;
}

.post-row{
display: grid;
grid-template-columns: 1fr 4fr 2fr 1fr 1fr;
align-items: center;
justify-items: center;
border-bottom: 1px solid lightgray;
font-size: 12px;
min-height: 40px; /*각 행의 높이 조절*/
}

a{
text-decoration: none;
color: black;
}
#post-make-div{
height: 5vh;
}
.post-make{
display: inline-flex;
margin-top: 2px;
height: 80%;
align-items:center;
border: 1px solid black;
background-color: white;
color: black;
border-radius: 4px;
transition: background-color 0.5s, color 0.5s;
}
.post-make:hover{
background-color: black;
color: white;
}

#find-posts{
display:flex;
justify-content: center;
align-items: flex-start;
height: 5vh;
}
#pagination{
display:flex;
justify-content: center;
align-items: center;
height: 5vh;
}