고래씌
[JSP] 5-1. 게시판 만들기(페이지) 본문
1. 게시판 만들기
- SERVER 계정에 저장되어있는 게시판 테이블과 관련 테이블들을 보도록 하겠다.
① 각 테이블에 관련한 클래스 생성하기
▶ Board 클래스는 Builder를 이용한다.
Builder는 메소드 순서가 안맞아도 필요한 값만 넣고 가져올수 있기 때문에 유용하므로 Builder를 사용한다!
■ Board 클래스
package com.kh.board.model.vo;
import java.sql.Date;
import oracle.net.aso.n;
public class Board {
// BOARD_NO NUMBER
private int boardNo;
// BOARD_TYPE NUMBER
private int boardType;
// CATEGORY_NO NUMBER
private int categoryNo;
// BOARD_TITLE VARCHAR2(100 BYTE)
private String boardTitle;
// BOARD_CONTENT VARCHAR2(4000 BYTE)
private String boardContent;
// BOARD_WRITER NUMBER
private String boardWriter;
// 쿼리문으로 join시에는 회원번호를 활용하여 join할 것이고,
// 조회결과 작성한 사용자의 이름 or id를 저장하기 위한 필드
// COUNT NUMBER
private int count;
// CREATE_DATE DATE
private Date createDate;
// STATUS VARCHAR2(1 BYTE)
private String status;
private String categoryName; // Board테이블에는 존재하지 않는 칼럼
// DB에 Category 테이블에도 CATEGORY_NO가 있기 때문에 하나 생성 해둠
public Board() {
}
public static class Builder {
private int boardNo;
private int boardType;
private int categoryNo;
private String boardTitle;
private String boardContent;
private String boardWriter;
private int count;
private Date createDate;
private String status;
private String categoryName; // Board테이블에는 존재하지 않는 칼럼
public Board bulid() {
// 빌더에 초기화한 값을 그대로 Builder로 반환시켜줌
// Board 타입 객체로 모두 옮겨놓음
Board b = new Board();
b.boardNo = boardNo;
b.boardType = boardType;
b.categoryNo = categoryNo;
b.boardTitle = boardTitle;
b.boardContent = boardContent;
b.boardWriter = boardWriter;
b.count = count;
b.createDate = createDate;
b.status = status;
b.categoryName = categoryName;
return b;
}
public Builder boardNo(int boardNo) {
this.boardNo = boardNo;
return this;
}
public Builder boardType(int boardType) {
this.boardType = boardType;
return this;
}
public Builder categoryNo(int categoryNo) {
this.categoryNo = categoryNo;
return this;
}
public Builder boardTitle(String boardTitle) {
this.boardTitle = boardTitle;
return this;
}
public Builder boardContent(String boardContent) {
this.boardContent = boardContent;
return this;
}
public Builder boardWriter(String boardWriter) {
this.boardWriter = boardWriter;
return this;
}
public Builder count(int count) {
this.count = count;
return this;
}
public Builder createDate(Date createDate) {
this.createDate = createDate;
return this;
}
public Builder status(String status) {
this.status = status;
return this;
}
public Builder categoryName(String categoryName) {
this.categoryName = categoryName;
return this;
}
}
// 똑같이 한 이유는 /...?
@Override
public String toString() {
return "Board [boardNo=" + boardNo + ", boardType=" + boardType + ", categoryNo=" + categoryNo + ", boardTitle="
+ boardTitle + ", boardContent=" + boardContent + ", boardWriter=" + boardWriter + ", count=" + count
+ ", createDate=" + createDate + ", status=" + status + ", categoryName=" + categoryName + "]";
}
public int getBoardNo() {
return boardNo;
}
public void setBoardNo(int boardNo) {
this.boardNo = boardNo;
}
public int getBoardType() {
return boardType;
}
public void setBoardType(int boardType) {
this.boardType = boardType;
}
public int getCategoryNo() {
return categoryNo;
}
public void setCategoryNo(int categoryNo) {
this.categoryNo = categoryNo;
}
public String getBoardTitle() {
return boardTitle;
}
public void setBoardTitle(String boardTitle) {
this.boardTitle = boardTitle;
}
public String getBoardContent() {
return boardContent;
}
public void setBoardContent(String boardContent) {
this.boardContent = boardContent;
}
public String getBoardWriter() {
return boardWriter;
}
public void setBoardWriter(String boardWriter) {
this.boardWriter = boardWriter;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
}
■ Category 클래스
package com.kh.board.model.vo;
public class Category {
private int categoryNo;
private String categoryName;
public Category() {
}
public Category(int categoryNo, String categoryName) {
super();
this.categoryNo = categoryNo;
this.categoryName = categoryName;
}
public int getCategoryNo() {
return categoryNo;
}
public void setCategoryNo(int categoryNo) {
this.categoryNo = categoryNo;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
@Override
public String toString() {
return "Category [categoryNo=" + categoryNo + ", categoryName=" + categoryName + "]";
}
}
■ Attachment 클래스
package com.kh.board.model.vo;
import java.sql.Date;
public class Attachment {
// FILE_NO NUMBER
private int fileNo;
// REF_BNO NUMBER
private int refBno;
// ORIGIN_NAME VARCHAR2(255 BYTE)
private String originName;
// CHANGE_NAME VARCHAR2(255 BYTE)
private String changName;
// FILE_PATH VARCHAR2(1000 BYTE)
private String filePath;
// UPLOAD_DATE DATE
private Date uploadDate;
// FILE_LEVEL NUMBER
private int fileLevel;
// STATUS VARCHAR2(1 BYTE)
private String status;
public Attachment() {
}
public Attachment(int fileNo, int refBno, String originName, String changName, String filePath, Date uploadDate,
int fileLevel, String status) {
super();
this.fileNo = fileNo;
this.refBno = refBno;
this.originName = originName;
this.changName = changName;
this.filePath = filePath;
this.uploadDate = uploadDate;
this.fileLevel = fileLevel;
this.status = status;
}
public int getFileNo() {
return fileNo;
}
public void setFileNo(int fileNo) {
this.fileNo = fileNo;
}
public int getRefBno() {
return refBno;
}
public void setRefBno(int refBno) {
this.refBno = refBno;
}
public String getOriginName() {
return originName;
}
public void setOriginName(String originName) {
this.originName = originName;
}
public String getChangName() {
return changName;
}
public void setChangName(String changName) {
this.changName = changName;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public Date getUploadDate() {
return uploadDate;
}
public void setUploadDate(Date uploadDate) {
this.uploadDate = uploadDate;
}
public int getFileLevel() {
return fileLevel;
}
public void setFileLevel(int fileLevel) {
this.fileLevel = fileLevel;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public String toString() {
return "Attachment [fileNo=" + fileNo + ", refBno=" + refBno + ", originName=" + originName + ", changName="
+ changName + ", filePath=" + filePath + ", uploadDate=" + uploadDate + ", fileLevel=" + fileLevel
+ ", status=" + status + "]";
}
}
▶ 공통적인 스타일을 주기위해 common.css 파일에 스타일 속성 추가
■ common.css
/* 게시판 목록 시작 */
.list-area{
border : 1px solid white;
text-align : center;
}
.list-area>tbody>tr:hover{
background: gray;
cursor: point;
}
/* 게시판 목록 끝 */
▶ 사용자에게 보여지는 기본화면에 일반게시판 연결을 해주기 위해서 menubar.jsp 에 일반게시판 링크 연결
■ menubar.jsp
② 게시판을 가져오기위해 SQL 쿼리문 작성
■ board-mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="selectList">
SELECT *
FROM(
SELECT ROWNUM RNUM, A.*
FROM(SELECT BOARD_NO,
CATEGORY_NAME,
BOARD_TITLE,
USER_ID,
COUNT,
CREATE_DATE
FROM BOARD B
LEFT JOIN CATEGORY USING(CATEGORY_NO)
JOIN MEMBER ON (BOARD_WRITER = USER_NO)
WHERE BOARD_TYPE = 1
AND B.STATUS = 'Y'
ORDER BY BOARD_NO DESC
) A
<-- STATUS = 'Y'이면서 BOARD_TYPE =1만 가져온다! -->
)
WHERE RNUM BETWEEN ? AND ? <-- 1페이지에서 ~ 몇페이지? -->
</entry>
<entry key="selectListCount">
SELECT COUNT(*)
FROM BOARD
WHERE STATUS = 'Y'
AND BOARD_TYPE = 1
</entry>
</properties>
③ 서버로 전송할 수 있는 Servlet 생성
▶ maxPage (가장 마지막 페이지가 몇번 페이지인지(총 페이지 수))공식
=> listCount, boardLimit에 영향을 받음 - 공식 구하기 단, boardLimit 10 이라는 가정하에 규칙을 세워봄 - 총 개수(listCount) boardLimit maxPage 2000개 / 10개 200번 페이지 2001개 / 10개 200.1번 페이지 => 올림처리 201번페이지 2005개 -------------------------------> 200.5번 → 201번 페이지 2010개 / 10개 201번 페이지 2011개 / 10개 201.1번 페이지 => 올림처리 202번페이지 => 나눗셈 연산한 결과를 올림처리한다면 maxPage 값이 나온다. 1) listCount를 double로 형변환 2) listCount / boardLimit 3) 결과에 올림처리후 4) 결과값을 int로 변환 |
▶ StartPage : 페이징바의 시작수
=> pageLimit, currentPage에 영향을 받음 - 공식 구하기 단, pageLimit는 10이라는 가정하에 규칙을 세워보기. startPage : 1, 11, 21, 31, 41, .... n => n*10+1 이런식으로 증가한다! 만약에 pageLimit가 5가 된다? 1, 6, 11, 16 ... => n*5+1 즉, n*pageLimit+1 의 공식으로 구하면 된다! currentPage(요청한 페이지) | startPage 1 1 5 1 10 1 11 11 15 11 20 11 1~10 => 1 => n*pageLimit + 1 ==> n=0 11~20 => 11 => n*pageLimit + 1 ==> n=1 21~30 => 21 => n*pageLimit + 1 ==> n=2 n = (currentPage - 1) / pageLimit 0~9 / 10 = 0 10~19 / 10 = 1 20~29 / 10 = 2 startPage = n * pageLimit + 1 => (currentPage - 1) / pageLimit * pageLimit + 1 |
▶ endPage : 페이징바의 끝수
=> startPage, pageLimit에 영향을 받음 + maxPage에도 - 공식 구하기(pageLimit는 10이라는 가정) startPage : 1 => 끝수 : 10 startPage : 11 => 20 startPage : 21 => 30 endPage = startPage + pageLimit - 1; |
▶ endPage 를 maxPage 와 같도록 설정
if(endPage > maxPage) { endPage = maxPage; // 끝 페이지가 maxPage보다 많아지면 같도록 설정 } |
▶ 이제 이 페이지 정보를 하나의 공간에 담아서 보내도록 설정
페이지 정보들을 담아주는 vo 클래스가 필요함.
=> 사진 게시판, 공지사항게시판, 그외에 추가될 수 있는 게시판에서도 쓰일 것이므로 common 패키지에 만들기
// 1. 페이징바 만들때 필요한 객체 PageInfo pi = new PageInfo(listCount, currentPage, pageLimit, boardLimit, maxPage, startPage, endPage); // 2. 현재 사용자가 요청한 페이지에 보여질 게시글 리스트 ArrayList<Board> list = new BoardService().selectList(pi); |
▶ 하나의 공간에 담아주는 vo 클래스는 PageInfo.java에서 처리
package com.kh.common.model.vo;
public class PageInfo {
private int listCount; // 총 게시글 개수
private int currentPage; // 현재 요청한 페이지
private int pageLimit; // 페이지바 하단에 보여질 페이징바의 페이지 최대 갯수
private int boardLimit; // 한 페이지에 보여질 게시글의 최대 개수
private int maxPage; // 가장 마지막 페이지가 몇번 페이지인지(총 페이지수)
private int startPage; // 페이지 하단에 보여질 페이징바의 시작수
private int endPage; // 페이지 하단에 보여질 페이징바의 끝 수
public PageInfo(int listCount, int currentPage, int pageLimit, int boardLimit, int maxPage, int startPage,
int endPage) {
super();
this.listCount = listCount;
this.currentPage = currentPage;
this.pageLimit = pageLimit;
this.boardLimit = boardLimit;
this.maxPage = maxPage;
this.startPage = startPage;
this.endPage = endPage;
}
public int getListCount() {
return listCount;
}
public void setListCount(int listCount) {
this.listCount = listCount;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getPageLimit() {
return pageLimit;
}
public void setPageLimit(int pageLimit) {
this.pageLimit = pageLimit;
}
public int getBoardLimit() {
return boardLimit;
}
public void setBoardLimit(int boardLimit) {
this.boardLimit = boardLimit;
}
public int getMaxPage() {
return maxPage;
}
public void setMaxPage(int maxPage) {
this.maxPage = maxPage;
}
public int getStartPage() {
return startPage;
}
public void setStartPage(int startPage) {
this.startPage = startPage;
}
public int getEndPage() {
return endPage;
}
public void setEndPage(int endPage) {
this.endPage = endPage;
}
@Override
public String toString() {
return "PageInfo [listCount=" + listCount + ", currentPage=" + currentPage + ", pageLimit=" + pageLimit
+ ", boardLimit=" + boardLimit + ", maxPage=" + maxPage + ", startPage=" + startPage + ", endPage="
+ endPage + "]";
}
}
④ JDBCTemplate 클래스와 연결하여 연결 객체 생성하기(BoardService 클래스)
■ JDBCTemplate
package com.kh.common;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JDBCTemplate {
// 1. Connection 객체 생성 후 Connection을 반환하는 메소드
public static Connection getConnection() {
// Properties
Properties prop = new Properties(); // 키, 밸류 형태로 문자열형태로 저장함
// 읽어들이고자하는 driver.properties 파일의 물리적인 경로 제시
String fileName = JDBCTemplate.class.getResource("/sql/driver/driver.properties").getPath();
// 현재 경로의 절대경로로 driver.properties 파일을 찾는다.
// C:\Web-workspace2\JSP_Project1\WebContent\WEB-INF\classes\sql\driver => 이 위치를 찾는다
try {
prop.load(new FileInputStream(fileName));
} catch (IOException e) {
e.printStackTrace();
}
Connection conn = null;
// 1) jdbc driver 등록
try {
Class.forName(prop.getProperty("driver"));
// 2) db와 접속된 Connection 객체 생성
conn = DriverManager.getConnection(prop.getProperty("url"),
prop.getProperty("username"),
prop.getProperty("password"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
//2. 전달받은 JDBC용 객체를 반납시켜주는 메소드(객체별로 오버로딩)
//2_1) Connection객체를 전달받아서 반납시켜주는 메소드
public static void close(Connection conn) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//2_2) Statement객체를 전달받아서 반납시켜주는 메소드
public static void close(Statement stmt) { // Statement + PreparedStatment 둘다
// 매개변수로 전달 가능(다형성)
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//2_3) ResultSet 객체를 전달받아서 반납시켜주는 메소드(오버로딩)
public static void close(ResultSet rset) {
try {
rset.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 3. 전달받은 Connection객체를 가지고 트랜잭션 처리를 해주는 메소드
// 3_1.) Commit메소드
public static void commit(Connection conn) {
try {
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 3_2) rollback메소드
public static void rollback(Connection conn) {
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
■ BoardService.java
package com.kh.board.model.service;
import static com.kh.common.JDBCTemplate.close;
import static com.kh.common.JDBCTemplate.getConnection;
import java.sql.Connection;
import java.util.ArrayList;
import com.kh.board.model.dao.BoardDao;
import com.kh.board.model.vo.Board;
import com.kh.common.model.vo.PageInfo;
public class BoardService {
public int selectListCount() {
Connection conn = getConnection();
int listCount = new BoardDao().selectListCount(conn);
close(conn);
return listCount;
}
public ArrayList<Board> selectList(PageInfo pi) {
Connection conn = getConnection();
ArrayList<Board> list = new BoardDao().selectListCount(conn, pi);
close(conn);
return list;
}
}
⑤ board-mapper.xml 파일에 SQL문을 작성했던 것을 처리하여 저장
■ BoardDao.java
package com.kh.board.model.dao;
import static com.kh.common.JDBCTemplate.close;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Properties;
import com.kh.board.model.vo.Board;
import com.kh.common.model.vo.PageInfo;
public class BoardDao {
private Properties prop = new Properties();
// Hashtable은 키와 값을 (Object, Object)의 형태로 저장하는데 비해
// Properties는 (String, String)형태로 저장하는 보다 단순화된 컬렉션 클래스이다.
// 주로 애플리케이션의 환경설정과 관련된 속성을 저장하는데 사용되며
// 데이터를 파일로부터 읽고 쓰는 편리한 기능을 제공한다.
// 그래서 간단한 입출력은 Properties를 활용하면 몇 줄의 코드로 쉽게 해결할 수 있다.
public BoardDao() {
try {
prop.loadFromXML(new FileInputStream(
BoardDao.class.getResource("/sql/board/board-mapper.xml").getPath()));
} catch (IOException e) {
e.printStackTrace();
}
}
⑥ SQL 쿼리문 실행
■ BoarderDao.java
public ArrayList<Board> selectListCount(Connection conn, PageInfo pi) {
// SELECT 문 => ResultSet
ArrayList<Board> list = new ArrayList<>();
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectList");
try {
pstmt = conn.prepareStatement(sql);
/*
* boardLimit = 10
* currentPage = 1 => 1~10 사용자가 요청한 페이지가 1페이지면 1~10페이지를 보여줌
* currentPage = 3 => 21~30
*
* 시작값 = (currentPage -1) * boardLimit + 1
* 끝값 = 시작값 + boardLimit - 1
*/
int startRow = (pi.getCurrentPage() - 1) * pi.getBoardLimit() + 1;
int endRow = startRow + pi.getBoardLimit() - 1;
pstmt.setInt(1, startRow);
pstmt.setInt(2, endRow);
rset = pstmt.executeQuery();
while(rset.next()) {
Board b = new Board.Builder().boardNo(rset.getInt("BOARD_NO"))
.categoryName(rset.getString("CATEGORY_NAME"))
.boardTitle(rset.getString("BOARD_TITLE"))
.boardWriter(rset.getString("USER_ID"))
.count(rset.getInt("COUNT"))
.createDate(rset.getDate("CREATE_DATE"))
.Bulid(); // build 객체 반환
list.add(b); // 생성한 Board 객체 b를 list에 추가
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return list;
}
⑦ 사용자에게 보여질 화면 디자인
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="com.kh.common.model.vo.PageInfo, java.util.ArrayList, com.kh.board.model.vo.Board" %>
<%
PageInfo pi = (PageInfo) request.getAttribute("pi"); // BoardListController 에서 pi를 받아오고,
ArrayList<Board> list = (ArrayList<Board>) request.getAttribute("list"); // BoardListController에서 Board객체 자료형인 list 받아옴
int startPage = pi.getStartPage();
int endPage = pi.getEndPage();
int currentPage = pi.getCurrentPage(); // 사용자가 요청한 페이지 정보
int maxPage = pi.getMaxPage();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<%@ include file="../common/menubar.jsp" %>
<div class="outer">
<br>
<h2 align="center">일반게시판</h2>
<br>
<!-- 로그인한 회원만 글작성 버튼이 보이게 조정 -->
<% if(loginUser != null){ %>
<div align="right" style="width:850px;">
<a href="<%=contextPath %>/insert.bo" class="btn btn-secondary">글작성</a>
<br>
<br>
<% } %>
<table align="center" class="list-area">
<thead>
<tr>
<th width="70">글번호</th>
<th width="70">카테고리</th>
<th width="300">제목</th>
<th width="100">작성자</th>
<th width="50">조회수</th>
<th width="100">작성일</th>
</tr>
</thead>
<tbody>
<% if(list.isEmpty()) { %>
<tr>
<td colspan="6">조회된 리스트가 없습니다..</td>
</tr>
<% }else { %>
<% for(Board b : list) { %>
<tr>
<td><%= b.getBoardNo() %></td>
<td><%= b.getCategoryName() %></td>
<td><%= b.getBoardTitle() %></td>
<td><%= b.getBoardWriter() %></td>
<td><%= b.getCount() %></td>
<td><%= b.getCreateDate() %></td>
</tr>
<% } %>
<% } %>
</tbody>
</table>
<br><br>
<!-- 페이징바 -->
<div align="center" class="paging-area">
<% if(currentPage != 1){ %>
<button onclick="location.href= '<%= contextPath %>/list.bo?currentPage=<%=currentPage -1 %>'"><</button>
<% } %>
<% for(int p = startPage; p <= endPage; p++) { %>
<% if(p != currentPage) { %>
<button onclick="location.href= '<%= contextPath %>/list.bo?currentPage=<%=p%>'"><%= p %></button>
<!-- 절대경로로 보내고 있음 -->
<% } else { %>
<button disabled><%= p %></button>
<% } %>
<% } %>
<% if(currentPage != maxPage) { %>
<button onclick="location.href= '<%= contextPath %>/list.bo?currentPage=<%=currentPage + 1 %>'">></button>
<% } %>
</div>
</div>
</body>
</html>
'Server > JSP과 Servlet' 카테고리의 다른 글
[JSP] 5-3. 게시판 상세 페이지 수정, 삭제 (0) | 2023.12.12 |
---|---|
[JSP] 5-2. 게시판 상세 페이지 만들기(+첨부파일 업로드) (0) | 2023.12.11 |
[JSP] 4-3. 마이페이지 만들기(회원정보 변경, 회원탈퇴, 비밀번호 변경) (0) | 2023.12.07 |
[JSP] 4-2. 로그아웃, 회원가입 페이지 만들기 (0) | 2023.12.07 |
[JSP] 4-1. DB 이용하여 로그인 기능 만들기 (0) | 2023.12.06 |