글 읽기와 조회수 증가 구현
list.do에 있는 목록 중 하나를 클릭 시 해당 글을 상세 보기 하는 기능을 만듭니다.
이때 글을 클릭해서 볼 때마다 조회수도 1 증가시킵니다.
조회수 증가라함은 거창하게 보일 수도 있지만 사실 UPDATE문 하나만 사용하면 됩니다. 😏
view를 위한 SELECT문과 조회수 증가를 위한 UPDATE 문 각각 한 개씩 필요하게 됩니다.
Controller
case "/view.do":
command = new ViewCommand();
command.execute(request, response);
viewPage = "view.jsp";
break;
ViewCommand
package com.command;
import java.sql.SQLException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.beans.BookDAO;
import com.beans.BookDTO;
public class ViewCommand implements Command {
@Override
public void execute(HttpServletRequest request, HttpServletResponse response) {
List<BookDTO> list = null;
int uid = Integer.parseInt(request.getParameter("uid"));
try {
list = new BookDAO().readByUid(uid);
request.setAttribute("list", list);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
view.do?uid 형태를 통해서 GET 방식으로 uid 값을 받아왔었습니다.
따라서 해당 uid 값을 사용하여 원하는 글을 SELECT 할 것입니다.
메서드의 이름은 selectByUid가 아닌 readByUid로 하였는데, 이는 readByUid에는 조회수 증가도 함께 구현할 것이어서 이름을 read로 지었습니다.
추후 UPDATE 기능 구현 시 selectByUid() 메서드를 따로 만들어서 사용할 것입니다.
BookDAO().readByUid()
public List<BookDTO> readByUid(int uid) throws SQLException {
List<BookDTO> list = null;
try {
conn.setAutoCommit(false);
pstmt = conn.prepareStatement(D.SQL_BOOK_INC_VIEWCNT);
pstmt.setInt(1, uid);
pstmt.executeUpdate();
pstmt.close();
pstmt = conn.prepareStatement(D.SQL_BOOK_SELECT_BY_UID);
pstmt.setInt(1, uid);
rs = pstmt.executeQuery();
list = buildList(rs);
conn.commit();
} catch (SQLException e) {
conn.rollback();
throw e;
} finally {
close();
}
return list;
}
만약 글을 읽으려 할 때 모종의 이유로 인해 불러와지지 않는 경우 조회수 증가도 되면 안 될 것입니다.
따라서 DCL인 commit을 사용해 글이 올바르게 불러와질 때에만 조회수가 증가할 수 있도록 합니다.
원래 JDBC 사용 시 autoCommit이 true로 되어있기 때문에 setAutoCommit() 메서드를 사용해 false로 설정해줍니다.
조회수 증가 쿼리를 실행한 뒤, 사용한 pstmt 인스턴스를 close() 합니다. pstmt를 이후에 한 번 더 사용해야 하므로 close()는 필수입니다.
이후 글을 불러오기 위한 쿼리문을 실행합니다. 해당 쿼리로 불러온 값을 ResultSet에 담은 뒤, buildList() 메서드의 인자로 넣어 BookDTO 타입의 list로 저장합니다.
autoCommit을 false로 설정하였기 때문에 위 작업이 모두 완료된다면 commit()을 해주어야 합니다. 만약 중간에 문제가 생긴다면 SQLException이 발생할 것이기 때문에 catch문 안에 rollback() 메서드를 작성해주었습니다.
Query문
public static final String SQL_BOOK_SELECT_BY_UID =
"SELECT bk_uid uid, bk_title title, bk_summary summary, bk_price price,"
+ "bk_viewcnt viewcnt, bk_regdate regdate " + "FROM book WHERE bk_uid = ?";
public static final String SQL_BOOK_INC_VIEWCNT =
"UPDATE book SET bk_viewcnt = bk_viewcnt + 1 WHERE bk_uid = ?";
SQL_BOOK_BY_UID에 저장된 쿼리문을 통해 받은 uid 값과 일치하는 데이터 하나를 SELECT 합니다.
FROM 앞에는 띄어쓰기가 꼭 있어야 하므로 쿼리문 작성 시 주의해야 합니다.
SQL_BOOK_INC_VIEWCNT에 저장된 쿼리문을 통해 받은 uid 값과 일치하는 데이터의 bk_viewcnt 값을 기존 값에서 1 증가시킵니다.
view.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="com.beans.*"%>
<%@ page import="java.util.*"%>
<%
List<BookDTO> list = (List<BookDTO>) request.getAttribute("list");
if (list == null || list.size() == 0) {
%>
<script>
alert("해당 정보가 삭제되었거나 존재하지 않습니다.");
history.back();
</script>
<%
return;
}
%>
<%
BookDTO dto = list.get(0);
int uid = dto.getUid();
String title = dto.getTitle();
String summary = dto.getSummary();
int price = dto.getPrice();
int viewCnt = dto.getViewCnt();
String regDate = dto.getRegDateTime();
%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>글 상세 보기</title>
</head>
<body>
<h1><%=title%> 정보</h1>
<hr>
<strong>도서 제목</strong> <%=title%><br>
<strong>도서 가격</strong> <%=price%><br>
<strong>조회수</strong> <%=viewCnt%><br>
<strong>등록일</strong> <%=regDate%><br><br>
<strong>요약</strong><br>
<%=summary%>
<br><hr><br>
<button type="button" onclick="chkDelete(<%=uid%>)">삭제하기</button>
<button type="button" onclick="location.href='list.do'">목록으로</button>
<button type="button" onclick="location.href='update.do?uid=<%=uid%>'">수정하기</button>
<button type="button" onclick="location.href='write.do'">신규등록</button>
<script>
function chkDelete(uid) {
let r = confirm("삭제하시겠습니까?");
if (r) {
location.href = "deleteOk.do?uid=" + uid;
}
}
</script>
</body>
</html>
SELECT 문의 WHERE 절을 통해 한 개의 값 만을 불러왔으니 list의 0번째에 담긴 데이터가 우리가 원하는 데이터가 됩니다. 따라서 list.get(0)을 해주어 BookDTO 타입의 객체를 가져온 뒤 BookDTO dto에 저장하고, get() 메서드를 사용하여 저장된 값들을 변수에 하나하나 저장합니다.
html 부분에서는 저장된 값 들을 출력하기만 하면 됩니다.
view 페이지에서는 4개의 버튼을 만들었는데 각각 삭제하기, 목록으로, 수정하기, 신규등록입니다.
아직 수정 기능은 구현하지 않았지만 update.do?uid 형태로 만들 것이므로 미리 작성해주었습니다.
삭제 기능 같은 경우는 삭제 전 확인창을 한 번 띄울 것이므로 삭제 버튼을 클릭하면 JS의 chkDelete() 함수가 실행되도록 할 것입니다.
Github
https://github.com/MJKim99/JSP_Board_Book.git