Day53 :) JSP - 게시판

Nux·2021년 11월 19일
0

자바웹개발

목록 보기
56/105
post-thumbnail

DTO, Util

DTO: Board.java

public class Board {

	private int no;
	private String title;
	private User writer;
	private String content;
	private int likeCount;
	private int viewCount;
	private String deleted;
	private Date createdDate;
	
	public Board() {}

	public int getNo() {
		return no;
	}

	public void setNo(int no) {
		this.no = no;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public User getWriter() {
		return writer;
	}
	
	public void setWriter(User writer) {
		this.writer = writer;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public int getLikeCount() {
		return likeCount;
	}

	public void setLikeCount(int likeCount) {
		this.likeCount = likeCount;
	}

	public int getViewCount() {
		return viewCount;
	}

	public void setViewCount(int viewCount) {
		this.viewCount = viewCount;
	}

	public String getDeleted() {
		return deleted;
	}

	public void setDeleted(String deleted) {
		this.deleted = deleted;
	}

	public Date getCreatedDate() {
		return createdDate;
	}

	public void setCreatedDate(Date createdDate) {
		this.createdDate = createdDate;
	}
	
}

Util: ConnectionUtil.java

/* import부분 생략 */
public class ConnectionUtil {

	String url = "jdbc:oracle:thin:@localhost:1521:xe";
	String id = "hr";
	String pw = "zxcv1234";
	
	static {
		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");
		} catch (ClassNotFoundException ex) {
			ex.getStackTrace();
		}
	}

	public static Connection getConnection() throws SQLException{
		return DriverManager.getConnection(url, id, pw);
	}
}

게시글 조회

DAO: BoardDao.java

package com.sample.board.dao;

import static utils.ConnectionUtil.*;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.sample.board.vo.Board;
import com.sample.board.vo.BoardLiker;
import com.sample.board.vo.User;

/**
 * 게시글 등록, 게시글 목록조회, 게시글 수정, 게시글 삭제, 추천 추가, 추천인 조회 기능을 제공하는 클래스다.
 * @author lee_e
 *
 */
public class BoardDao {
	
	private static BoardDao self = new BoardDao();
	private BoardDao() {}
	public static BoardDao getInstance() {
		return self;
	}

	/**
	 * 지정된 게시글 정보를 테이블에 저장한다.
	 * @param board 게시글 정보
	 * @throws SQLException
	 */
	public void insertBoard(Board board) throws SQLException {
		String sql = "insert into tb_comm_boards (board_no, board_title, board_writer_no, board_content) "
				   + "values (comm_board_seq.nextval, ?, ?, ?)";
		
		Connection connection = getConnection();
		PreparedStatement pstmt = connection.prepareStatement(sql);
		pstmt.setString(1, board.getTitle());
		pstmt.setInt(2, board.getWriter().getNo());
		pstmt.setString(3, board.getContent());
		
		pstmt.executeUpdate();
		
		pstmt.close();
		connection.close();
	}
	
	/**
	 * 수정된 정보가 포함된 게시글 정보를 테이블에 반영한다.
	 * @param board
	 * @throws SQLException
	 */
	public void updateBoard(Board board) throws SQLException {
		String sql = "update tb_comm_boards "
				   + "set "
				   + "	board_title = ?, "
				   + "	board_content = ?, "
				   + "	board_like_count = ?, "
				   + "	board_view_count = ?, "
				   + "	board_updated_date = sysdate "
				   + "where board_no = ? ";
		
		Connection connection = getConnection();
		PreparedStatement pstmt = connection.prepareStatement(sql);
		pstmt.setString(1, board.getTitle());
		pstmt.setString(2, board.getContent());
		pstmt.setInt(3, board.getLikeCount());
		pstmt.setInt(4, board.getViewCount());
		pstmt.setInt(5, board.getNo());
		
		pstmt.executeUpdate();
		
		pstmt.close();
		connection.close();
	}
	
	/**
	 * 지정된 번호의 게시글을 삭제처리한다.
	 * @param no 글번호
	 * @throws SQLException
	 */
	public void deleteBoard(int no) throws SQLException {
		String sql = "update tb_comm_boards "
				   + "set "
				   + "	board_deleted = 'Y', "
				   + "	board_deleted_date = sysdate, "
				   + "	board_updated_date = sysdate "
				   + "where board_no = ? ";
		
		Connection connection = getConnection();
		PreparedStatement pstmt = connection.prepareStatement(sql);
		pstmt.setInt(1, no);
		
		pstmt.executeUpdate();
		
		pstmt.close();
		connection.close();
	}
	
	/**
	 * 테이블에 저장된 게시글정보의 갯수를 반환한다.
	 * @return 게시글 정보 갯수
	 * @throws SQLException
	 */
	public int getTotalRecords() throws SQLException {
		String sql = "select count(*) cnt "
				   + "from tb_comm_boards";
		
		int totalRecords = 0;
		
		Connection connection = getConnection();
		PreparedStatement pstmt = connection.prepareStatement(sql);
		ResultSet rs = pstmt.executeQuery();
		rs.next();
		totalRecords = rs.getInt("cnt");
		rs.close();
		pstmt.close();
		connection.close();
		
		return totalRecords;
	}
	
	/**
	 * 지정된 범위에 속하는 게시글 정보를 반환한다.
	 * @param begin 시작 순번번호
	 * @param end 끝 순번번호
	 * @return 게시글 목록
	 * @throws SQLException
	 */
	public List<Board> getBoardList(int begin, int end) throws SQLException {
		String sql = "select board_no, board_title, user_no, user_id, user_name, board_content, "
				   + "       board_view_count, board_like_count, board_deleted, "
				   + "		 board_deleted_date, board_updated_date, board_created_date "
				   + "from (select row_number() over (order by B.board_no desc) rn, "
				   + "             B.board_no, B.board_title, U.user_no, U.user_id, U.user_name, B.board_content,  "
				   + "             B.board_view_count, B.board_like_count, B.board_deleted, "
				   + "			   B.board_deleted_date, B.board_updated_date, B.board_created_date "
				   + "      from tb_comm_boards B, tb_comm_users U "
				   + "      where B.board_writer_no = U.user_no) "
				   + "where rn >= ? and rn <= ? ";
		
		List<Board> boardList = new ArrayList<>();
		
		Connection connection = getConnection();
		PreparedStatement pstmt = connection.prepareStatement(sql);
		pstmt.setInt(1, begin);
		pstmt.setInt(2, end);
		ResultSet rs = pstmt.executeQuery();
		while (rs.next()) {
			Board board = new Board();
			User user = new User();
			
			board.setNo(rs.getInt("board_no"));
			board.setTitle(rs.getString("board_title"));
			board.setContent(rs.getString("board_content"));
			board.setLikeCount(rs.getInt("board_like_count"));
			board.setViewCount(rs.getInt("board_view_count"));
			board.setDeleted(rs.getString("board_deleted"));
			board.setDeletedDate(rs.getDate("board_deleted_date"));
			board.setUpdatedDate(rs.getDate("board_updated_date"));
			board.setCreatedDate(rs.getDate("board_created_date"));
			
			user.setNo(rs.getInt("user_no"));
			user.setId(rs.getString("user_id"));
			user.setName(rs.getString("user_name"));
			
			board.setWriter(user);
			
			boardList.add(board);
		}
		rs.close();
		pstmt.close();
		connection.close();
		
		return boardList;
	}
	
	/**
	 * 지정된 번호의 게시글 정보를 반영한다.
	 * @param no 게시긃ㄴ호
	 * @return 게시글 정보
	 * @throws SQLException
	 */
	public Board getBoardDetail(int no) throws SQLException {
		String sql = "select B.board_no, B.board_title, U.user_no, U.user_id, U.user_name, B.board_content, "
				   + "       B.board_view_count, B.board_like_count, B.board_deleted, "
				   + "		 B.board_deleted_date, B.board_updated_date, B.board_created_date "
				   + "from tb_comm_boards B, tb_comm_users U "
				   + "where B.board_writer_no = U.user_no "
				   + "and B.board_no = ? ";
		
		Board board = null;
		
		Connection connection = getConnection();
		PreparedStatement pstmt = connection.prepareStatement(sql);
		pstmt.setInt(1, no);
		ResultSet rs = pstmt.executeQuery();
		while (rs.next()) {
			board = new Board();
			User user = new User();
			
			board.setNo(rs.getInt("board_no"));
			board.setTitle(rs.getString("board_title"));
			board.setContent(rs.getString("board_content"));
			board.setLikeCount(rs.getInt("board_like_count"));
			board.setViewCount(rs.getInt("board_view_count"));
			board.setDeleted(rs.getString("board_deleted"));
			board.setDeletedDate(rs.getDate("board_deleted_date"));
			board.setUpdatedDate(rs.getDate("board_updated_date"));
			board.setCreatedDate(rs.getDate("board_created_date"));
			
			user.setNo(rs.getInt("user_no"));
			user.setId(rs.getString("user_id"));
			user.setName(rs.getString("user_name"));
			
			board.setWriter(user);
		}
		rs.close();
		pstmt.close();
		connection.close();
		
		return board;
	}
	
	/**
	 * 게시글 추천 정보를 저장한다.
	 * @param boardLiker 게시글 추천정보(게시글번호, 로그인한 사용자번호 포함)
	 * @throws SQLException
	 */
	public void insertBoardLiker(BoardLiker boardLiker) throws SQLException {
		String sql = "insert into tb_comm_board_like_users (board_no, user_no) "
				   + "values (?, ?)";
		
		Connection connection = getConnection();
		PreparedStatement pstmt = connection.prepareStatement(sql);
		pstmt.setInt(1, boardLiker.getBoardNo());
		pstmt.setInt(2, boardLiker.getUserNo());
		
		pstmt.executeUpdate();
		
		pstmt.close();
		connection.close();
	}
	
	/**
	 * 지정된 글번호와 사용자번호로 추천정보를 조회해서 반환한다.
	 * @param boardNo 글번호
	 * @param userNo 사용자번호
	 * @return 추천정보
	 * @throws SQLException
	 */
	public BoardLiker getBoardLiker(int boardNo, int userNo) throws SQLException {
		String sql = "select board_no, user_no "
				   + "from tb_comm_board_like_users "
				   + "where board_no = ? and user_no = ?";
		
		BoardLiker boardLiker = null;
		
		Connection connection = getConnection();
		PreparedStatement pstmt = connection.prepareStatement(sql);
		pstmt.setInt(1, boardNo);
		pstmt.setInt(2, userNo);
		ResultSet rs = pstmt.executeQuery();
		if (rs.next()) {
			boardLiker = new BoardLiker();
			boardLiker.setBoardNo(rs.getInt("board_no"));
			boardLiker.setUserNo(rs.getInt("user_no"));	
		}
		rs.close();
		pstmt.close();
		connection.close();
		
		return boardLiker;
	}
	
	/**
	 * 지정된 번호의 글에 추천한 사용자의 목록을 반환한다.
	 * @param boardNo 글번호
	 * @return 사용자 목록
	 * @throws SQLException
	 */
	public List<User> getLikeUsers(int boardNo) throws SQLException {
		String sql = "select U.user_no, U.user_id, U.user_name "
				   + "from tb_comm_board_like_users L, tb_comm_users U "
				   + "where L.user_no = U.user_no "
				   + "and L.board_no = ? ";
		
		List<User> userList = new ArrayList<>();
		
		Connection connection = getConnection();
		PreparedStatement pstmt = connection.prepareStatement(sql);
		pstmt.setInt(1, boardNo);
		ResultSet rs = pstmt.executeQuery();
		while (rs.next()) {
			User user = new User();
			
			user.setNo(rs.getInt("user_no"));
			user.setId(rs.getString("user_id"));
			user.setName(rs.getString("user_name"));
			
			userList.add(user);
		}
		rs.close();
		pstmt.close();
		connection.close();
		
		return userList;
	}
}

게시글 조회 페이지: detail.jsp

<%@page import="com.sample.board.vo.BoardLiker"%>
<%@page import="java.util.List"%>
<%@page import="com.sample.board.vo.Pagination"%>
<%@page import="com.sample.board.vo.Board"%>
<%@page import="com.sample.board.dao.BoardDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!doctype html>
<html lang="ko">
<head>
	<meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
	<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" >
    <title>커뮤니티 게시판::상세</title>
</head>
<body>
<%
	// include 시킨 navbar의 nav-item 중에서 페이지에 해당하는 nav-item를 active 시키기위해서 "menu"라는 이름으로 페이지이름을 속성으로 저장한다.
	// pageContext에 menu라는 이름으로 설정한 속성값은 navbar.jsp의 6번째 라인에서 조회해서 navbar의 메뉴들 중 하나를 active 시키기 위해서 읽어간다.
	pageContext.setAttribute("menu", "board");
%>
<%@ include file="../common/navbar.jsp" %>
<div class="container">    
	<div class="row mb-3">
		<div class="col">
			<h1 class="fs-3">게시글 상세</h1>
		</div>
	</div>
<%
	// 클라이언트는 게시글 상세 페이지를 조회할 때 "detail.jsp?no=글번호&pageNo=페이지번호" 로 요청한다.(list.jsp의 78번 라인 참조)
	// 요청파라미터에서 글번호와 페이지 번호를 조회한다.
	int no = Integer.parseInt(request.getParameter("no"));
	String pageNo = request.getParameter("pageNo");
	String error = request.getParameter("error");
	
	// 게시글 정보를 제공하는 BoardDao객체를 획득한다.
	BoardDao boardDao = BoardDao.getInstance();	
	
	// 게시글 번호에 해당하는 글 정보를 조회한다.
	Board board = boardDao.getBoardDetail(no);
	
	// 게시글의 조회수를 1 증가시킨다.
	board.setViewCount(board.getViewCount() + 1);
	// 조회수가 1증가된 글정보를 테이블에 반영시킨다.
	boardDao.updateBoard(board);
%>
	<div class="row mb-3">
		<div class="col">
<%
	if ("deny-delete".equals(error)) {
%>
			<div class="alert alert-danger">
				<strong>삭제 실패!!</strong> 자신이 작성한 글이 아닌 경우 삭제할 수 없습니다.
			</div>
<%		
	} else if ("deny-update".equals(error)) {
%>
			<div class="alert alert-danger">
				<strong>수정 실패!!</strong> 자신이 작성한 글이 아닌 경우 수정할 수 없습니다.
			</div>
<%		
	} else if ("deny-like".equals(error)) {
%>
			<div class="alert alert-danger">
				<strong>추천 실패!!</strong> 이미 추천한 글이거나 자신의 글은 추천할 수 없습니다.
			</div>
<%	
	}
%>
			<table class="table">
				<tbody>
					<tr class="d-flex">
						<th class="col-2">번호</th>
						<td class="col-4"><%=board.getNo() %></td>
						<th class="col-2">등록일</th>
						<td class="col-4"><%=board.getCreatedDate() %></td>
					</tr>
					<tr class="d-flex">
						<th class="col-2">제목</th>
						<td class="col-4"><%=board.getTitle() %></td>
						<th class="col-2">작성자</th>
						<td class="col-4"><%=board.getWriter().getName() %></td>
					</tr>
					<tr class="d-flex">
						<th class="col-2">조회수</th>
						<td class="col-4"><%=board.getViewCount() %></td>
						<th class="col-2">추천수</th>
						<td class="col-4">
							<%=board.getLikeCount() %>
<%
	// 이 게시글에 대한 추천이 있는 경우에 버튼을 출력한다.
	if (board.getLikeCount() > 0) {
%> 
							<button type="button" class="btn btn-outline-primary btn-sm" data-bs-toggle="modal" data-bs-target="#board-liker-modal">보기</button>
<%
	}
%>
						</td>
					</tr>
					<tr class="d-flex">
						<th class="col-2">내용</th>
						<td class="col-10"><%=board.getContent() %></td>
					</tr>
				</tbody>				
			</table>
		</div>
	</div>
	<div class="row mb-3">
		<div class="col">
			<div class="d-flex justify-content-between">
				<div>
<%
	if (loginUserInfo != null && loginUserInfo.getNo() == board.getWriter().getNo()) {		// 로그인한 사용자의 사용자번호와 게시글작성자의 사용자번호가 일치하는 경우 버튼이 출력된다.
%>
					<a href="delete.jsp?no=<%=board.getNo() %>&pageNo=<%=pageNo %>" class="btn btn-danger">삭제</a>
<%
	}
%>
<%
	if (loginUserInfo != null && loginUserInfo.getNo() != board.getWriter().getNo()) {		// 로그인한 사용자의 사용자번호와 게시글작성자의 사용자번호가 서로 다른 경우 버튼이 출력된다.
		BoardLiker boardLiker = boardDao.getBoardLiker(board.getNo(), loginUserInfo.getNo());	// 게시글번호와 로그인한 사용자번호로 추천정보를 조회한다.
%>					
					<!-- 
						추천정보가 존재하면 추천버튼을 비활성화한다.	
					 -->
					<a href="like.jsp?no=<%=board.getNo() %>&pageNo=<%=pageNo %>" class="btn btn-success <%=boardLiker != null ? "disabled" : ""  %>">추천</a>
<%
	}
%>
				</div>
				<a href="list.jsp?pageNo=<%=pageNo %>" class="btn btn-primary">목록</a>
			</div>
		</div>
	</div>
</div>
<!-- 
	이 글을 추천한 추천인 정보를 표시하는 모달창
 -->
<%
	List<User> boardLikeUserList = boardDao.getLikeUsers(no);
%>
<div class="modal" tabindex="-1" id="board-liker-modal">
	<div class="modal-dialog">
		<div class="modal-content">
			<!-- <div class="modal-header">
				<h5 class="modal-title">추천 리스트</h5>
				<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
			</div> -->
			<div class="modal-body">
				<div class="card bg-primary">
					<div class="card-header fs-bold text-white">추천인</div>
					<ul class="list-group">
<%
	for (User user : boardLikeUserList) {
%>
						<li class="list-group-item"><%=user.getName() %></li>
<%
	}
%>
					</ul>
				</div>
			</div>
			<div class="modal-footer">
				<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
			</div>
		</div>
	</div>
</div> 

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

list.jsp(게시판)

<%@page import="com.sample.board.vo.Board"%>
<%@page import="java.util.List"%>
<%@page import="com.sample.board.vo.Pagination"%>
<%@page import="com.sample.board.dao.BoardDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!doctype html>
<html lang="ko">
<head>
	<meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
	<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" >
    <title>커뮤니티 게시판::리스트</title>
</head>
<body>
<%
	// include 시킨 navbar의 nav-item 중에서 페이지에 해당하는 nav-item를 active 시키기위해서 "menu"라는 이름으로 페이지이름을 속성으로 저장한다.
	// pageContext에 menu라는 이름으로 설정한 속성값은 navbar.jsp의 6번째 라인에서 조회해서 navbar의 메뉴들 중 하나를 active 시키기 위해서 읽어간다.
	pageContext.setAttribute("menu", "board");
%>
<%@ include file="../common/navbar.jsp" %>
<div class="container">    
	<div class="row mb-3">
		<div class="col">
			<h1 class="fs-3">게시글 목록</h1>
		</div>
	</div>
<%
	// 요청파라미터에서 pageNo값을 조회한다.
	// 요청파라미터에 pageNo값이 존재하지 않으면 Pagination객체에서 1페이지로 설정한다.
	String pageNo = request.getParameter("pageNo");

	// 게시글 정보 관련 기능을 제공하는 BoardDao객체를 획득한다.
	BoardDao boardDao = BoardDao.getInstance();
	
	// 총 데이터 갯수를 조회한다.
	int totalRecords = boardDao.getTotalRecords();
	
	// 페이징 처리 필요한 값을 계산하는 Paginatition객체를 생성한다.
	Pagination pagination = new Pagination(pageNo, totalRecords);
	
	// 현재 페이지번호에 해당하는 게시글 목록을 조회한다.
	List<Board> boardList = boardDao.getBoardList(pagination.getBegin(), pagination.getEnd());
%>
	<div class="row mb-3">
		<div class="col">
			<table class="table">
				<thead>
					<tr class="d-flex">
						<th class="col-1">번호</th>
						<th class="col-5">제목</th>
						<th class="col-2">작성자</th>
						<th class="col-1">추천수</th>
						<th class="col-1">조회수</th>
						<th class="col-2">등록일</th>
					</tr>
				</thead>
				<tbody>
<%
	if (boardList.isEmpty()) {
%>
					<tr>
						<td class="text-center" colspan="6">게시글이 존재하지 않습니다.</td>
					</tr>
<%
	} else {
		for (Board board : boardList) {
%>
					<tr class="d-flex">
						<td class="col-1"><%=board.getNo() %></td>
						<td class="col-5">
<%
			if ("Y".equals(board.getDeleted())) {
%>
							<span><del>삭제된 글입니다.</del></span>
<%
			} else {
%>
							<a href="detail.jsp?no=<%=board.getNo()%>&pageNo=<%=pagination.getPageNo()%>"><%=board.getTitle() %></a>
<%
			}
%>
						</td>
						<td class="col-2"><%=board.getWriter().getName() %></td>
						<td class="col-1"><%=board.getLikeCount() %></td>
						<td class="col-1"><%=board.getViewCount() %></td>
						<td class="col-2"><%=board.getCreatedDate() %></td>
					</tr>
<%
		}
	}
%>
				</tbody>				
			</table>
		</div>
	</div>
	<div class="row mb-3">
		<div class="col-6 offset-3">
			<nav>
				<ul class="pagination justify-content-center">
					<!-- 
						Pagination객체가 제공하는 isExistPrev()는 이전 블록이 존재하는 경우 true를 반환한다.
						Pagination객체가 제공하는 getPrevPage()는 이전 블록의 마지막 페이지값을 반환한다.
					 -->
					<li class="page-item <%=!pagination.isExistPrev() ? "disabled" : "" %>"><a class="page-link" href="list.jsp?pageNo=<%=pagination.getPrevPage()%>" >이전</a></li>
<%
	// Pagination 객체로부터 해당 페이지 블록의 시작 페이지번호와 끝 페이지번호만큼 페이지내비게이션 정보를 표시한다.
	for (int num = pagination.getBeginPage(); num <= pagination.getEndPage(); num++) {
%>					
					<li class="page-item <%=pagination.getPageNo() == num ? "active" : "" %>"><a class="page-link" href="list.jsp?pageNo=<%=num%>"><%=num %></a></li>
<%
	}
%>					
					<!-- 
						Pagination객체가 제공하는 isExistNext()는 다음 블록이 존재하는 경우 true를 반환한다.
						Pagination객체가 제공하는 getNexPage()는 다음 블록의 첫 페이지값을 반환한다.
					 -->
					<li class="page-item <%=!pagination.isExistNext() ? "disabled" :"" %>"><a class="page-link" href="list.jsp?pageNo=<%=pagination.getNextPage()%>" >다음</a></li>
				</ul>
			</nav>
		</div>
		<div class="col-3 text-end">
<%
	// 로그인되지 않은 경우 새 글 버튼이 출력되지않는다.
	if (loginUserInfo != null) { 
%>
			<a href="form.jsp" class="btn btn-outline-primary">새 글</a>
<%
	}
%>
		</div>
	</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

0개의 댓글