개발일지/JSP

[JSP] 게시판_페이징_검색_게시물 띄우기

연습용365 2021. 12. 3. 04:14

저번 글에 이어서 . . .

 

[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())%>&nbsp;&nbsp;&nbsp;
                  <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가 오기 전까지만 해도 뭐야 개발자 별 거 아니네~ 자만하고 있었는데 너무나 별 거이고, 내가 어리석었다. 언제나 자만하고 오만방자한 것이 내 매력이기도 하지만 이제 전력을 다 해 공부하는 수 밖에 없는 듯하다. 히잉.... 솔직히 봐도 봐도 휘발성 지식이라 다 날아가곤 하지만 칼을 뽑았으면 무라도 썰어야지 어디 함 해보자!!