베스트 게시물 관련 게시판 구현 (내 프로젝트에 적용)

Back-End/Spring 2019. 9. 26. 17:34
728x90
반응형

회원 게시판에 있는 게시글 중에 추천수 상위 10개의 글만 '베스트 게시물 게시판' 으로 옮겨서 출력하기.


만약 추천수가 동일하다면 조회수 순으로 순위를 매기도록 구현함.




menu.jsp (메뉴 출력 view)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 <!-- views/include/menu.jsp -->
 <%@ include file="header.jsp"%>
 
<center>
 
<!-- 다른 기능들 링크가 걸려있는 메뉴 페이지 -->
 
ㅣ  <a href="${path}/home">메인페이지</a> ㅣ
 
<!-- 어떠한 아이디로든 로그인되지 않았을 경우에만 회원가입 링크를 출력시킨다. -->
 
<c:if test = "${sessionScope.user_id == null and sessionScope.navername == null and sessionScope.kakaonickname == null and sessionScope.facebookname == null and sessionScope.admin_id == null}">
 
<a href="${path}/member/email.do">회원가입</a> ㅣ
 
</c:if>
 
<a href="${path}/board/list.do">회원 게시판</a> ㅣ
 
<a href="${path}/board/best_list.do">베스트 게시물 게시판</a> ㅣ
 
<!-- 관리자가 로그인 하지 않았을 경우에만 로그인 링크를 출력시킴 -->
<c:if test = "${sessionScope.user_id == null and sessionScope.navername == null and sessionScope.kakaonickname == null and sessionScope.facebookname == null and sessionScope.admin_id == null}">
<a href="${path}/admin/admin_login_view.do">관리자 로그인</a> ㅣ    
</c:if>
 
 
<a href="${path}/board/admin_board_list.do">공지사항</a> ㅣ
<br>
<br>
 
<c:if test = "${sessionScope.admin_id != null}">
관리자 메뉴 : ㅣ <a href="${path}/admin/admin_member_forced_eviction_view.do">회원 강제 탈퇴</a> ㅣ    
<a href = "${path}/admin/admin_member_info.do">회원 정보</a> ㅣ
</c:if>
 
</center>
 
 
cs




bestboard.jsp (베스트 게시글 목록 페이지)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<%@ include file="../include/header.jsp"%>
<%@ include file="../include/menu.jsp"%>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
 
<script>
//게시판 목록 페이지로 이동하게 하는 함수
function list(page){
    console.log("페이지를 이동합니다.");
    location.href="list.do?curPage="+page;
};
 
</script>
 
 
 
<%@ include file="../include/login.jsp"%><br>
    
 
 
<center>
<h2>베스트 게시물 게시판</h2>
<table border = "1" width = "800px" align = "top">
<center>
    <tr>
        
        <th>추천수 순위</th>
        <th>회원 게시글 번호</th>
        <th>제목</th>
        <th>작성자</th>
        <th>내용</th>
        <th>날짜</th>
        <th>조회수</th>
        <th>추천수</th>
 
        
    <!-- forEach var = "개별데이터" items = "집합데이터" -->
    <c:forEach var = "row" items = "${map.list}"<!-- 컨트롤러에서 map안에 list를 넣었기 때문에 이렇게 받는다. -->
    <tr>
        <td>${row.rk}</td>    <!-- 게시글 순위 -->
        <td>${row.member_bno}</td>    <!-- 글번호 -->
        <!-- 클릭하면 컨트롤러의 view.do로 이동하고, 게시물번호, 페이지 번호, 검색옵션, 키워드를 같이 넘긴다 -->
        <td>
        <a href="best_board_view.do?member_bno=${row.member_bno}">${row.title}</a>
<c:if test="${row.rcnt > 0}"
   <span style="color:red;">( ${row.rcnt} )</span
</c:if>  
</td>
 
        <td>${row.user_id}</td>    <!-- 작성자의 이름 -->
        <td>${row.content}</td>    <!-- 글의내용 -->
        <td>${row.reg_date}</td>    <!-- 날짜의 출력형식을 변경함 -->
        <td>${row.viewcnt}</td>    <!-- 조회수 -->
        <td>${row.recommend}</td>    <!-- 추천수 -->
 
 
    
    </tr>
    </c:forEach>
    
 
</table>
 
 
<form name="form1" method="post" action="list.do">
    <select name="search_option">
        <option value="user_id"
<c:if test="${map.search_option == 'user_id'}">selected</c:if>
        >작성자</option>
        <option value="title" 
<c:if test="${map.search_option == 'title'}">selected</c:if>
        >제목</option>
        <option value="content" 
<c:if test="${map.search_option == 'content'}">selected</c:if>
        >내용</option>
        <option value="all" 
<c:if test="${map.search_option == 'all'}">selected</c:if>
        >작성자+내용+제목</option>
    </select>
    <input name="keyword" value="${map.keyword}">
    <input type="submit" value="조회">
   
</form>
 
 
 
 
</center>
<br><br><%@ include file="../include/Botton.jsp"%>
</body>
</html>
cs




bestboardview.jsp (베스트 게시글 상세보기 페이지)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<%@ include file="../include/header.jsp" %>
<%@ include file="../include/menu.jsp" %>
<script src="${path}/include/js/common.js"></script>
<script src="${path}/ckeditor/ckeditor.js"></script>
 
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$(function(){
    
    //목록 버튼
    $("#btnList").click(function(){
        location.href="best_list.do";
    });
 
    
//추천하기 버튼
$("#btnRecommend").click(function(){
    if(confirm("해당 글을 추천하시겠습니까?")){
        document.form1.action="recommend.do";
        document.form1.submit();
        
        alert("해당 글을 추천하였습니다.")
        
        }
    });
 
    
    
});
 
 
 
</script>
 
<h2>베스트 게시물 보기</h2>
<!-- 게시물을 작성하기 위해 컨트롤러의 insert.do로 맵핑 -->
<form id="form1" name="form1" method="post" action="${path}/board/insert.do">
<input type = "hidden" id = "member_bno" name = "member_bno" value = "${dto.member_bno }">
    <div>제목 <input name="title" id="title" size="80"
                    value="${dto.title}"
                    placeholder="제목을 입력하세요"><br><br>
<!-- placeholder은 제목을 입력할 수 있도록 도움말을 출력함 -->
    </div>
    <div>조회수 : ${dto.viewcnt}    </div><br><br>
    <div style="width:800px;">
        <textarea id="content" name="content"
rows="3" cols="80" 
placeholder="내용을 입력하세요">${dto.content}</textarea></div><br><br>
</form>
 
 
 
<!-- 마찬가지로 내용을 입력하도록 도움말을 출력함 -->
<script>
// ckeditor 적용
//id가 content인 태그 (글의 내용을 입력하는 태그)를 ck에디터를 적용한다는 의미
CKEDITOR.replace("content",{
    height: "300px"
});
 
CKEDITOR.replace("r_content",{
    height: "300px"
});
</script>
 
<div style = "width:700px; text-align:center;">
<!-- 수정, 삭제에 필요한 글번호를 hidden 태그에 저장한다. -->
    <input type = "hidden" name = "member_bno" value = "${dto.member_bno }">
    
 
    <!-- 관리자에게는 삭제 버튼을 표시한다. -->
    <c:if test = "${sessionScope.admin_id != null}">
            <button type = "button" id = "btnDelete">삭제</button>
    </c:if>
    
    
    <!-- 로그인이 되어있고, 본인 글이 아닐경우에만 추천할 수 있도록 버튼을 출력 -->
    <c:if test = "${sessionScope.user_id != null and sessionScope.user_id != dto.user_id
    or sessionScope.navername != null and sessionScope.navername != dto.user_id
    or sessionScope.kakaonickname != null and sessionScope.kakaonickname != dto.user_id
    or sessionScope.facebookname != null and sessionScope.facebookname != dto.user_id}">
            <button type = "button" id = "btnRecommend">추천하기</button>
    
    </c:if>
    
    <!-- 관리자에게도 추천 버튼 출력 -->
    <!-- 관리자에게는 삭제 버튼을 표시한다. -->
    <c:if test = "${sessionScope.admin_id != null}">
            <button type = "button" id = "btnRecommend">추천하기</button>
    </c:if>
    
    
    <!-- 글목록은 본인이 아니어도 확인 가능하게 한다. -->
    <button type = "button" id = "btnList">목록</button><br><br>
    
<body>
<br><br><%@ include file="../include/Botton.jsp"%>
</body>
</html>
cs




MemberBoardController.java (게시판 컨트롤러 중 일부)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package com.example.hansub_project.controller.board;
 
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
 
import com.example.hansub_project.Member_Pager;
import com.example.hansub_project.controller.member.MemberController;
import com.example.hansub_project.model.board.dto.MemberBoardDTO;
import com.example.hansub_project.model.board.dto.MemberBoardReplyDTO;
import com.example.hansub_project.model.member.dto.MemberDTO;
import com.example.hansub_project.service.board.MemberBoardService;
import com.example.hansub_project.service.member.MemberService;
 
 
@Controller    //게시판 관련 컨트롤러를 선언함
public class MemberBoardController {
    
    @Inject        //서비스를 호출하기위해서 의존성을 주입함
    MemberBoardService memberboardservice;
    
    @Inject        
    MemberService memberservice;
    
    //로깅을 위한 변수
        private static final Logger logger=
        LoggerFactory.getLogger(MemberBoardController.class);
    
 
    
    //베스트 게시판 게시글 출력
    @RequestMapping("/board/best_list.do")    //세부적인 url mapping
    public ModelAndView best_list(//RequestParam으로 옵션, 키워드, 페이지의 기본값을 각각 설정해준다.
            )
             throws Exception{
        
        List<MemberBoardDTO> list = memberboardservice.bestlistAll();
        
        ModelAndView mav = new ModelAndView();
        Map<String,Object> map = new HashMap<>();    //넘길 데이터가 많기 때문에 해쉬맵에 저장한 후에 modelandview로 값을 넣고 페이지를 지정
        
        map.put("list", list);                         //map에 list(게시글 목록)을 list라는 이름의 변수로 자료를 저장함.
    
        mav.addObject("map", map);                    //modelandview에 map를 저장
        
        mav.setViewName("board/bestboard");                //자료를 넘길 뷰의 이름
        
        return mav;    //게시판 페이지로 이동
    
        
    }
    
    
    
    //베스트 게시판 게시물 세부 내용 확인
    @RequestMapping(value = "/board/best_board_view", method= {RequestMethod.GET, RequestMethod.POST})
    public ModelAndView best_board_view(@RequestParam int member_bno, HttpSession session) throws Exception{
        
        //조회수 증가 쿼리
        memberboardservice.increaseViewcnt(member_bno, session);
        
        ModelAndView mav = new ModelAndView();
        mav.setViewName("board/bestboardview");
        
        //view로 자료를 넘기기위해서 mav에 값들을 저장해서 view.jsp로 리턴시킨다.
        mav.addObject("dto", memberboardservice.read(member_bno)); //상세보기를 한번 클릭하면 조회수를 1증가시킨다.
        
        return mav;     //view로 넘어가서 출력이 된다.
    }
    
    
}
 
cs




MemberBoardServiceImpl.java (게시판 서비스 구현 클래스 중 일부)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package com.example.hansub_project.service.board;
 
import java.util.List;
import java.util.Map;
 
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.servlet.http.HttpSession;
 
import org.springframework.stereotype.Service;
 
import com.example.hansub_project.model.board.dao.MemberBoardDAO;
import com.example.hansub_project.model.board.dto.MemberBoardDTO;
 
@Service    //서비스 빈으로 설정함
public class MemberBoardServiceImpl implements MemberBoardService {
    
    @Inject    //dao를 호출하기 때문에 의존성을 주입한다.
    MemberBoardDAO memberboarddao;
 
    @Override
    public void create(MemberBoardDTO dto) throws Exception {
        memberboarddao.create(dto);
        //dto를 매개값으로 dao를 호출한다.
    }
 
    //게시물 읽기
    @Override
    public MemberBoardDTO read(int member_bno) throws Exception {
        return memberboarddao.read(member_bno);
    }
 
    //게시글 수정
    @Override
    public void update(MemberBoardDTO dto) throws Exception {
        memberboarddao.update(dto);
        
    }
    
    //게시물 삭제 관련 메소드
    @Override
    public void delete(int member_bno) throws Exception {
        memberboarddao.delete(member_bno);
        
    }
 
    @Override
    public List<MemberBoardDTO> listAll(String search_option, String keyword,int start, int end) throws Exception {
 
        return memberboarddao.listAll(search_option, keyword, start, end);
    }
 
    
    //조회수를 증가하게하는 쿼리
    //조회수 처리를 할때 일정 시간이 지난후 다시 클릭할때만 조회수가 증가하도록 설정
    @Override
    public void increaseViewcnt(int member_bno, HttpSession session) throws Exception {
        long update_time = 0//null을 방지하기 위해 초기값을 null로 설정함
        if(session.getAttribute("update_time_"+member_bno)!=null) {
            
            //최근에 조회수를 올린 시간이 null이 아니면
            update_time = (long)session.getAttribute("update_time_"+member_bno);
        }
        
        long current_time = System.currentTimeMillis();
        
        //일정 시간이 경과한 후에 조회수를 증가시킨다.
        if(current_time - update_time > 5 * 1000) {
            
        //조회수가 1증가했을때로부터 5000초 후에 다시 클릭을 해야 조회수가 다시 1 증가한다는 말이다.
        //조회수 증가 처리
            memberboarddao.increateViewcnt(member_bno);
            
            //조회수를 올린 시간을 저장함
            session.setAttribute("update_time_"+member_bno, current_time);
            
        }
        
    }
 
    @Override
    public int countArticle(String search_option, String keyword) throws Exception {
        return memberboarddao.countArticle(search_option,keyword); 
    }
 
    //게시글 추천관련 메소드 구현
    @Override
    public void recommend(int member_bno) throws Exception {
         
        memberboarddao.recommend(member_bno);
        
        }
    
    //베스트 게시판 게시글 목록 출력
    @Override
    public List<MemberBoardDTO> bestlistAll() throws Exception {
        
        return memberboarddao.bestlistAll();
        
        }
    }
 
 
 
cs




MemberBoardDAOImpl.java (게시판 DAO 중 일부)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package com.example.hansub_project.model.board.dao;
 
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import javax.inject.Inject;
 
import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;
 
import com.example.hansub_project.model.board.dto.MemberBoardDTO;
import com.example.hansub_project.model.member.dao.MemberDAO;
 
 
@Repository        //dao 선언
public class MemberBoardDAOImpl implements MemberBoardDAO {
 
    @Inject    //db에 접속하기 위해 의존관계를 주입
    SqlSession sqlSession; 
    
    
    //게시글 쓰기
    @Override
    public void create(MemberBoardDTO dto) throws Exception {
        sqlSession.insert("memberboard.insert",dto);
        
    }
 
    //게시글 수정 
    @Override
    public void update(MemberBoardDTO dto) throws Exception {
        sqlSession.update("memberboard.update", dto);
        
    }
 
    //게시물 삭제 관련
    @Override
    public void delete(int member_bno) throws Exception {
        sqlSession.delete("memberboard.deleteArticle", member_bno); //mapper로 게시글 번호를 넘긴다.
        
    }
    
    //게시물 목록을 리턴
    @Override
    public List<MemberBoardDTO> listAll(String search_option, String keyword, int start, int end) throws Exception {
        
        Map<String,Object> map = new HashMap<>();
          map.put("search_option", search_option);
          map.put("keyword", keyword);
          map.put("start", start); //맵에 자료 저장
          map.put("end", end);
        
        //매개변수는 시작 레코드의 번호, 끝 번호, 옵션과 키워드가 들어간다.
        return sqlSession.selectList("memberboard.listAll", map);
    }
    
    //조회수 증가처리를 하는 메소드
    @Override
    public void increateViewcnt(int member_bno) throws Exception {
        
        sqlSession.update("memberboard.increaseViewcnt", member_bno);
    }
 
    @Override
    public int countArticle(String search_option, String keyword) throws Exception {
 
        Map<String,String> map=new HashMap<>();
        map.put("search_option", search_option);
        map.put("keyword""%"+keyword+"%");
        return sqlSession.selectOne("memberboard.countArticle",map);
    }
 
    //게시글 상세정보 
    @Override
    public MemberBoardDTO read(int member_bno) throws Exception {
        
        return sqlSession.selectOne("memberboard.read", member_bno);
    }
 
    //추천수 증가처리 메소드
    @Override
    public void recommend(int member_bno) throws Exception {
        
        sqlSession.update("memberboard.recommend", member_bno);
    }
 
    
    //베스트 게시글 게시판 게시글 목록 출력
    @Override
    public List<MemberBoardDTO> bestlistAll() throws Exception {
        
            return sqlSession.selectList("bestboard.bestlistAll");
        }
    
}    
    
cs




bestboardMapper.xml (베스트 게시판 관련 게시글 목록 출력 Mapper)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
<!-- 다른 mapper와 중복되지 않도록 네임스페이스 기재 -->
 
<mapper namespace="bestboard">
    
<!-- 베스트게시물 게시판에 출력되는 목록 mapper -->

<!-- 순위를 출력할때 먼저 추천수 기준으로 순위를 매기고 추천수가 동일한 경우에는
조회수를 기준으로 순위를 다시 매겨서 중복이 없도록 출력한다. -->

<select id="bestlistAll" resultType="com.example.hansub_project.model.board.dto.MemberBoardDTO">
 
select member_bno, 
user_id, 
reg_date, 
viewcnt, 
title, 
rcnt, 
content, 
recommend,
row_number() over (order by recommend desc, viewcnt desc) as rk
from 
    (
    select recommend, member_bno, user_id, reg_date, viewcnt, title, rcnt, content
    from member_board
    order by recommend desc
)
<![CDATA[where rownum <= 10]]>
        
</select>
 
 
 
</mapper>
 
cs











728x90
반응형
: