본문 바로가기

개발일지/JSP

[JSP] 게시판 구성(게시판 조회 LOWNUM)

 

[JSP] 게시판 구성하기 (feat.시퀀스쿼리)

  log4j 자동으로 log 파일이 만들어진다. **로그 관련 파일 ** web.xml (복붙) log4j가 어디있다 알려주면서 한글 깨지는 문제 처리 해주는 부분 **HttpE.." data-og-host="practice365.xyz" d.." data-..

practice365.xyz

 

기존에 하드 코딩으로 게시판을 띄우는 것과 달리 오늘은 DB에 입력 돼 있는 글을 게시판에 띄울 수 있게 연동을 해주었습니다. 새로운 쿼리문과 ArrayList에 대한 개념도 배웠고 여전히 어렵슴다!!! 그래도 아자아자 화이팅팅!!!

 

 


 

SELECT 
        BBS_SEQ,
        USER_ID,
        BBS_NAME,
        BBS_EMAIL,
        BBS_PWD,
        BBS_TITLE,
        BBS_CONTENT,
        BBS_READ_CNT,
        REG_DATE
FROM (SELECT  ROWNUM AS RNUM,
        BBS_SEQ,
        USER_ID,
        BBS_NAME,
        BBS_EMAIL,
        BBS_PWD,
        BBS_TITLE,
        BBS_CONTENT,
        BBS_READ_CNT,
        REG_DATE
FROM(SELECT
        A.BBS_SEQ AS BBS_SEQ,
        NVL(B.USER_ID, '') AS USER_ID,
        NVL(B.USER_NAME, '') AS BBS_NAME,
        NVL(B.USER_EMAIL, '') AS BBS_EMAIL,
        NVL(A.BBS_PWD, '') AS BBS_PWD,
        NVL(A.BBS_TITLE, '') AS BBS_TITLE,
        NVL(A.BBS_CONTENT, '') AS BBS_CONTENT,
        NVL(A.BBS_READ_CNT, '') AS BBS_READ_CNT,
        NVL(TO_CHAR(A.REG_DATE, 'YYYY.MM.DD HH24:MI:SS'), '') AS REG_DATE
    FROM
        TBL_BOARD A, TBL_USER B
        WHERE A.USER_ID = B.USER_ID
          AND B.USER_NAME LIKE '%트%'
          AND A.BBS_TITLE LIKE '%a%'
          AND DBMS_LOB.INSTR(A.BBS_CONTENT, 'a') > 0
   ORDER BY A.BBS_SEQ DESC))
   WHERE RNUM >= 1
     AND RNUM <= 5;
-- 페이징이란? 현재 페이지 몇 번부터 다음 페이지 몇 번까지 . . .
-- 여기서는 1페이지에 5개씩 보여주는 것을 의미한다.
-- INLINE VIEW 프롬절에 서브쿼리 들어간 거


--전체 리스트 건수 조회
SELECT
        COUNT(A.BBS_SEQ) AS TOTAL_COUNT --별표하면 풀스캔하니까 SEQ만
    FROM
        TBL_BOARD A, TBL_USER B
    WHERE A.USER_ID = B.USER_ID
      AND B.USER_NAME LIKE '%트%'
      AND A.BBS_TITLE LIKE '%a%'
      AND DBMS_LOB.INSTR(A.BBS_CONTENT, 'a') > 0;
--건수가 없으면 리스트 보여 줄 필요가 없다.

 

게시판에서 글을 보여 줄 때 최근에 등록 된 순으로 보여준다. 어떤 칼럼을 이용해서 보여주느냐 하면 REG_DATE도 있지만 PK에 해당하는 게시글의 번호(BBS_SEQ)를 통해서 보여준다. 가장 큰 값이 가장 최근에 입력된 값이라고 볼 수 있음. 얘를 이용해서 ORDER BY를 해준다. 큰 번호가 최상단에 온다.

 

  ROWNUM  

웹에서 서비스 하는 것 중 SELECT절은 이게 가장 어렵다. 모든 페이지들을 살펴 보면 한 방에 보여주는 곳 없이 게시판에 들어갈 때 페이지 처리를 해줘야한다. WHERE절로 값을 거르고 난 뒤 메모리에 올리고 나서 소팅을 시킴 그리고 나서 ORDER BY 소팅을 하는 거임(ROWID는 그 위치를 가리키니까 값이 유일함). LNUM과 ORDER BY이 같은 절에 있을 때 ORDER BY의 순서가 나중에 오기 때문에 LNUM을 인라인뷰 서브쿼리로 빼줘야한다. 

 

 

함께 보면 좋은 영상

 

 


<Board.java>

RNUM에서 사용할 페이징 시작번호와 끝번호의 인스턴스 변수 정의함. (getter/setter)

 

 

 


< BoardDao.java >

 

기존에 만들었던 BoardDao.java에 새로운 메소드 boardList() 게시판 조회 리스트, boardTotalCount() 게시판 리스트 총건수 조회 기능을 두 개를 추가한다.

 

	//게시판 조회 리스트 //리스트의 보드타입으로 넘길거예여
	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());
			
			//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")));
				
				list.add(board); 
				//list.add(++idx, board); 
				//board에 다 담아줌 //리스트에 배열을 복사
			}
		}
		catch(SQLException e)
		{
			Logger.error("[BoardDao] BoardList SQLException", e);
		}
		finally
		{
			DBManager.close(rs, pstmt, conn);
		}
		return list;
	}
	
	//게시판 리스트 총건수 조회 //건수니까 롱
	public long boardTotalCount(Board search)
	{
		long totalCount = 0;
		
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		StringBuilder sql = new StringBuilder();
		
		sql.append("SELECT ");
		sql.append(" 		COUNT(A.BBS_SEQ) AS TOTAL_COUNT ");
		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 ");
			}
		}
		
		try
		{
			int idx = 0;
			conn = DBManager.getConnection();
			pstmt = conn.prepareStatement(sql.toString());
			
			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());
				}
			}
			
			rs = pstmt.executeQuery();
			
			if(rs.next())
			{
				totalCount = rs.getLong("TOTAL_COUNT");
			}
			
			Logger.debug("totalCount : " + totalCount);
		}
		catch(SQLException e)
		{
			Logger.error("[BoardDao] boardTotalCount SQLException", e);
		}
		finally
		{
			DBManager.close(rs, pstmt, conn);
		}
		return totalCount;
	}

< webapp / board / list.jsp >

 

수정 전 화면에 하드 코딩으로 임의의 글을 화면에 띄웠다.

 

 

 

아래처럼 글쓰기를 했을 때 아래 처럼 내가 쓴 글이 DB에 등록이 되고, 그 글을 화면에 띄울 수 있도록 수정해 줌. 이 과정에서 번호, 제목, 작성자, 날짜, 조회수가 연결 됐다.

 

 

<%@ 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="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;

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)
{
	//여기가 페이징 처리 추가
	
	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(); //아래의 값을 가지고 넘어간다.
	});
});
</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">작성자</option>
            <option value="2">제목</option>
            <option value="3">내용</option>
         </select>
         <input type="text" name="_searchValue" id="_searchValue" value="" 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">

         <li class="page-item active"><a class="page-link" href="javascript:void(0)" style="cursor:default;">1</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="" />
      <input type="hidden" name="searchValue" value="" /> <!-- 페이지번호 -->
      <input type="hidden" name="curPage" value="1" /> <!-- 하드코딩 -->
   </form>
</div>
</body>
</html>