[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>
'개발일지 > JSP' 카테고리의 다른 글
[JSP] 게시판_수정/삭제 ★마지막★ (0) | 2021.12.04 |
---|---|
[JSP] 게시판_페이징_검색_게시물 띄우기 (0) | 2021.12.03 |
[JSP] 게시판 구성하기 (feat.시퀀스쿼리) (0) | 2021.12.01 |
[JSP] 회원 정보 수정을 위한 파일 수정 (0) | 2021.11.29 |
[JSP] 로그인 화면 분석 ④ (쿠키 적용, 로그아웃, nav 자동변경) (0) | 2021.11.29 |