[JSP] 게시판_페이징_검색_게시물 띄우기
저번 글에 이어서 . . .
[JSP] 게시판 구성(게시판 조회 LOWNUM)
[JSP] 게시판 구성하기 (feat.시퀀스쿼리) log4j 자동으로 log 파일이 만들어진다. **로그 관련 파일 ** web.xml (복붙) log4j가 어디있다 알려주면서 한글 깨지는 문제 처리 해주는 부분 **HttpE.." d..
practice365.xyz
제법 기능들이 붙어가는 게시판입니다. 써먹을 수 있는 기능들을 하나씩 사용해 볼 때마다 너무 신기하고 이게 이렇게 돌아가는 거라니!! 뿌듯하고 재밌지만 ... 자잘한 에러와 습관적 까먹음으로 인해 오늘도 우당탕탕 힘든 하루였긔 ... 배우면 배울수록 더 모르겠고 제 자신이 바부 같아서 자기애는 바닥 치닫고 있지만 뭐 어쩌겠어요 이왕 할 거 오늘도 최선을 다해 봅니다 아자아자아 할 수 있다~~!!!
* 추가된 기능(2021.12.02)
- 페이징 처리
- 검색 기능
- 게시물 띄우기
- 조회수 증가(+1)
- 내 작성글의 경우 수정/삭제 버튼 생성
-.web.model 패키지
< Paging.java >
- 공통 모듈 붙여넣기
- pagingProc 계산용도
< list.jsp >
- 기존에 사용하던 파일 수정
- 일단 코드 전체를 두고 하나씩 설명을 할 거다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List" %>
<%@ page import="com.icia.common.util.StringUtil" %>
<%@ page import="com.icia.web.util.CookieUtil" %>
<%@ page import="com.icia.web.util.HttpUtil" %>
<%@ page import="com.icia.web.dao.BoardDao" %>
<%@ page import="com.icia.web.model.Board" %>
<%@ page import="com.icia.web.model.Paging" %>
<%@ page import="com.icia.web.model.BoardFileConfig" %>
<%@ page import="org.apache.logging.log4j.LogManager" %>
<%@ page import="org.apache.logging.log4j.Logger" %>
<%
//로그
Logger logger = LogManager.getLogger("/board/list.jsp");
HttpUtil.requestLogString(request, logger);
//리스트 페이지의 특징
//1페이지에서 2페이지를 누르면 내 자신을 호출함
//조회항목(1:작성자, 2:제목, 3:내용)
String searchType = HttpUtil.get(request, "searchType", ""); //아무값 없으면""처리
String searchValue = HttpUtil.get(request, "searchValue", "");
//커런트 현재페이지
long curPage = HttpUtil.get(request, "curPage", (long)1); //없으면 1페이지 호출
//게시물 리스트
List<Board> list = null;
//총 게시물 수 //리스트를 갖고 와서 사용
long totalCount = 0;
//페이징 객체 생성
Paging paging = null;
Board search = new Board();
BoardDao boardDao = new BoardDao();
//매개변수가 보드기 때문에 보드에 입력하는거임 먼말?
//
if(!StringUtil.isEmpty(searchType) && !StringUtil.isEmpty(searchValue))
{
if(StringUtil.equals(searchType, "1"))
{
//1은 작성자니까 BbsName에 정보를 입력한다.
//2면 title 3면 content Type으로 결정이 되고
//값은 value로 넣어줌
search.setBbsName(searchValue);
}
else if(StringUtil.equals(searchType, "2"))
{
search.setBbsTitle(searchValue);
}
else if(StringUtil.equals(searchType, "3"))
{
search.setBbsContent(searchValue);
}
}
else
{
searchType = "";
searchValue = "";
}
totalCount = boardDao.boardTotalCount(search);
if(totalCount > 0)
{
//여기가 페이징 처리 추가
//생성자의 매개변수가 6개
paging = new Paging("/board/list.jsp", totalCount, BoardFileConfig.LIST_COUNT,
BoardFileConfig.PAGE_COUNT, curPage, "curPage");
paging.addParam("searchType", searchType); //파라미터를 추가하는 메소드
paging.addParam("searchValue", searchValue); //(이름, 오브젝트)
//주석처리 했던 값의 셋팅
search.setStartRow(paging.getStartRow());
search.setEndRow(paging.getEndRow());
list = boardDao.boardList(search); //넘김
}
%>
<!DOCTYPE html>
<html>
<head>
<%@ include file="/include/head.jsp" %>
<script>
$(document).ready(function(){
$("#btnWrite").on("click", function(){//글쓰기 버튼만 구현
document.bbsForm.bbsSeq.value = "";
document.bbsForm.action = "/board/write.jsp";
document.bbsForm.submit(); //아래의 값을 가지고 넘어간다.
});
$("#btnSearch").on("click", function(){
document.bbsForm.bbsSeq.value = "";
document.bbsForm.searchType.value = $("#_searchType").val();
//오른쪽이 갖고 있는 값을 넣어 준다.
document.bbsForm.searchValue.value = $("#_searchValue").val();
document.bbsForm.curPage.value = "1";
//검색을 해도 무조건 첫번째 페이지로 가야 됨
document.bbsForm.action = "/board/list.jsp";
document.bbsForm.submit();
});
});
function fn_list(curPage)
{
document.bbsForm.bbsSeq.value = "";
document.bbsForm.curPage.value = curPage;
document.bbsForm.action = "/board/list.jsp";
document.bbsForm.submit();
}
function fn_view(bbsSeq)
{
document.bbsForm.bbsSeq.value = bbsSeq;
document.bbsForm.action = "/board/view.jsp";
document.bbsForm.submit();
}
</script>
</head>
<body>
<%@ include file="/include/navigation.jsp" %>
<div class="container">
<div class="d-flex">
<div style="width:50%;">
<h2>게시판</h2>
</div>
<div class="ml-auto input-group" style="width:50%;">
<select name="_searchType" id="_searchType" class="custom-select" style="width:auto;">
<option value="">조회 항목</option>
<option value="1" <% if(StringUtil.equals(searchType, "1")){%>selected<%} %>>작성자</option>
<option value="2" <% if(StringUtil.equals(searchType, "2")){%>selected<%} %>>제목</option>
<option value="3" <% if(StringUtil.equals(searchType, "3")){%>selected<%} %>>내용</option>
</select>
<input type="text" name="_searchValue" id="_searchValue" value="<%=searchValue%>" class="form-control mx-1" maxlength="20" style="width:auto;ime-mode:active;" placeholder="조회값을 입력하세요." />
<button type="button" id="btnSearch" class="btn btn-secondary mb-3 mx-1">조회</button>
</div>
</div>
<table class="table table-hover">
<thead>
<tr style="background-color: #dee2e6;">
<th scope="col" class="text-center" style="width:10%">번호</th>
<th scope="col" class="text-center" style="width:55%">제목</th>
<th scope="col" class="text-center" style="width:10%">작성자</th>
<th scope="col" class="text-center" style="width:15%">날짜</th>
<th scope="col" class="text-center" style="width:10%">조회수</th>
</tr>
</thead>
<tbody>
<%
//0이 아니고 사이즈가 더 커
if(list != null && list.size() > 0)
{
for(int i=0; i < list.size(); i++)
{
//하나의 레코드를 갖고 오면 보드 객체에 담아야한다.
Board board = list.get(i); //알아서 하나씩 담아
%>
<tr>
<td class="text-center"><%=board.getBbsSeq()%></td>
<td><a href="javascript:void(0)" onclick="fn_view(<%=board.getBbsSeq()%>)"><%=board.getBbsTitle()%></a></td>
<td class="text-center"><%=board.getBbsName()%></td>
<td class="text-center"><%=board.getRegDate()%></td>
<td class="text-center"><%=board.getBbsReadCnt()%></td>
</tr>
<%
}
}
%>
</tbody>
<tfoot>
<tr>
<td colspan="5"></td>
</tr>
</tfoot>
</table>
<nav>
<ul class="pagination justify-content-center">
<%
if(paging != null)
{
if(paging.getPrevBlockPage() > 0)
{ //프리뷰 블럭이 0보다 크면 내용이 있는 거임
%>
<li class="page-item"><a class="page-link" href="javascript:void(0)" onclick="fn_list(<%=paging.getPrevBlockPage()%>)">이전블록</a></li>
<%
}
//현재 페이지와 커런트 페이지가 같으면 스타일시트가 클릭할 수 없게끔 달라진다.
for(long i = paging.getStartPage(); i <= paging.getEndPage(); i++)
{
//5 페이지라면 1부터 5까지만 나온다. 2~5까지 누를 수 있게 만들거임
if(paging.getCurPage() != i)
{ //action, style 날림. 같지 않다면 눌리면 안 됨
%>
<li class="page-item"><a class="page-link" href="javascript:void(0)" onclick="fn_list(<%=i%>)"><%=i%></a></li>
<%
}
else
{
%>
<li class="page-item active"><a class="page-link" href="javascript:void(0)" style="cursor:default;"><%=i%></a></li>
<%
}
}
if(paging.getNextBlockPage() > 0)
{
%>
<li class="page-item"><a class="page-link" href="javascript:void(0)" onclick="fn_list(<%=paging.getNextBlockPage()%>)">다음블록</a></li>
<%
}
}
%>
</ul>
</nav>
<button type="button" id="btnWrite" class="btn btn-secondary mb-3">글쓰기</button>
<form name="bbsForm" id="bbsForm" method="post">
<!-- 딱 필요한 것만 서버에 보내자! -->
<input type="hidden" name="bbsSeq" value="" /> <!-- 항상 물고 다님 -->
<input type="hidden" name="searchType" value="<%=searchType%>" />
<input type="hidden" name="searchValue" value="<%=searchValue%>" /> <!-- 페이지번호 -->
<input type="hidden" name="curPage" value="<%=curPage%>" /> <!-- 하드코딩(수정) -->
</form>
</div>
</body>
</html>
페이징 처리를 위한 객체 생성(공통 모듈 사용)
-.web.model 패키지
< BoardFileConfig.java >
list.java에서 사용하기 위한 인터페이스 구현, 페이징 처리를 하기 위해서이다.
package com.icia.web.model;
public interface BoardFileConfig
{ //인터페이스 상수(상수 선언할 땐 WAS 내리기)
//한 페이지에 노출될 게시물 수
public static final int LIST_COUNT = 2;
//페이징 수
public static final int PAGE_COUNT = 2;
}
<Board.java>
RNUM에 해당하는 시작값과 끝값을 DB에서 불러오기 위해서는 JSP에서 사용하기 위해 게시글에 해당하는 Board.java 파일에서 변수 선언이 필요하다. 마찬가지로 private를 사용하기 위해서 getter/setter 처리를 해 줌
페이징 처리를 위한 준비
62 totalCount 총 게시물에 대한 변수 생성 (Board search)
64 게시물이 하나라도 있다면
68~69 Paging 객체 생성 및 매개변수 6개 등록(페이징 처리를 위한 객체)
71~72 파라미터 추가 메소드 검색을 위한
75~76 BoardDao.java boardList()의 주석을 풀기 위한 세팅값(시작값, 끝값)
78 75~76번에서 세팅한 값을 메소드에 넘긴다.
< BoardDao.java >
- boardList() 메소드
- 어제까지 작성한 부분은 DB연결을 통해 등록된 글들을 화면에 띄우는 부분까지 작성이 되었다.
//게시판 조회 리스트 //리스트의 보드타입으로 넘길거예여
public List<Board> boardList(Board search)
{
List<Board> list = new ArrayList<Board>(); //아래에 다 담음
//리스트가 어레이리스트 보다 상위(상속 받음)
//List는 여러 건이다. 그 한 건 한 건이 Board라는 뜻
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
StringBuilder sql = new StringBuilder();
sql.append("SELECT ");
sql.append(" BBS_SEQ, ");
sql.append(" USER_ID, ");
sql.append(" BBS_NAME, ");
sql.append(" BBS_EMAIL, ");
sql.append(" BBS_PWD, ");
sql.append(" BBS_TITLE, ");
sql.append(" BBS_CONTENT, ");
sql.append(" BBS_READ_CNT, ");
sql.append(" REG_DATE ");
sql.append(" FROM (SELECT ROWNUM AS RNUM, ");
sql.append(" BBS_SEQ, ");
sql.append(" USER_ID, ");
sql.append(" BBS_NAME, ");
sql.append(" BBS_EMAIL, ");
sql.append(" BBS_PWD, ");
sql.append(" BBS_TITLE, ");
sql.append(" BBS_CONTENT, ");
sql.append(" BBS_READ_CNT, ");
sql.append(" REG_DATE ");
sql.append(" FROM(SELECT ");
sql.append(" A.BBS_SEQ AS BBS_SEQ, ");
sql.append(" NVL(B.USER_ID, '') AS USER_ID, ");
sql.append(" NVL(B.USER_NAME, '') AS BBS_NAME, ");
sql.append(" NVL(B.USER_EMAIL, '') AS BBS_EMAIL, ");
sql.append(" NVL(A.BBS_PWD, '') AS BBS_PWD, ");
sql.append(" NVL(A.BBS_TITLE, '') AS BBS_TITLE, ");
sql.append(" NVL(A.BBS_CONTENT, '') AS BBS_CONTENT, ");
sql.append(" NVL(A.BBS_READ_CNT, '') AS BBS_READ_CNT, ");
sql.append(" NVL(TO_CHAR(A.REG_DATE, 'YYYY.MM.DD HH24:MI:SS'), '') AS REG_DATE ");
sql.append(" FROM ");
sql.append(" TBL_BOARD A, TBL_USER B ");
sql.append(" WHERE A.USER_ID = B.USER_ID ");
if(search != null)
{
if(!StringUtil.isEmpty(search.getBbsName()))
{ //서치타입,밸류로 받아도 되긴함. 여기서는 변수에 대한 의미가 없어서 이걸 사용한 것이다.
sql.append(" AND B.USER_NAME LIKE '%' || ? || '%' ");
}
if(!StringUtil.isEmpty(search.getBbsTitle()))
{
sql.append(" AND A.BBS_TITLE LIKE '%' || ? || '%' ");
}
if(!StringUtil.isEmpty(search.getBbsContent()))
{
sql.append(" AND DBMS_LOB.INSTR(A.BBS_CONTENT,?) > 0 ");
}
}
//페이징 기능이 기본적으로 들어오기 때문에 if 안 함
//startRow endRow가 기본적으로 따라옴
sql.append(" ORDER BY A.BBS_SEQ DESC)) ");
sql.append(" WHERE RNUM >= ? ");
sql.append(" AND RNUM <= ? ");
//start, end 0으로 해놔서 안 됐음
try
{ //똑같은 로직과 인수값으로 들어간다.
int idx = 0;
conn = DBManager.getConnection();
pstmt = conn.prepareStatement(sql.toString());
Logger.debug("boardList SQL : " + sql.toString());
Logger.debug("startRow : " + search.getStartRow());
Logger.debug("endRow : " + search.getEndRow());
//1, 2, 3에 따라서 결과를 다르게 넣어줌
if(search != null)
{
if(!StringUtil.isEmpty(search.getBbsName()))
{
pstmt.setString(++idx, search.getBbsName());
}//작성자
if(!StringUtil.isEmpty(search.getBbsTitle()))
{
pstmt.setString(++idx, search.getBbsTitle());
}//타이틀
if(!StringUtil.isEmpty(search.getBbsContent()))
{
pstmt.setString(++idx, search.getBbsContent());
}//글 //반드시 일치해야 됨
//페이징 기능이 기본적으로 들어오기 때문에 if 안 함
pstmt.setLong(++idx, search.getStartRow());
//변수를 이용해서 순서를 정한 이유 순서가 변경돼도 알아서 변경
pstmt.setLong(++idx, search.getEndRow());
//인수에 대입
}
rs = pstmt.executeQuery();
//쿼리 날리는데 객체가 한 건이 아니니까 if대신 while 처리를 해준다.
//rs.next() => i++의 역할 현재의 숫자에서 다음값을 의미한다.
while(rs.next())
{
Board board = new Board();
//보드 객체에 레코드가 다 담김
//return List<Board>
//리스트는 동적이다. 내부적으로는 배열 복사이다.
//어레이리스트는 리스트의 종류 인턴페이스를 상속 (자료구조) 순차적으로 들어감
board.setBbsSeq(rs.getLong("BBS_SEQ")); //받아와서 넣어준다
board.setUserId(StringUtil.nvl(rs.getString("USER_ID")));
board.setBbsName(StringUtil.nvl(rs.getString("BBS_NAME")));
board.setBbsEmail(StringUtil.nvl(rs.getString("BBS_EMAIL")));
board.setBbsPwd(StringUtil.nvl(rs.getString("BBS_PWD")));
board.setBbsTitle(StringUtil.nvl(rs.getString("BBS_TITLE")));
board.setBbsContent(StringUtil.nvl(rs.getString("BBS_CONTENT")));
board.setBbsReadCnt(rs.getInt("BBS_READ_CNT"));
board.setRegDate(StringUtil.nvl(rs.getString("REG_DATE")));
//board에 다 담아줌
list.add(board);
//list.add(++idx, board);
//리스트에 하나씩 레코드를 추가했다
}
}
catch(SQLException e)
{
Logger.error("[BoardDao] BoardList SQLException", e);
}
finally
{
DBManager.close(rs, pstmt, conn);
}
return list;
}
- search.getStartRow(), search.getEndRow()을 사용할 수 있게 되었으니 주석을 제거 했다. 마찬가지로 주석 제거로 인해 시작값과 끝값을 설정할 수 있게 됨(RNUM)
<list.jsp>
- html 태그 부분 수정
<nav>
<ul class="pagination justify-content-center">
<%
if(paging != null)
{
if(paging.getPrevBlockPage() > 0)
{ //이전 페이징 블럭의 끝 페이지 번호를 얻는다. //프리뷰 블럭이 0보다 크면 내용이 있는 거임
%>
<li class="page-item"><a class="page-link" href="javascript:void(0)" onclick="fn_list(<%=paging.getPrevBlockPage()%>)">이전블록</a></li>
<%
}
//현재 페이지와 커런트 페이지가 같으면 스타일시트가 클릭할 수 없게끔 달라진다.
for(long i = paging.getStartPage(); i <= paging.getEndPage(); i++)
{
//5 페이지라면 1부터 5까지만 나온다. 2~5까지 누를 수 있게 만들거임
if(paging.getCurPage() != i)
{ //action, style 날림. 현재 페이지가 i가 아니라면
%>
<li class="page-item"><a class="page-link" href="javascript:void(0)" onclick="fn_list(<%=i%>)"><%=i%></a></li>
<%
}
else
{
%>
<li class="page-item active"><a class="page-link" href="javascript:void(0)" style="cursor:default;"><%=i%></a></li>
<%
}
}
if(paging.getNextBlockPage() > 0)
{
%>
<li class="page-item"><a class="page-link" href="javascript:void(0)" onclick="fn_list(<%=paging.getNextBlockPage()%>)">다음블록</a></li>
<%
}
}
%>
</ul>
</nav>
- 커런트 페이지 + 이전 블록 + 다음 블록을 만들어주었다.
- 코드에는 들어 있지만 onclick에 들어 있는 제이쿼리 함수를 만들지 않아서 아직은 눌렀을 때 반응이 없는 상태이다.
- for문을 이용해 글의 개수에 따라서 페이지 수 조절이 가능하다.
- 내가 위치한 페이지 버튼은 온 클릭이 없으므로 누를 수 없는 상태이다.
지금까지 .. 이전페이지 / 숫자 / 다음페이지 껍데기만 만들어주었다. 여기에 제이쿼리 함수만 추가 된다면 온 클릭으로 선택할 수 있게 된다.
< list.jsp >
이전페이지 / 숫자 / 다음페이지
버튼을 선택 했을 때 이동하는 기능을 위해
제이쿼리문에 fn_list()함수를 만들어 준다.
어떤 커런트 페이지에 이동을 하더라도
list 나 자신의 주소를 가르키고 있어야 한다.
여기까지 했을 때 페이지를 이동할 수 있게 된다!
<form name="bbsForm" id="bbsForm" method="post">
<!-- 딱 필요한 것만 서버에 보내자! -->
<input type="hidden" name="bbsSeq" value="" /> <!-- 항상 물고 다님 -->
<input type="hidden" name="searchType" value="<%=searchType%>" />
<input type="hidden" name="searchValue" value="<%=searchValue%>" /> <!-- 페이지번호 -->
<input type="hidden" name="curPage" value="<%=curPage%>" /> <!-- 하드코딩(수정) -->
</form>
최하단의 폼/히든 부분도 수정해준다. 디자인적으로 보이는 부분은 아니고 정보 전달용으로 100% 백엔드 개발자가 사용하는 부분이다.
검색 조회 버튼
버튼을 눌렀을 때 내가 선택한 타입과 값에 대한 게시물만 보이도록 온클릭 처리를 해주었다. 타입 안에는 타입의 값(1:작성자 2:제목 3:내용)과 밸류값이 들어간다. 검색을 해도 무조건 첫번째 페이지가 뜰 수 있도록 커런트페이지 값은 "1", 값만 추출해서 갖고 오는 것이므로 페이지는 그대로 나 자신을 바라 보고 있어야한다.
여기서부터는 게시글을 선택 했을 때 해당 게시글에 들어가고, 조회수가 올라가게 처리해 줄 예정이다.
< view.jsp > 생성
- HTML 뼈대는 복붙 임포트는 필요한 만큼 복붙해도 되고 작성하면서 ctrl+shift+o를 이용해 추가해도 된다.
- 이미 완성된 파일이라 하단에서 설명할 예정
클릭해서 글을 보기 위해서는 게시물의 정보를 읽어 와야하고, 조회수가 올라가도록 장치를 해야한다. 조회수가 올라 가는 것도 DB에 저장이 되어야 하니까 메소드를 만드는 게 좋음 View.java는 게시물 관련된 파일이니까 BoardDao에서 메소드를 만들어 줘야한다.
<BoardDao.java>
boardSelect() - 게시물 1건 조회
//게시물 조회(1건)
public Board boardSelect(long bbsSeq)
{
Board board = null;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
StringBuilder sql = new StringBuilder();
sql.append("SELECT ");
sql.append(" BBS_SEQ, ");
sql.append(" NVL(B.USER_ID, '') AS USER_ID, ");
sql.append(" NVL(B.USER_NAME, '') AS BBS_NAME, ");
sql.append(" NVL(B.USER_EMAIL, '') AS BBS_EMAIL, ");
sql.append(" NVL(BBS_PWD, '') AS BBS_PWD, ");
sql.append(" NVL(BBS_TITLE, '') AS BBS_TITLE, ");
sql.append(" NVL(BBS_CONTENT, '') AS BBS_CONTENT, ");
sql.append(" NVL(BBS_READ_CNT, 0) AS BBS_READ_CNT, ");
sql.append(" NVL(TO_CHAR(A.REG_DATE, 'YYYY.MM.DD HH24:MI:SS'), '') AS REG_DATE ");
sql.append(" FROM ");
sql.append(" TBL_BOARD A, TBL_USER B ");
sql.append(" WHERE A.BBS_SEQ = ? ");
sql.append(" AND A.USER_ID = B.USER_ID ");
try
{
conn = DBManager.getConnection();
pstmt = conn.prepareStatement(sql.toString());
pstmt.setLong(1, bbsSeq); //매개변수 넣음 //한 건만 읽으니까~
rs = pstmt.executeQuery();
if(rs.next())
{
board = new Board();
board.setBbsSeq(rs.getLong("BBS_SEQ"));
board.setUserId(rs.getString("USER_ID"));
board.setBbsName(rs.getString("BBS_NAME"));
board.setBbsEmail(rs.getString("BBS_EMAIL"));
board.setBbsPwd(rs.getString("BBS_PWD"));
board.setBbsTitle(rs.getString("BBS_TITLE"));
board.setBbsContent(rs.getString("BBS_CONTENT"));
board.setBbsReadCnt(rs.getInt("BBS_READ_CNT"));
board.setRegDate(rs.getString("REG_DATE"));
}
}
catch(SQLException e)
{
Logger.error("[BoardDao] boardSelect SQLException", e);
}
finally
{
DBManager.close(rs, pstmt, conn);
}
return board;
}
- PK인 SEQ를 통해서 조회한다.
- 글에 관련된 모든 정보들을 읽어 온다.
- 그리고 전해 주는 것도 모든 정보들을 주어야 하기 때문에 리턴타입은 객체여야하고 Board 통째로 전해주면 된다!
- 딱 한 개의 글을 보는 거니까 pstmt.setLong(1, bbsSeq);에서 idx 선언할 필요 없이 1로 설정하면 된다.
boardReadCntPlus() - 게시물 선택시 조회수 +1 생성
//게시물 조회시 조회수 증가
public int boardReadCntPlus(long bbsSeq)
{
int count = 0;
Connection conn = null;
PreparedStatement pstmt = null;
StringBuilder sql = new StringBuilder();
sql.append("UPDATE TBL_BOARD ");
sql.append(" SET BBS_READ_CNT = BBS_READ_CNT + 1 ");
sql.append(" WHERE BBS_SEQ = ? ");
try
{
conn = DBManager.getConnection();
pstmt = conn.prepareStatement(sql.toString());
pstmt.setLong(1, bbsSeq);
count = pstmt.executeUpdate();
}
catch(SQLException e)
{
Logger.error("[BoardDao] boardReadCntPlus SQLException", e);
}
finally
{
DBManager.close(pstmt, conn);
}
return count;
}
- 글을 선택하면 조회수가 1 증가 되니까 수정된 정보를 DB에 전해주는 것이다. 때문에 UPDATE절을 사용하고, SET 부분에서 바로 +1을 해주면 된다. WHERE에는 SEQ 값을 주면 빠르게 전달 가능!
- 마찬가지로 글 한 건에 대한 처리이기 때문에 pstmt.setLong(1, bbsSeq); 한 건만 넘겨주면 된다.
< view.jsp > 생성
- 쿠키로 연동하는 건 마지막에 . . .
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.icia.common.util.StringUtil" %>
<%@ page import="com.icia.web.util.HttpUtil" %>
<%@ page import="com.icia.web.util.CookieUtil" %>
<%@ page import="com.icia.web.dao.BoardDao" %>
<%@ page import="com.icia.web.model.Board" %>
<%@ page import="com.icia.web.model.User" %>
<%@ page import="org.apache.logging.log4j.LogManager" %>
<%@ page import="org.apache.logging.log4j.Logger" %>
<%
//로그
Logger logger = LogManager.getLogger("/board/view.jsp");
HttpUtil.requestLogString(request, logger);
//쿠키
String cookieUserId = CookieUtil.getValue(request, "USER_ID");
//갖고 왔다가 다시 돌아가더라도 "bbsForm"은 들고가야 됨
long bbsSeq = HttpUtil.get(request, "bbsSeq", (long)0); // 값이 없다면 0으로 없는 페이지를 보여줌
String searchType = HttpUtil.get(request, "searchType");
String searchValue = HttpUtil.get(request, "searchValue");
long curPage = HttpUtil.get(request, "curPage", (long)1); // 1페이지를 들고 감
BoardDao boardDao = new BoardDao();
Board board = boardDao.boardSelect(bbsSeq);
//게시글을 클릭하고 들어 갔을 때 조회수가 증가 되어야 함
//나간다음에 readCnt가 증가 돼야 됨(업데이트)
if(board != null)
{
//조회수 증가(업데이트)
boardDao.boardReadCntPlus(bbsSeq);
}
%>
<!DOCTYPE html>
<html>
<head>
<%@ include file="/include/head.jsp" %>
<script>
$(function(){
<%
if(board == null)
{ //보드 객체가 없을 때 //검색창에 치고 들어 갔을 때
%>
alert("조회하신 게시물이 존재하지 않습니다.");
document.bbsForm.action = "/board/list.jsp";
document.bbsForm.submit();
<%
}
else
{
%>
<%
}
%>
});
</script>
</head>
<body>
<%
if(board != null)
{
%>
<%@ include file="/include/navigation.jsp" %>
<div class="container">
<h2>게시물 보기</h2>
<div class="row" style="margin-right:0; margin-left:0;">
<table class="table">
<thead>
<tr class="table-active">
<th scope="col" style="width:60%">
<%=HttpUtil.filter(board.getBbsTitle())%><br/>
<%=HttpUtil.filter(board.getBbsName())%>
<a href="mailto:<%=board.getBbsEmail()%>" style="color:#828282;"><%=board.getBbsEmail()%></a>
</th>
<th scope="col" style="width:40%" class="text-right">
조회 : <%=StringUtil.toNumberFormat(board.getBbsReadCnt())%><br/> <!-- 패턴을 빼놓음 -->
<%=board.getRegDate()%>
</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2"><pre><%=StringUtil.replace(HttpUtil.filter(board.getBbsContent()),"\n", "<br />")%></pre></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2"></td>
</tr>
</tfoot>
</table>
</div>
<button type="button" id="btnList" class="btn btn-secondary">리스트</button>
<%
if(StringUtil.equals(cookieUserId, board.getUserId()))
{
%>
<button type="button" id="btnUpdate" class="btn btn-secondary">수정</button>
<button type="button" id="btnDelete" class="btn btn-secondary">삭제</button>
<%
}
%>
<br/>
<br/>
</div>
<%
}
%>
<!-- form은 히든이니까 위에 중괄호 -->
<form name="bbsForm" id="bbsForm" method="post">
<input type="hidden" name="bbsSeq" value="<%=bbsSeq%>" />
<input type="hidden" name="searchType" value="<%=searchType%>" />
<input type="hidden" name="searchValue" value="<%=searchValue%>" />
<input type="hidden" name="curPage" value="<%=curPage%>" />
</form>
</body>
</html>
<form name="bbsForm" id="bbsForm" method="post">
<!-- 딱 필요한 것만 서버에 보내자! -->
<input type="hidden" name="bbsSeq" value="" /> <!-- 항상 물고 다님 -->
<input type="hidden" name="searchType" value="<%=searchType%>" />
<input type="hidden" name="searchValue" value="<%=searchValue%>" /> <!-- 페이지번호 -->
<input type="hidden" name="curPage" value="<%=curPage%>" /> <!-- 하드코딩(수정) -->
</form>
최하단 히든으로 숨겨둔 폼도 수정을 한다.
여기서 쿠키는 왜 필요한가?
내가 작성한 글이라면 수정과 삭제 기능을 추가하기 위해서이다.
그건 내일 배울거임~
19 게시글을 구분하는 것은 bbsSeq 값이다.
값이 없을 땐 0을 들고 가서 없는 페이지를 보여줘야 하기 때문에 기본값이 0이다.
20-21 서치 타입과 서치 벨류가 게시글을 읽어 들이는데 왜 필요할까?
리스트 버튼을 눌렀을 때 글에 이전 값을 갖고 있다가 다시 보여줘야하기 때문!!
22 마찬가지로 커런트 페이지 또한 기본값으로 1페이지를 들고간다!
43 게시물이 없을 때(보드 객체가 없다) 게시판에서는 지워지지만 주소창에 치고 들어오는 경우가 있는데 그 때 경고창을 띄워주고 리스트 페이지로 돌아가도록 하는 문장
보드 객체에 값이 있다면 화면을 뿌려준다.
74 board.getBbsTitle() 제목을 뿌림. 그걸 감싸고 있는 HttpUtil.filter() 특수문자 필터링은 특수문자를 문자 그대로 사용할 수 있도록 필터를 적용한 공통모듈이다.
80 조회수 롱타입 변수를 얻어 문자형으로 출력한다.
87 DB에서는 엔터가 \n으로 들어가고, http는 <br>로 해야 엔터가 쳐진다. 그것을 설정해주기 위해서 StringUtil.replace() 메소드로 감싸준다.
100 쿠키아이디와 게시글의 사용자아이디를 읽어들여서 아이디가 맞는지 확인을 한다. 아이디는 홈페이지 내에 하나만 존재할 수 있으니 아이디가 같다면 [ 수정 / 삭제 ] 버튼을 띄운다.
<%=StringUtil.replace(HttpUtil.filter(board.getBbsContent()), "\n", "<br />") %>
<list.java>
다시 리스트로 와서 게시물 클릭을 위한 제이쿼리 함수 fn_view()를 만들어준다.
<tr>
<td class="text-center"><%=board.getBbsSeq()%></td>
<td><a href="javascript:void(0)" onclick="fn_view(<%=board.getBbsSeq()%>)"><%=board.getBbsTitle()%></a></td>
<td class="text-center"><%=board.getBbsName()%></td>
<td class="text-center"><%=board.getRegDate()%></td>
<td class="text-center"><%=board.getBbsReadCnt()%></td>
</tr>
<a> 태그로 걸어둔 걸 보면 알겠지만 텍스트(타이틀)를 눌렀을 때 실행이 되는 걸 알 수 있다~
$(document).ready(function(){ });
$(function(){ });
오늘의 어이 없는 오류상 : 오타난건데 첫번째 거 지금까지 잘 써놓고 '브라우저에서 지원 안 하나?' 이러고 앉았음 에휴... 오늘도 혼란하다 혼란해 증말... 이런 저런 어이 없는 오류가 많았지만 이제는 브라우저 F12 콘솔과 이클립스 콘솔창을 확인하면서 잡아가고 있다.
솔직히 JSP가 오기 전까지만 해도 뭐야 개발자 별 거 아니네~ 자만하고 있었는데 너무나 별 거이고, 내가 어리석었다. 언제나 자만하고 오만방자한 것이 내 매력이기도 하지만 이제 전력을 다 해 공부하는 수 밖에 없는 듯하다. 히잉.... 솔직히 봐도 봐도 휘발성 지식이라 다 날아가곤 하지만 칼을 뽑았으면 무라도 썰어야지 어디 함 해보자!!