본문 바로가기

SpringFramework-OTOlympic

[스프링 프로젝트] 게시판 - 댓글 조회 + 페이징

이전 글)

 

[스프링 프로젝트] 게시판 - 게시글 삭제

이전 글) [스프링 프로젝트] 게시판 - 게시글 수정 이전 글) [스프링 프로젝트] 게시판 - 조회수 이전 글) [스프링 프로젝트] 게시판 - 게시글 상세보기 이전 글) [스프링 프로젝트] 게시판 - 게시글

dongcu.tistory.com


댓글은 게시글 상세보기를 했을 때 같이 확인할 수 있다.

그렇기 때문에 댓글과 관련된 jsp 파일의 경우, 게시글 상세보기 jsp 파일에 이어서 작성하면 된다.

 

더보기
<div class="banana">
		<div class="board_body">
			<div class="title_box">
				<h2>자유 게시판</h2>
			</div>
			<!-- 제목 -->
			<div id="title_box2">
				<div class="view_title" id="b_title" name="b_title">
					${freedetail.b_title}
				</div>
				<div class="cnt_date_writer">
					<div class="writer" id="b_writer" name="b_writer">
						작성자: ${freedetail.b_writer}
					</div>
					<div class="view_date" id="b_date" name="b_date">
						작성일 : <fmt:formatDate value="${freedetail.b_date}"	pattern="yy-MM-dd HH:mm" />
					</div>
					<div class="view_cnt" id="view_cnt" name="view_cnt">
						조회수: ${freedetail.view_cnt}
					</div>
					<input type="hidden" name="b_num" id="b_num" value="${freedetail.b_num}">
				</div>
			</div>
			<!-- 본문 -->
			<div class="view_content" id="b_content" name="b_content">
				${freedetail.b_content}<br>
			</div>
			<div class="btns">
				<a class="btn_back" href="/free/freelist?searchType=T&keyword=${keyword}&num=${select}">목록</a>
				<!-- 수정 삭제 버튼 나오기 -->
				<c:if test="${freedetail.b_writer == loginUser.userid || loginUser.userid == 'admin'}">
					<a class="btn_modi" href="/free/freemodify?b_num=${freedetail.b_num}">수정</a>
					<form method="post" action="${cp}/free/freedelete" id="deleteForm" name="deleteForm">
						<input type="hidden" value="${freedetail.b_num}" name="b_num">
						<input class="btn_delete" type="submit" value="삭제" onclick="return delete_check();" id="btn_delete">
					</form>
				</c:if>
			</div>
			<!--댓글 달기  -->
			<form action="/free/replywrite" name="replyform" id="replyform" method="post">
				<ul class="reply_textbox">
					<div class="reply_textbox2">
						<div class="reply_wr">
							<label class="reply_writer">댓글 작성자</label>
							<input style="width: 90px; padding: 0 5px;" type="text" value="${loginUser.userid}" id="c_writer" name="c_writer" readonly>
						</div>
						<div>
							<textarea rows="5" cols="50" class="reply_text" style="resize: none;" name="c_contents"></textarea>
							<input type="hidden" value="${freedetail.b_num}" name="b_num" />
						</div>
						<div>
							<button type="button" class="reply_btn" onclick="replyCheck();">댓글 작성</button>
						</div>
					</div>
				</ul>
			</form>
			<!-- 댓글 보기 -->
			<c:forEach items="${reply}" var="free_reply">
				<li class="reply" id="reply${free_reply.c_num}">
					<div class="free_reply_div1 reply${free_reply.c_num}">
						<p>${free_reply.c_writer} · <fmt:formatDate value="${free_reply.c_date}" pattern="yyyy.MM.dd HH:mm:ss" /></p>
						<p>${free_reply.c_contents}</p>
						<div class="free_reply_div2">
							<!-- 댓글 수정 삭제 -->
							<c:if test="${free_reply.c_writer == loginUser.userid || loginUser.userid == 'admin'}">
								<a class="free_reply_modify" id="free_reply_modify${free_reply.c_num}" href="${free_reply.c_num}">수정</a>
								<form method="post" action="${cp}/free/replydelete" id="reply_delete_form" name="reply_delete_form">
									<input type="hidden" value="${free_reply.c_num}" name="c_num">
									<input type="hidden" value="${select}" name="select">
									<input type="hidden" value="${free_reply.b_num}" name="b_num">
									<input class="free_reply_delete" type="submit" value="삭제" onclick="return reply_delete();" id="">
								</form>
							</c:if>
						</div>
					</div>
				</li>
			</c:forEach>
			<!-- 댓글 페이징 -->
			<div class="btns">
				<ul class="pagination" style="display: flex;">
					<c:if test="${page.prev}">
						<li style="margin-right: 5px;">
							[<a href='/free/freedetail?b_num=${freedetail.b_num}&reply_num=${page.startPageNum-1}'>이전</a>]
						</li>
					</c:if>
					<c:forEach begin="${page.startPageNum}" end="${page.endPageNum}" var="num">
						<li style="margin-right: 5px;">
							<c:if test="${select != num}">
								<a href="/free/freedetail?b_num=${freedetail.b_num}&reply_num=${num}">${num}</a>
							</c:if>
							<c:if test="${select == num}">
								<b style="font-weight: 700; color: red; text-decoration: underline;">${num}</b>
							</c:if>
						</li>
					</c:forEach>
					<c:if test="${page.next}">
						<li>
							[<a href="/free/freedetail?b_num=${freedetail.b_num}&reply_num=${page.endPageNum+1}">다음</a>]
						</li>
					</c:if>
				</ul>
			</div>
		</div>
	</div>

 

즉 댓글을 포함한 게시글 상세보기의 최종 jsp 파일은 이렇게 구성되는 것이다.

 

 

댓글을 작성하는 칸은 게시글의 밑에 출력된다.

 

더보기
function replyCheck() {
	let reply_writer = $("#c_writer").val();
	let reply_content = $(".reply_text").val();
	if (reply_writer == "") {
		alert("로그인 후 이용하세요!");
		return false;
	}
	if (reply_content == "") {
		alert("댓글을 입력하세요.");
		return false;
	}
	if (confirm("댓글을 등록하시겠습니까?")) {
		$("#replyform").submit();
	}
}

 

자바스크립트 함수를 통해서 유효성 검사를 진행한다.

로그인을 하지 않았을 시 댓글을 작성할 수 없으며, 댓글 내용을 입력하지 않으면 작성을 완료할 수 없다.

또한 이 모든 것들이 충족되었을 시 confirm을 통해 댓글 작성 여부를 재차 확인한다.

 

댓글 작성과 관련된 코드들을 작성하기에 앞서 댓글과 관련된 변수들이 저장된 ReplyVO를 만든다.

경로는 src/main/java → com.gym → domain이다.

 

더보기
@Data
public class ReplyVO {

	private int c_num;
	private String c_writer;
	private String c_contents;
	private Date c_date;
	private int b_num;
	private String b_title;
	
}

 

ReplyVO를 작성했으면 쿼리문을 작성한다.

Controller, Service, DAO는 따로 만들지 않고 FreeBoard에 그대로 작성하면 된다.

이미 작성되어 있는 댓글을 불러오는 쿼리문이다.

 

더보기
<!-- 댓글 조회 -->
<select id="freeReplyList" resultType="com.gym.domain.ReplyVO">
	select b_num, c_writer, c_contents, c_date, c_num
	from f_comment
	where b_num = #{b_num} order by c_num desc limit #{displayPost}, #{postNum}
</select>

<!-- 댓글 총 개수 -->
<select id="freeReplyCnt" resultType="_int">
	select
	count(*)
	from f_comment
	where b_num = #{b_num}
</select>

 

select문을 통해 검색한 결과는 ReplyVO 타입이기 때문에 resultType을 잘 적어준다.

또한 댓글 페이징을 위해서는 해당 게시글에 작성된 댓글의 총 개수를 세는 쿼리문도 필요하다.

다음으로 FreeBoardDAO와 FreeBoardDAOImpl에 메소드를 적어준다.

 

더보기
// 댓글 조회
public List<ReplyVO> freeReplylist(int b_num, int displayPost, int postNum) throws Exception;

// 댓글 총 개수
public int getReplyCnt(int b_num) throws Exception;

 

더보기
// 댓글 조회
@Override
public List<ReplyVO> freeReplylist(int b_num, int displayPost, int postNum) throws Exception {
	HashMap<String, Integer> data = new HashMap<String, Integer>();
	data.put("b_num", b_num);
	data.put("displayPost", displayPost);
	data.put("postNum", postNum);
    
	return sql.selectList(namespace + ".freeReplyList", data);
}

// 댓글 총 개수
@Override
public int getReplyCnt(int b_num) throws Exception {
	return sql.selectOne(namespace + ".freeReplyCnt", b_num);
}

 

댓글도 페이징을 해야 하기 때문에 출력할 게시물을 나타내는 displayPost와 페이지 번호를 나타내는 postNum을 함께 넘겨준다.

return 값의 매개변수로 여러 개의 값을 넘길 수 없기 때문에 hashmap에 담아서 넘긴다.

댓글을 조회할 때는 하나의 댓글만을 조회하는 것이 아니라 여러 개의 댓글을 불러와야 한다.

그렇기 때문에 selectOne이 아니라 selectList를 사용해서 여러 개의 댓글들을 불러올 수 있고록 한다.

FreeBoardService와 FreeBoardServiceImpl에 다음의 메소드를 작성한다.

 

더보기
// 댓글 조회
public List<ReplyVO> freeReplylist(int b_num, int displayPost, int postNum) throws Exception;

// 댓글 총 개수
public int getReplyCnt(int b_num) throws Exception;

 

더보기
// 댓글 조회
@Override
public List<ReplyVO> freeReplylist(int b_num, int displayPost, int postNum) throws Exception {
	return dao.freeReplylist(b_num, displayPost, postNum);
}

// 댓글 총 개수
@Override
public int getReplyCnt(int b_num) throws Exception {
	return dao.getReplyCnt(b_num);
}

 

FreeBoardService에 작성된 댓글을 불러오는 메소드는 FreeBoardController의 게시글 상세보기 메소드 안에 함께 작성한다.

 

더보기
// 게시물 조회
@GetMapping("/freedetail")
public String getFreeDetail(String keyword, int b_num, Model model, int reply_num, HttpServletRequest req) throws Exception {
	FreeBoardVO vo = service.getFreeDetail(b_num);
	model.addAttribute("freedetail", vo);
	model.addAttribute("keyword", keyword);
    
	String userid = ((UserVO) req.getSession().getAttribute("loginUser")).getUserid();
	if (!userid.equals(vo.getB_writer())) {
		service.freeViewCnt(b_num);
	}
	
	// 댓글 조회
	ReplyPage page = new ReplyPage();
	page.setNum(reply_num);
	page.setCount(service.getReplyCnt(b_num)); // 이거는 b_num에 대한 reply 개수
    
	List<ReplyVO> reply = null;
	reply = service.freeReplylist(b_num, page.getDisplayPost(), page.getPostNum());
	model.addAttribute("page", page);
	model.addAttribute("reply", reply);
	model.addAttribute("select", reply_num);
    
	return "/board/freeboard_detail";
}

 

우선 댓글 페이징을 위한 ReplyPage를 초기화 한다.

ReplyPage와 관련된 내용은 밑에서 다시 다루도록 하겠다.

 

다음으로 현재 페이지 번호를 나타내는 num과 댓글의 총 개수를 나타내는 count를 service에서 작성한 댓글 총 개수를 구하는 메소드를 통해 세팅한다.

 

Service에서 선언한 댓글을 조회하는 메소드인 'freeReplylist' 메소드를 사용해서 불러온 댓글 목록을 ReplyVO 타입의 List에 담아준다.

List<ReplyVO> reply = service.freeReplylist(b_num, page.getDisplayPost(), page.getPostNum());

여기서는 null로 초기화 한 후에 데이터를 담아주지만 이렇게 초기화와 동시에 담아도 상관없다.

 

이제 model을 통해 값들을 view로 전달하고 이는 게시글의 경우와 방식이 동일하다.

그렇게 나온 결과는 아래와 같다.

 

 

현재 로그인 하고 있는 아이디가 댓글 작성자의 아이디와 같거나 관리자이면 수정, 삭제 버튼이 출력된다.

페이징의 경우 방식은 게시글과 동일하다.

다만 게시글은 한 페이지에 10개 씩 출력하는 반면 댓글은 한 페이지에 5개 씩 출력되기 때문에 댓글을 위한 Page 클래스를 다시 만들어야 한다.

 

 

다른 것은 모두 동일하고, 한 페이지에 출력할 게시물(댓글) 개수를 의미하는 postNum 변수만 5로 수정하면 된다.

 


다음 글)

728x90