공부한것들을 정리하는 블로그 입니다.
7-4. 글 조회수 카운트, session을 이용한 조회수 중복 방지 처리(jsp) : BoardDAO.java, getBoard.jsp 본문
7-4. 글 조회수 카운트, session을 이용한 조회수 중복 방지 처리(jsp) : BoardDAO.java, getBoard.jsp
호 두 2019. 6. 28. 11:05
게시판 조회수 중복 방지를 위해서는 쿠키, 세션, ip 3가지 방법이 있습니다.
실제 업무에서는 대개 쿠키를 이용하지만,
현재 제 게시물 중 실습7-* 들은 MVC Model1을 기반으로 대부분 session 을 이용하여 단순히 값을 넘기는 정도로만 처리하고 있습니다.
(자세한 설명은 실습8 이후에서 session을 대체할 수 있는 방법들을 소개하면서 진행하려 생각중입니다.)
따라서 이번 실습도 session을 이용한 조회수 중복 방지 처리(jsp)로 진행하도록 하겠습니다.
BoardDAO.java
- 글 조회수 카운트를 위한 updateBoardCnt() 메서드가 최하단에 추가되었습니다.
package com.springbook.biz.board.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.springbook.biz.board.BoardVO;
import com.springbook.biz.common.JDBCUtil;
import hello.printSimpleName;
@Repository("boardDAO")
public class BoardDAO {
private Connection conn = null;
private PreparedStatement stmt = null;
private ResultSet rs = null;
private final String BOARD_INSERT = "insert into board(seq, title, writer, content, regdate) values((select nvl(max(seq),0)+1 from board),?,?,?,sysdate)";
private final String BOARD_UPDATE = "update board set title=?, content=? where seq=?";
private final String BOARD_DELETE = "delete board where seq=?";
private final String BOARD_GET = "select * from board where seq=?";
// private final String BOARD_LIST = "select * from board order by seq desc";
private final String BOARD_LIST = "select * from board order by seq desc";
private final String BOARD_LIST_T = "select * from board where title like '%'||?||'%' order by seq desc";
private final String BOARD_LIST_C = "select * from board where content like '%'||?||'%' order by seq desc";
private final String BOARD_UPDAT_CNT = "update board set cnt=cnt+1 where seq=?";
printSimpleName p = new printSimpleName();
public void insertBoard(BoardVO vo) {
System.out.println("===> JDBC로 insertBoard() 기능 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_INSERT);
stmt.setString(1, vo.getTitle());
stmt.setString(2, vo.getWriter());
stmt.setString(3, vo.getContent());
stmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
}
public void updateBoard(BoardVO vo) {
System.out.println("===> JDBC로 updateBoard() 기능 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_UPDATE);
stmt.setString(1, vo.getTitle());
stmt.setString(2, vo.getContent());
stmt.setInt(3, vo.getSeq());
stmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
}
public void deleteBoard(BoardVO vo) {
System.out.println("===> JDBC로 deleteBoard() 기능 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_DELETE);
stmt.setInt(1, vo.getSeq());
stmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
}
public BoardVO getBoard(BoardVO vo) {
System.out.println("===> JDBC로 getBoard() 기능 처리");
BoardVO board = null;
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_GET);
stmt.setInt(1, vo.getSeq());
rs = stmt.executeQuery();
if(rs.next()) {
board = new BoardVO();
board.setSeq(rs.getInt("SEQ"));
board.setTitle(rs.getString("TITLE"));
board.setWriter(rs.getString("WRITER"));
board.setContent(rs.getString("CONTENT"));
board.setRegDate(rs.getDate("REGDATE"));
board.setCnt(rs.getInt("CNT"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
return board;
}
// public List<BoardVO> getBoardList(BoardVO vo) {
// System.out.println("===> JDBC로 getBoardList() 기능 처리");
// List<BoardVO> boardList = new ArrayList<BoardVO>();
// try {
// conn = JDBCUtil.getConnection();
// stmt = conn.prepareStatement(BOARD_LIST);
// rs = stmt.executeQuery();
// while(rs.next()) {
// BoardVO board = new BoardVO();
// board.setSeq(rs.getInt("SEQ"));
// board.setTitle(rs.getString("TITLE"));
// board.setWriter(rs.getString("WRITER"));
// board.setContent(rs.getString("CONTENT"));
// board.setRegDate(rs.getDate("REGDATE"));
// board.setCnt(rs.getInt("CNT"));
// boardList.add(board);
// }
// } catch (Exception e) {
// e.printStackTrace();
// } finally {
// JDBCUtil.close(stmt, conn);
// }
// return boardList;
// }
// public List<BoardVO> getBoardListFromSearch(BoardVO vo) {
public List<BoardVO> getBoardList(BoardVO vo) {
System.out.println("===> JDBC로 getBoardList() 기능 처리");
List<BoardVO> boardList = new ArrayList<BoardVO>();
String flagBoardListFromSearch = "N";
try {
conn = JDBCUtil.getConnection();
if (vo.getSearchCondition() != null && vo.getSearchKeyword() != null) {
flagBoardListFromSearch = "Y";
}
if("Y".equals(flagBoardListFromSearch) && vo.getSearchCondition().equals("TITLE")) {
stmt = conn.prepareStatement(BOARD_LIST_T);
stmt.setString(1, vo.getSearchKeyword());
} else if("Y".equals(flagBoardListFromSearch) && vo.getSearchCondition().equals("CONTENT") ) {
stmt = conn.prepareStatement(BOARD_LIST_C);
stmt.setString(1, vo.getSearchKeyword());
} else {
stmt = conn.prepareStatement(BOARD_LIST);
}
rs = stmt.executeQuery();
while(rs.next()) {
BoardVO board = new BoardVO();
board.setSeq(rs.getInt("SEQ"));
board.setTitle(rs.getString("TITLE"));
board.setWriter(rs.getString("WRITER"));
board.setContent(rs.getString("CONTENT"));
board.setRegDate(rs.getDate("REGDATE"));
board.setCnt(rs.getInt("CNT"));
boardList.add(board);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
return boardList;
}
public void updateBoardCnt(BoardVO vo) {
System.out.println("===> JDBC로 updateBoardCnt() 기능 처리");
try {
conn = JDBCUtil.getConnection();
stmt = conn.prepareStatement(BOARD_UPDAT_CNT);
stmt.setInt(1, vo.getSeq());
stmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.close(stmt, conn);
}
}
}
getBoard.jsp
- 글 상세보기 페이지 상단에 있는 페이지 디렉터에 조회수 중복 방지를 위한 로직이 추가되었습니다.
- session을 이용하여 간단히 처리하였고, session = 글 고유번호(seq) + 쿠키(cookies) 로 구성되어 있습니다.
- 처음 조회수를 올린 후 해당 session값은 session ex에 저장됩니다. 이 session ex가 중복 카운트를 방지하기 위한 validation check 비교문에 이용됩니다.
- 39라인에 cnt +1 해준 이유는 가시적으로 사용자 편의성을 위해서입니다. (view에서 볼때 조회수 1이 선추가 된 모습을 보여주기 위해) 해당로직이 없다면 목록을 클릭해도 조회수는 그대로고 f5로 새로고침 또는 글목록으로 나가야 비로수 조회수가 올라간 모습을 볼 수 있을 것입니다. 하단에서 boardDAO.getBoard()를 재실행시키거나 ajax를 이용하여 동적으로 처리하면 해결 가능하지만 전자는 자원이 너무 비효율적으로 소모되고 후자는 다음번 실습때 진행하도록 하겠습니다.
( * 참고 : A.equals(B) 함수를 쓸때는 A부분에 not null 값이 오도록 하자. 왜냐하면 equals()도 함수이므로 A위치에 null이 오면 NullPointerException 에러가 납니다. )
<%@ page import="com.springbook.biz.board.impl.BoardDAO" %>
<%@ page import="com.springbook.biz.board.BoardVO" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
// 저장된 쿠키 불러오기
Cookie[] cookieFromRequest = request.getCookies();
String cookieValue = null;
for(int i = 0 ; i<cookieFromRequest.length; i++) {
// 요청정보로부터 쿠키를 가져온다.
cookieValue = cookieFromRequest[0].getValue(); // 테스트라서 추가 데이터나 보안사항은 고려하지 않으므로 1번째 쿠키만 가져옴
}
// 글 목록 -> 글 상세 : 글번호(seq)
String seq = request.getParameter("seq");
// 쿠키 세션 입력
if (session.getAttribute(seq+":cookie") == null) {
session.setAttribute(seq+":cookie", seq + ":" + cookieValue);
} else {
session.setAttribute(seq+":cookie ex", session.getAttribute(seq+":cookie"));
if (!session.getAttribute(seq+":cookie").equals(seq + ":" + cookieValue)) {
session.setAttribute(seq+":cookie", seq + ":" + cookieValue);
}
}
BoardVO vo = new BoardVO();
vo.setSeq(Integer.parseInt(seq));
BoardDAO boardDAO = new BoardDAO();
// 글 상세 조회
BoardVO board = boardDAO.getBoard(vo);
// 조회수 카운트
if (!session.getAttribute(seq+":cookie").equals(session.getAttribute(seq+":cookie ex"))) {
boardDAO.updateBoardCnt(vo);
// 가시적으로 조회수 1 추가해줌
board.setCnt(board.getCnt() + 1);
}
//System.out.println("중복방지 111 = " + session.getAttribute(seq+":cookie") );
//System.out.println("중복방지 222 = " + session.getAttribute(seq+":cookie ex") );
//System.out.println("중복방지 333 = " + session.toString() );
//for(int i = 0; i < session.getValueNames().length; i++){
// System.out.println("중복방지 444 = " + session.getValueNames()[i].toString() );
//}
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>글 상세</title>
</head>
<body>
<center>
<h1>글 상세</h1>
<a href="logout_proc.jsp">log out</a>
<hr>
<form action="updateBoard_proc.jsp" method="post">
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td bgcolor="orange" width="70">제목</td>
<td align="left">
<input name="title" type="text" value="<%= board.getTitle() %>">
</td>
</tr>
<tr>
<td bgcolor="orange">작성자</td>
<td align="left"><%= board.getWriter() %></td>
</tr>
<tr>
<td bgcolor="orange">내용</td>
<td align="left">
<textarea name="content" cols="40" rows="10"><%= board.getContent() %></textarea>
</td>
</tr>
<tr>
<td bgcolor="orange">등록일</td>
<td align="left"><%= board.getRegDate() %></td>
</tr>
<tr>
<td bgcolor="orange">조회수</td>
<td align="left"><%= board.getCnt() %></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="글 수정" />
</td>
</tr>
</table>
<input name="seq" type="hidden" value="<%= board.getSeq() %>" />
</form>
<hr>
<a href="insertBoard.jsp">글 등록</a>
<a href="deleteBoard_proc.jsp?seq=<%= board.getSeq() %>">글 삭제</a>
<a href="getBoardList.jsp">글 목록</a>
</center>
</body>
</html>
실습화면 :
'(2019) 사이드 프로젝트 > BoardWeb(게시판-MVC1,MVC2,스프링MVC)' 카테고리의 다른 글
7-8. 글 등록, 수정, 삭제 구현 : insertBoard.jsp, insertBoard_proc.jsp, updateBoard_proc.jsp, deleteBoard_proc.jsp (0) | 2019.07.12 |
---|---|
7-7. 실습 중간점검 , 프로젝트 별첨 (0) | 2019.07.05 |
7-6. 페이징처리(paging, pagination) : BoardDAO.java, BoardList2.jsp, PagingVO.java (2) | 2019.07.04 |
7-5. 실습 중간점검 , 프로젝트 별첨 (0) | 2019.06.28 |
7-3. 글 상세 조회 : getBoard.jsp (0) | 2019.06.24 |
7-2. 글 목록 조회, 글 검색 기능 : getBoardList.jsp, BoardDAO.java (0) | 2019.06.24 |
7-1. 로그인, 로그아웃, 로그인 검증 및 실패 후처리 : login.jsp, login_proc.jsp, logout_proc.jsp (3) | 2019.06.24 |
7. MVC Model1 아키텍처로 게시판 개발 : web.xml, representation-layer.xml, index.jsp (0) | 2019.06.20 |