Spring 게시판 목록 (내 프로젝트에 적용)

Back-End/Spring 2019. 8. 9. 16:41
728x90
반응형


1. 게시판 관련 Controller, model, service , view, Pager(게시판 목록 페이징 관련), Mapper을 생성





2. 각 파일들을 생성해서 연동


Pager.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package com.example.hansub_project;
 
public class Pager {
 
    public static final int PAGE_SCALE=25;    //페이지당 게시물수
    public static final int BLOCK_SCALE=10;    //화면당 페이지수
    
    private int curPage;     //현재 페이지
    private int prevPage;    //이전 페이지
    private int nextPage;    //다음 페이지
    private int totPage;    //전체 페이지 갯수
    private int totBlock;     //전체 페이지블록 갯수
    private int curBlock;     //현재 블록
    private int prevBlock;     //이전 블록
    private int nextBlock;     //다음 블록
    private int pageBegin;     // #{start} 변수에 전달될 값
    private int pageEnd;     // #{end} 변수에 전달될 값
    private int blockBegin; //블록의 시작페이지 번호
    private int blockEnd;     //블록의 끝페이지 번호
    
    
    //생성자
    // Pager(레코드갯수, 출력할페이지번호)
    public Pager(int count, int curPage) {
        curBlock = 1//현재블록 번호
        this.curPage = curPage; //현재 페이지 번호
        setTotPage(count); //전체 페이지 갯수 계산
        setPageRange(); // #{start}, #{end} 값 계산하는 메소드
        setTotBlock(); // 전체 블록 갯수 계산
        setBlockRange(); //블록의 시작,끝 번호 계산
    }
 
 
    public void setBlockRange() {
        //원하는 페이지가 몇번째 블록에 속하는지 계산
        curBlock=(curPage-1)/BLOCK_SCALE + 1;
        //블록의 시작페이지,끝페이지 번호 계산
        blockBegin=(curBlock-1)*BLOCK_SCALE+1;
        blockEnd=blockBegin+BLOCK_SCALE-1;
        //마지막 블록 번호가 범위를 초과하지 않도록 처리
        if(blockEnd > totPage) {
            blockEnd = totPage;
        }
        //[이전][다음]을 눌렀을 때 이동할 페이지 번호
        prevPage=(curBlock==1) ? 1 : (curBlock-1)*BLOCK_SCALE;
        nextPage=curBlock>totBlock ? (curBlock*BLOCK_SCALE)
                : (curBlock*BLOCK_SCALE)+1;
        //마지막 페이지가 범위를 초과하지 않도록 처리
        if(nextPage >= totPage) {
            nextPage=totPage;
        }
    }
    
    //페이지블록의 총 갯수 계산 (총 100페이지라면 10개의 블록이다)
    public void setTotBlock() {
        totBlock = (int)Math.ceil(totPage*1.0 / BLOCK_SCALE);
    }
    
// where rn between #{start} and #{end}에 입력될 값        
    public void setPageRange() {
// 시작번호=(현재페이지-1)x페이지당 게시물수 + 1
// 끝번호=시작번호 + 페이지당 게시물수 - 1        
        pageBegin = (curPage-1* PAGE_SCALE + 1;
        pageEnd = pageBegin + PAGE_SCALE - 1;
    }
    
    
    public int getCurPage() {
        return curPage;
    }
    public void setCurPage(int curPage) {
        this.curPage = curPage;
    }
    public int getPrevPage() {
        return prevPage;
    }
    public void setPrevPage(int prevPage) {
        this.prevPage = prevPage;
    }
    public int getNextPage() {
        return nextPage;
    }
    public void setNextPage(int nextPage) {
        this.nextPage = nextPage;
    }
    public int getTotPage() {
        return totPage;
    }
    public void setTotPage(int totPage) {
        this.totPage = totPage;
    }
    public int getTotBlock() {
        return totBlock;
    }
    public void setTotBlock(int totBlock) {
        this.totBlock = totBlock;
    }
    public int getCurBlock() {
        return curBlock;
    }
    public void setCurBlock(int curBlock) {
        this.curBlock = curBlock;
    }
    public int getPrevBlock() {
        return prevBlock;
    }
    public void setPrevBlock(int prevBlock) {
        this.prevBlock = prevBlock;
    }
    public int getNextBlock() {
        return nextBlock;
    }
    public void setNextBlock(int nextBlock) {
        this.nextBlock = nextBlock;
    }
    public int getPageBegin() {
        return pageBegin;
    }
    public void setPageBegin(int pageBegin) {
        this.pageBegin = pageBegin;
    }
    public int getPageEnd() {
        return pageEnd;
    }
    public void setPageEnd(int pageEnd) {
        this.pageEnd = pageEnd;
    }
    public int getBlockBegin() {
        return blockBegin;
    }
    public void setBlockBegin(int blockBegin) {
        this.blockBegin = blockBegin;
    }
    public int getBlockEnd() {
        return blockEnd;
    }
    public void setBlockEnd(int blockEnd) {
        this.blockEnd = blockEnd;
    }
 
     
}
 
cs



view를 작성 ( memberboard.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
<%@ 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"%>
<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>
 
</head>
<body>
<br><%@ include file="../include/menu.jsp"%><br>
<center>
<h2>회원 게시판</h2>
<table border = "1" width = "600px">
<center>
    <tr>
    
        <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.member_bno}</td>    <!-- 글번호 -->
        <td>${row.title}</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>
    
    <!-- 페이지 네비게이션 (페이지 알고리즘 관련) 출력 -->
 <tr>
        <td colspan = "7" align = "center">
            <c:if test="${map.pager.curBlock > 1}">
  <a href="#" onclick="list('1')">[처음]</a>
            </c:if<!-- 현재 블록이 1블록보다 크면 (뒤쪽에 있기때문에) 처음으로 갈 수 있도록 링크를 추가 -->
        
            <c:if test="${map.pager.curBlock > 1}">
                <a href="#" onclick="list('${map.pager.prevPage}')">[이전]</a>
            </c:if<!-- 현재 블록이 1블록보다 크면 이전 블록으로 이동할 수 있도록 링크 추가 -->
            
            <c:forEach var="num"
                begin="${map.pager.blockBegin}"
                end="${map.pager.blockEnd}">
                <c:choose>
                    <c:when test="${num == map.pager.curPage}">
                    
                    <!-- 현재 페이지인 경우 하이퍼링크 제거 -->
                    <!-- 현재 페이지인 경우에는 링크를 빼고 빨간색으로 처리를 한다. -->
                        <span style="color:red;">${num}</span>
                    </c:when>
                    <c:otherwise>
                        <a href="#" onclick="list('${num}')" >${num}</a>
                    </c:otherwise>
                </c:choose>
            </c:forEach>
            
            
            <c:if test="${map.pager.curBlock <= map.pager.totBlock}">
                <a href="#" onclick="list('${map.pager.nextPage}')">[다음]</a>
            </c:if<!-- 현재 페이지블록이 총 페이지블록보다 작으면 다음으로 갈 수있도록 링크를 추가 -->
            
            
            <c:if test="${map.pager.curPage <= map.pager.totPage}">
                <a href="#" onclick="list('${map.pager.totPage}')">[끝]</a>
            </c:if<!-- 현재 페이지블록이 총 페이지블록보다 작거나 같으면 끝으로 갈 수 있도록 링크를 추가함-->
            </td>
    </tr>
    
    </center>
</table>

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
package com.example.hansub_project.controller.board;
 
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import javax.inject.Inject;
 
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
 
import com.example.hansub_project.Pager;
import com.example.hansub_project.controller.member.MemberController;
import com.example.hansub_project.model.board.dto.MemberBoardDTO;
import com.example.hansub_project.service.board.MemberBoardService;
import com.example.hansub_project.service.member.MemberService;
 
 
@Controller    //게시판 관련 컨트롤러를 선언함
public class MemberBoardController {
    
    @Inject        //서비스를 호출하기위해서 의존성을 주입함
    MemberBoardService memberboardservice;
    
    //로깅을 위한 변수
        private static final Logger logger=
        LoggerFactory.getLogger(MemberBoardController.class);
    
    @RequestMapping("list.do")    //세부적인 url mapping
    public ModelAndView list(//RequestParam으로 옵션, 키워드, 페이지의 기본값을 각각 설정해준다.
            
//초기값을 설정해야 에러가 발생되지 않는다.
            @RequestParam(defaultValue="1"int curPage,
            @RequestParam(defaultValue="user_id"String search_option,
            @RequestParam(defaultValue=""String keyword
 
            )
             throws Exception{
        
        //레코드 갯수를 계산
        int count = 1000;
        
        //페이지 관련 설정, 시작번호와 끝번호를 구해서 각각 변수에 저장한다.
        Pager pager = new Pager(count, curPage);
        int start = pager.getPageBegin();
        int end =  pager.getPageEnd();
        

//map에 담기위해 리스트에 검색옵션, 키워드, 시작과 끝번호를 저장
        List<MemberBoardDTO> list = memberboardservice.listAll(search_option, keyword, start, end);
        
        ModelAndView mav = new ModelAndView();
        Map<String,Object> map = new HashMap<>();    //넘길 데이터가 많기 때문에 해쉬맵에 저장한 후에 modelandview로 값을 넣고 페이지를 지정
        
        map.put("list", list);                         //map에 list(게시글 목록)을 list라는 이름의 변수로 자료를 저장함.
        map.put("pager", pager);
        map.put("count", count);
        map.put("search_option", search_option);
        map.put("keyword", keyword);
        mav.addObject("map", map);                    //modelandview에 map를 저장
        
        mav.setViewName("board/memberboard");                //자료를 넘길 뷰의 이름
        
        return mav;    //게시판 페이지로 이동
    
    }
 
}
 
cs




게시판 관련 서비스 인터페이스와 구현 클래스를 생성


MemberBoardService (서비스 인터페이스)

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
package com.example.hansub_project.service.board;
 
import java.util.List;
import java.util.Map;
 
import javax.servlet.http.HttpSession;
 
import com.example.hansub_project.model.board.dto.MemberBoardDTO;
 
 
public interface MemberBoardService {
 
    
    //목록 (페이지 나누기, 검색 기능을 포함)
    //매개변수는 시작 레코드 번호, 끝번호, 옵션과 키워드가 들어간다.
    public List<MemberBoardDTO> listAll(String search_option, String keyword, int start, int end)throws Exception;
    
   
}
 
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
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 List<MemberBoardDTO> listAll(String search_option, String keyword,int start, int end) throws Exception {
 
        return memberboarddao.listAll(search_option, keyword, start, end);
    }
 
 
}
 
cs



DAO관련 인터페이스와 구현 클래스 생성


MemberBoardDAO (인터페이스)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.example.hansub_project.model.board.dao;
 
import java.util.List;
 
 
import com.example.hansub_project.model.board.dto.MemberBoardDTO;
 
//DAO 클래스
public interface MemberBoardDAO {

    //게시글 리스트를 출력하는 메소드 (검색옵션, 키워드, 시작번호와 끝번호 포함)
    public List<MemberBoardDTO> listAll(String search_option, String keyword,int start, int end) throws Exception;     
    
    
}
 
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
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 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);
    }
    
}
cs




계층별 자료 전송을 위해 DTO 클래스 작성


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
package com.example.hansub_project.model.board.dto;
 
import java.sql.Date;
 
//회원 게시판 관련 dto 클래스
public class MemberBoardDTO {
    
    private int member_bno;        //게시글 번호
    private String user_id;        //작성자 아이디
    private Date reg_date;        //작성 날짜
    private int viewcnt;        //조회수
    private String title;        //제목
    private int rcnt;            //댓글 갯수
    private String content;        //댓글 내용
    private int recommend;        //추천수
    

    public int getMember_bno() {
        return member_bno;
    }
    public void setMember_bno(int member_bno) {
        this.member_bno = member_bno;
    }
    public String getUser_id() {
        return user_id;
    }
    public void setUser_id(String user_id) {
        this.user_id = user_id;
    }
    public Date getReg_date() {
        return reg_date;
    }
    public void setReg_date(Date reg_date) {
        this.reg_date = reg_date;
    }
    public int getViewcnt() {
        return viewcnt;
    }
    public void setViewcnt(int viewcnt) {
        this.viewcnt = viewcnt;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public int getRcnt() {
        return rcnt;
    }
    public void setRcnt(int rcnt) {
        this.rcnt = rcnt;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public int getRecommend() {
        return recommend;
    }
    public void setRecommend(int recommend) {
        this.recommend = recommend;
    }
    
    
    @Override
    public String toString() {
        return "MemberBoardDTO [member_bno=" + member_bno + ", user_id=" + user_id + ", reg_date=" + reg_date
                + ", viewcnt=" + viewcnt + ", title=" + title + ", rcnt=" + rcnt + ", content=" + content
                + ", recommend=" + recommend + "]";
    }
 
}
 
cs




게시판 목록과 페이지 관련, 검색기능을 추가한 쿼리를 mapper에 작성


BoardMapper.xml 작성

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
<?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="memberboard">
    
    <!-- rownum을 rn이란 문자로 줄여쓴다. 밑에 from문을 A로 줄여쓴다.  -->
                  <!-- 이 from문이 먼저 실행된 다음에 번호를 붙였기 때문에 일련번호가 다시 새로 매겨졌다. -->
                <!--  이 안쪽의 쿼리가 가장 중요함 -->
                
                
<select id="listAll" resultType="com.example.hansub_project.model.board.dto.MemberBoardDTO">
<!-- 쿼리의 결과를 출력시키기 위해서 dto 타입으로 반환한다.-->
        <include refid="paging_header" />  //밑에 있는 sql문을 include 한다
        
        <!-- ref는 다른테이블을 의미한다. -->
        <!-- 번호, 제목, 작성자, 이름, 날짜, 조회수 , 그리고 댓글의 갯수를 검색 -->
        <!-- board 테이블과 member 테이블로 부터 검색 -->
            select member_bno, user_id, reg_date, viewcnt, title, rcnt, content, recommend            
            from member_board
            <!-- bno의 내림차순으로 검색 -->
            <!-- where절은 (조건)은 include 태그를 이용했음 -->

        <include refid="search" />
            order by member_bno desc
        <include refid="paging_footer" />        
    </select>
    
    
    <sql id="paging_header">
<!-- 게시물을 한페이지에 10개씩 볼 수 있게하는 쿼리 윗부분-->
        select *
        from (
            select rownum as rn, A.*
            from (    
    </sql>
    
    
    
    <sql id="paging_footer">
<!-- 게시물을 한페이지에 10개씩 볼 수 있게하는 쿼리  아랫 부분-->
    <!-- 새로 매겨진 일련번호 1~10번 글까지 1페이지 -->
    <!-- 11~20번 글까지 2페이지.. -->
            ) A
        ) where rn between #{start} and #{end}    
    </sql>
    
    
 
    
<sql id="search">
<choose>
          //작성자 , 내용, 제목으로 각각 검색할 수 있게끔 한다.
            <when test="search_option == 'all' ">
                where  
                      user_id like '%'||#{keyword}||'%'
                      or content like '%' || #{keyword}||'%'
                      or title like '%'||#{keyword}||'%'   
            </when>
            <otherwise>
//작성자 + 내용 + 제목을 포함한 항목을 검색할 수 있게끔 한다.
                where ${search_option} like '%'||#{keyword}||'%'
            </otherwise>
        </choose>
    </sql>
        
 
 
</mapper>
 
cs










728x90
반응형
: