관리 메뉴

거니의 velog

231011_AJAX 강의 본문

대덕인재개발원/대덕인재개발원_Front End

231011_AJAX 강의

Unlimited00 2023. 10. 11. 11:07

[board.jsp]

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page isELIgnored="true" %>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>게시판 리스트</title>
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link href="<%= request.getContextPath() %>/css/style07.css" rel="stylesheet" />
		<script src="<%= request.getContextPath() %>/js/jquery-3.7.1.min.js"></script>
		<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
		<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
		<script defer src="<%= request.getContextPath() %>/js/script.js"></script>
		<script>
		
			var mypath = "<%= request.getContextPath() %>";
			var currentpage = 1;
			
			$(function(){
				
				// 실행하자마자 리스트 출력 - stype, sword 없다.
				$.listBoardServer(currentpage);
				
				// 페이지 번호 클릭 이벤트
				$(document).on("click", ".pageno", function(){
					currentpage = parseInt( $(this).text().trim() );
					$.listBoardServer(currentpage);
				})
				
				// 다음 클릭 이벤트
				$(document).on("click", "#next", function(){
					currentpage = parseInt( $(".pageno").last().text().trim() ) + 1;
					$.listBoardServer(currentpage);
				});
				
				// 이전 클릭 이벤트
				$(document).on("click", "#prev", function(){
					currentpage = parseInt( $(".pageno").first().text().trim() ) - 1;
					$.listBoardServer(currentpage);
				});
				
				// Search 검색 이벤트
				$(document).on("click", "#search", function(){
					currentpage = 1;
					$.listBoardServer(currentpage);
				});
				
			});
			
		</script>
	</head>
	<body>
	
		<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
		  <div class="container-fluid">
		    <a class="navbar-brand" href="javascript:void(0)">Logo</a>
		    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mynavbar">
		      <span class="navbar-toggler-icon"></span>
		    </button>
		    <div class="collapse navbar-collapse" id="mynavbar">
		      <ul class="navbar-nav me-auto">
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		      </ul>
		      <form class="d-flex">
		      	<select class="form-select" id="stype" name="stype" style="margin-right: 5px;">
		      		<option value="">전체</option>
		      		<option value="writer">작성자</option>
		      		<option value="subject">제목</option>
		      		<option value="content">내용</option>
		      	</select>
		        <input class="form-control me-2" id="sword" name="sword" type="text" placeholder="Search">
		        <button id="search" class="btn btn-primary" type="button">Search</button>
		      </form>
		    </div>
		  </div>
		</nav>
		
		<br /><br />
		
		<div id="result"></div>
		<br />
		<br />
		<div id="pageList"></div>
		
	</body>
</html>

[style07.css]

@charset "utf-8";

@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;500;700&display=swap');

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: "Noto Sans KR", sans-serif!important;
    color: #333;
    word-break: keep-all;
}

a {
	text-decoration: none;
	color: #333;
}

.cen {
    max-width: 1000px;
    width: 100%;
    margin: auto;
}

/***********************************/

.card-body {
	display: flex;
	flex-direction: column;
}
.divContBox {
	display: flex;
	flex-direction: row;
}
.divCont1 {
	flex: 70%;
}
.divCont2 {
	flex: 30%;
	text-align: right;
}

header {
	padding: 4%;
	height: 200px;
	border: 2px dotted gold;
	margin: 30px;
	background-image: linear-gradient(to bottom, cornflowerBlue, white);
}

section {
	padding: 3%;
	margin: 30px;
}

.dlog {
	border: 1px dotted blue;
	padding: 10px;
	display: flex;
	flex-direction: row;
	flex-wrap: wrap;
}

.dlog input {
	width: calc(150px - 1%);
	height: 30px;
}

iframe[name=iboard] {
	width: 100%;
    height: 500px;
    border: none;
}

.navbar { margin : 2% }

.navbar a {
	display: none;!important
	/* visibility: hidden!important; */
}

.card-body div {
	border: 1px solid gold;
	margin : 5px;
	padding : 5px;
}

#pageList {
	position: relative;
}

#pageList .pagination {
	position: absolute;
	left: 50%;
	transform: translateX(-50%);
}

[script.js]

$.listBoardServer = function(cpage){
	
	var stype = $("#stype option:selected").val().trim();
	var sword = $("#sword").val().trim();
	
	//console.log("stype : " + stype);
	//console.log("sword : " + sword);
	
	// 실행하자마자 리스트 출력
	$.ajax({
		url : `${mypath}/boardList.do`,
		type : 'post',
		data : {
			'page' : cpage,
			'stype' : stype,
			'sword' : sword
		},
		success : function(res){
			
			var code = "<div class='container mt-3'>";
			code += "<div id='accordion'>";
			
			$.each(res.data, function(i, v){
				code += `<div class="card">
							<div class="card-header">
								<a class="btn" data-bs-toggle="collapse" href="#collapse${v.num}">
									${v.subject}
								</a>
							</div>
							<div id="collapse${v.num}" class="collapse" data-bs-parent="#accordion">
								<div class="card-body">
									<div class="divContBox">
										<div class="divCont1">
											작성자 <span class="wr">${v.writer}</span> 
											이메일 <span class="em">${v.mail}</span> 
											조회수 <span class="fit">${v.hit}</span> 
											날짜 <span class="date">${v.wdate}</span>
										</div>
										<div class="divCont2">
											<button idx="${v.num}" class="modifyCont btn btn-warning btn-sm" type="button">수정</button>
											<button idx="${v.num}" class="removeCont btn btn-danger btn-sm" type="button">삭제</button>
										</div>
									</div>
									<div>${v.content}</div>
									<div>댓글쓰기 textarea 등록버튼</div>
								</div>
							</div>
						</div>`;
			}); // 반복문 끝
			
			code += "</div>";
			code += "</div>";
			
			$("#result").html(code);
			
			// 페이지 번호 만들기
			var paging = pageList(res.startPage, res.endPage, res.totalPage);
			
			// 출력
			$("#pageList").html(paging);
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});

} // 리스트 끝

var pageList = function(sp, ep, tp){
	
	var paging = `<ul class="pagination">`;
	
	// 이전
	if(sp > 1){
		paging += `<li class="page-item"><a id="prev" class="page-link" href="#">Previous</a></li>`;
	}
	
	// 페이지 번호
	for(var i=sp; i<=ep; i++){
		if(i == currentpage){
			paging += `<li class="page-item active"><a class="page-link pageno" href="#">${i}</a></li>`;
		}else{
			paging += `<li class="page-item"><a class="page-link pageno" href="#">${i}</a></li>`;
		}
	}
	
	// 다음
	if(ep < tp){
		paging += `<li class="page-item"><a id="next" class="page-link" href="#">Next</a></li>`;
	}
	
	paging += `</ul>`;
	
	return paging;
	
}

[index.jsp]

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>로그인 화면</title>
		<link href="<%= request.getContextPath() %>/css/style07.css" rel="stylesheet" />
	</head>
	<body>
		<header>
			<div class="dlog">
				<!-- 
					로그인 / 로그아웃 - HttpSession을 이용하여 로그인폼 또는 로그아웃폼을 삽입. 
					logpro.jsp를 실행시킨 결과물을 현재 페이지에 삽입
				-->
				<jsp:include page="logpro.jsp"></jsp:include>
			</div>
			<br />
			<a href="join.jsp" target="iboard">회원가입</a>&nbsp;&nbsp;&nbsp;
			<a href="../board/board.jsp" target="iboard">게시판</a>
		</header>
		<section>
			<iframe name="iboard" src="../board/board.jsp"></iframe>
		</section>
	</body>
</html>

- http://localhost/boardpro/member/index.jsp


[reply.xml]

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper 
	PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN" 
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="reply">
	
	<!-- 댓글 삭제 결과 : int, param : 댓글번호(int), id이름 = 메소드 이름 -->
	<delete id="deleteReply" parameterType="int">
		DELETE FROM replytab
		WHERE renum = #{renum}
	</delete>
	
	<!-- 댓글 수정 결과 : int, param : vo, id이름 = 메소드 이름 -->
	<update id="updateReply" parameterType="replyVo">
		UPDATE replytab SET cont = #{cont}, redate = sysdate
		WHERE renum = #{renum} AND bonum = #{bonum}
	</update>
	
	<!-- 댓글 저장 결과 : int, param : vo, id이름 = 메소드 이름 -->
	<insert id="insertReply" parameterType="replyVo">
		INSERT INTO replytab (renum, bonum, name, cont, redate) 
		VALUES (replytab_renum_seq.nextval, #{bonum}, #{name}, #{cont}, sysdate)
	</insert>
	
	<!-- 댓글 리스트 결과 : vo, param : 게시판번호(int), id이름 = 메소드 이름 -->
	<select id="listReply" parameterType="int" resultType="replyVo">
		select * from replytab 
		where bonum = #{bonum}
	</select>
	
</mapper>

[ReplyDaoImpl.java]

package kr.or.ddit.reply.dao;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import kr.or.ddit.mybatis.config.MyBatisUtil;
import kr.or.ddit.reply.vo.ReplyVO;

public class ReplyDaoImpl implements IReplyDAO {

	private static ReplyDaoImpl dao;
	private ReplyDaoImpl() {}
	public static ReplyDaoImpl getInstance() {
		if(dao == null) dao = new ReplyDaoImpl();
		return dao;
	}
	
	@Override
	public int insertReply(ReplyVO vo) {
		SqlSession session = MyBatisUtil.getSqlSession();
		int cnt = 0;
		
		try {
			cnt = session.insert("reply.insertReply", vo);
			if(cnt > 0) session.commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(session != null) session.close();
		}
		
		return cnt;
	}

	@Override
	public int deleteReply(int rno) {
		SqlSession session = MyBatisUtil.getSqlSession();
		int cnt = 0;
		
		try {
			cnt = session.delete("reply.deleteReply", rno);
			if(cnt > 0) session.commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(session != null) session.close();
		}
		
		return cnt;
	}

	@Override
	public int updateReply(ReplyVO vo) {
		SqlSession session = MyBatisUtil.getSqlSession();
		int cnt = 0;
		
		try {
			cnt = session.update("reply.updateReply", vo);
			if(cnt > 0) session.commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(session != null) session.close();
		}
		
		return cnt;
	}

	@Override
	public List<ReplyVO> listReply(int bno) {
		SqlSession session = MyBatisUtil.getSqlSession();
		List<ReplyVO> replyList = null;
		
		try {
			replyList = session.selectList("reply.listReply", bno);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(session != null) session.close();
		}

		return replyList;
	}

}

[ReplyServiceImpl.java]

package kr.or.ddit.reply.service;

import java.util.List;

import kr.or.ddit.reply.dao.IReplyDAO;
import kr.or.ddit.reply.dao.ReplyDaoImpl;
import kr.or.ddit.reply.vo.ReplyVO;

public class ReplyServiceImpl implements IReplyService {

	private IReplyDAO dao;
	
	private static ReplyServiceImpl service;
	private ReplyServiceImpl() {
		dao = ReplyDaoImpl.getInstance();
	}
	public static ReplyServiceImpl getInstance() {
		if(service == null) service = new ReplyServiceImpl();
		return service;
	}
	
	@Override
	public int insertReply(ReplyVO vo) {
		return dao.insertReply(vo);
	}

	@Override
	public int deleteReply(int rno) {
		return dao.deleteReply(rno);
	}

	@Override
	public int updateReply(ReplyVO vo) {
		return dao.updateReply(vo);
	}

	@Override
	public List<ReplyVO> listReply(int bno) {
		return dao.listReply(bno);
	}

}

[ReplyVO.java]

package kr.or.ddit.reply.vo;

public class ReplyVO {

	private int renum;
	private int bonum;
	private String name;
	private String cont;
	private String redate;
	
	public ReplyVO() {}
	public ReplyVO(int renum, int bonum, String name, String cont, String redate) {
		this.renum = renum;
		this.bonum = bonum;
		this.name = name;
		this.cont = cont;
		this.redate = redate;
	}
	
	public int getRenum() {
		return renum;
	}
	public void setRenum(int renum) {
		this.renum = renum;
	}
	public int getBonum() {
		return bonum;
	}
	public void setBonum(int bonum) {
		this.bonum = bonum;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getCont() {
		return cont;
	}
	public void setCont(String cont) {
		this.cont = cont;
	}
	public String getRedate() {
		return redate;
	}
	public void setRedate(String redate) {
		this.redate = redate;
	}
	
	@Override
	public String toString() {
		return "ReplyVO [renum=" + renum + ", bonum=" + bonum + ", name=" + name + ", cont=" + cont + ", redate="
				+ redate + "]";
	}
	
}

[ReplyInsert.java]

package kr.or.ddit.reply.controller;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import kr.or.ddit.reply.service.IReplyService;
import kr.or.ddit.reply.service.ReplyServiceImpl;
import kr.or.ddit.reply.vo.ReplyVO;


@WebServlet("/replyInsert.do")
public class ReplyInsert extends HttpServlet {
	
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		request.setCharacterEncoding("utf-8");
		
		// 전송데이터 받기
		//data : reply ==> name, bonum, cont
		int bonum = Integer.parseInt(request.getParameter("bonum"));
		String name = request.getParameter("name");
		String cont = request.getParameter("cont");
		
		ReplyVO rvo = new ReplyVO();
		rvo.setBonum(bonum);
		rvo.setName(name);
		rvo.setCont(cont);
		
		// 서비스 객체 얻기
		IReplyService service = ReplyServiceImpl.getInstance();
		
		// 서비스 메소드 호출 - 결과값 int
		int insRepCnt = service.insertReply(rvo);
		
		// 결과를 request에 저장
		request.setAttribute("insRepCnt", insRepCnt);
		
		// view페이지 설정 = forward
		request.getRequestDispatcher("/boardview/insRepResult.jsp").forward(request, response);
		
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

[insRepResult.jsp]

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	// 서블릿에서 저장한 데이터 꺼내기
	int insRepCnt = (int) request.getAttribute("insRepCnt");
	if(insRepCnt > 0){
%>
		{
			"msg" : "댓글 저장 성공"
		}
	<%}else{%>
		{
			"msg" : "댓글 저장 실패"
		}
<%	
	}
%>

[board.jsp]

<%@page import="com.google.gson.Gson"%>
<%@page import="kr.or.ddit.member.vo.MemberVO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page isELIgnored="true" %>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>게시판 리스트</title>
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link href="<%= request.getContextPath() %>/css/style07.css" rel="stylesheet" />
		<script src="<%= request.getContextPath() %>/js/jquery-3.7.1.min.js"></script>
		<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
		<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
		<script defer src="<%= request.getContextPath() %>/js/script.js"></script>
		<% 
			// 로그인 상태 체크
			MemberVO mvo = (MemberVO) session.getAttribute("loginok");
			String jsonVo = null;
			Gson gson = new Gson();
			if(mvo != null) jsonVo = gson.toJson(mvo);
			/*  
				jsonVo = {
					"mem_id" : "a001",
					"mem_pass" : "asdfasdf",
					"mem_name" : "김은대"
				}
			*/
		%>
		<script>
		
			var mvo = <%= jsonVo %>; /* mvo.mem_id, mvo.mem_pass, mvo.mem_name */
			//console.log(mvo);
		
			var mypath = "<%= request.getContextPath() %>";
			var currentpage = 1;
			
			var reply = {}; // 객체형 변수, 동적으로 추가 가능. - reply.bonum, reply.name, reply.cont
			
			$(function(){
				
				// 실행하자마자 리스트 출력 - stype, sword 없다.
				$.listBoardServer(currentpage);
				
				// 페이지 번호 클릭 이벤트
				$(document).on("click", ".pageno", function(){
					currentpage = parseInt( $(this).text().trim() );
					$.listBoardServer(currentpage);
				})
				
				// 다음 클릭 이벤트
				$(document).on("click", "#next", function(){
					currentpage = parseInt( $(".pageno").last().text().trim() ) + 1;
					$.listBoardServer(currentpage);
				});
				
				// 이전 클릭 이벤트
				$(document).on("click", "#prev", function(){
					currentpage = parseInt( $(".pageno").first().text().trim() ) - 1;
					$.listBoardServer(currentpage);
				});
				
				// Search 검색 이벤트
				$(document).on("click", "#search", function(){
					currentpage = 1;
					$.listBoardServer(currentpage);
				});
				
				// 게시글 수정, 게시글 삭제, 댓글 등록, 제목 클릭, 댓글 삭제, 댓글 수정 이벤트
				$(document).on("click", ".action", function(){
					var thisIs = $(this);
					var name = thisIs.attr("name");
					var idx = thisIs.attr("idx");
					
					if(name == "delete"){
						//alert(idx + "번 글을 삭제합니다.");
					}else if(name == "title"){
						//alert(idx + "번 글의 댓글을 출력합니다.");
					}else if(name == "modify"){
						//alert(idx + "번 글을 수정합니다.");
					}else if(name == "reply"){
						//alert(idx + "번 댓글을 등록합니다.");
						
						//입력한 내용 가져오기
						var cont = thisIs.prev().val();
						//console.log(cont);
						
						reply.name = mvo.mem_name;
						reply.bonum = idx;
						reply.cont = cont;
						//console.log(reply);
						
						$.replyInsert();
						
						// 입력 값 비우기
						thisIs.prev().val("");
					}
				});
				
			});
			
		</script>
	</head>
	<body>
	
		<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
		  <div class="container-fluid">
		    <a class="navbar-brand" href="javascript:void(0)">Logo</a>
		    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mynavbar">
		      <span class="navbar-toggler-icon"></span>
		    </button>
		    <div class="collapse navbar-collapse" id="mynavbar">
		      <ul class="navbar-nav me-auto">
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		      </ul>
		      <form class="d-flex">
		      	<select class="form-select" id="stype" name="stype" style="margin-right: 5px;">
		      		<option value="">전체</option>
		      		<option value="writer">작성자</option>
		      		<option value="subject">제목</option>
		      		<option value="content">내용</option>
		      	</select>
		        <input class="form-control me-2" id="sword" name="sword" type="text" placeholder="Search">
		        <button id="search" class="btn btn-primary" type="button">Search</button>
		      </form>
		    </div>
		  </div>
		</nav>
		
		<br /><br />
		
		<div id="result"></div>
		<br />
		<br />
		<div id="pageList"></div>
		
	</body>
</html>

[script.js]

$.replyInsert = function(){
	$.ajax({
		url : `${mypath}/replyInsert.do`,
		type : 'post',
		data : reply, /* name, bonum, cont */
		success : function(res){
			
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

$.listBoardServer = function(cpage){
	
	var stype = $("#stype option:selected").val().trim();
	var sword = $("#sword").val().trim();
	
	//console.log("stype : " + stype);
	//console.log("sword : " + sword);
	
	// 실행하자마자 리스트 출력
	$.ajax({
		url : `${mypath}/boardList.do`,
		type : 'post',
		data : {
			'page' : cpage,
			'stype' : stype,
			'sword' : sword
		},
		success : function(res){
			
			var code = "<div class='container mt-3'>";
			code += "<div id='accordion'>";
			
			$.each(res.data, function(i, v){
				code += `<div class="card">
							<div class="card-header">
								<a class="btn action" name="title" idx="${v.num}" data-bs-toggle="collapse" href="#collapse${v.num}">
									${v.subject}
								</a>
							</div>
							<div id="collapse${v.num}" class="collapse" data-bs-parent="#accordion">
								<div class="card-body">
									<div class="divContBox">
										<div class="divCont1">
											작성자 <span class="wr">${v.writer}</span> 
											이메일 <span class="em">${v.mail}</span> 
											조회수 <span class="fit">${v.hit}</span> 
											날짜 <span class="date">${v.wdate}</span>
										</div>
										<div class="divCont2">
											<button idx="${v.num}" class="action btn btn-warning btn-sm" type="button" name="modify">수정</button>
											<button idx="${v.num}" class="action btn btn-danger btn-sm" type="button" name="delete">삭제</button>
										</div>
									</div>
									<div>${v.content}</div>
									<div>
										<textarea rows="2" cols="50"></textarea>
										<button style="height: 50px; vertical-align: top; padding: 0px 10px;" class="action" name="reply" type="button" idx="${v.num}">등록</button>
									</div>
								</div>
							</div>
						</div>`;
			}); // 반복문 끝
			
			code += "</div>";
			code += "</div>";
			
			$("#result").html(code);
			
			// 페이지 번호 만들기
			var paging = pageList(res.startPage, res.endPage, res.totalPage);
			
			// 출력
			$("#pageList").html(paging);
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});

} // 리스트 끝

var pageList = function(sp, ep, tp){
	
	var paging = `<ul class="pagination">`;
	
	// 이전
	if(sp > 1){
		paging += `<li class="page-item"><a id="prev" class="page-link" href="javascript:void(0)">Previous</a></li>`;
	}
	
	// 페이지 번호
	for(var i=sp; i<=ep; i++){
		if(i == currentpage){
			paging += `<li class="page-item active"><a class="page-link pageno" href="javascript:void(0)">${i}</a></li>`;
		}else{
			paging += `<li class="page-item"><a class="page-link pageno" href="javascript:void(0)">${i}</a></li>`;
		}
	}
	
	// 다음
	if(ep < tp){
		paging += `<li class="page-item"><a id="next" class="page-link" href="javascript:void(0)">Next</a></li>`;
	}
	
	paging += `</ul>`;
	
	return paging;
	
}

- http://localhost/boardpro/member/index.jsp

로그인 한 상태에서만 댓글 작성이 가능하다.
DB에도 잘 적용되었다.


[ReplyList.java]

package kr.or.ddit.reply.controller;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import kr.or.ddit.reply.service.IReplyService;
import kr.or.ddit.reply.service.ReplyServiceImpl;
import kr.or.ddit.reply.vo.ReplyVO;


@WebServlet("/replyList.do")
public class ReplyList extends HttpServlet {
	
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		
		// 전송 데이터 가져오기
		int bonum = Integer.parseInt(request.getParameter("bonum"));
		
		// 서비스 객체
		IReplyService service = ReplyServiceImpl.getInstance();
		
		// 메소드 호출
		List<ReplyVO> repList = service.listReply(bonum);
		
		// 저장
		request.setAttribute("repList", repList);
		
		// 보내기
		request.getRequestDispatcher("/boardview/listRepResult.jsp").forward(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

[listRepResult.jsp]

<%@page import="com.google.gson.Gson"%>
<%@page import="kr.or.ddit.reply.vo.ReplyVO"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	// 서블릿에서 저장한 데이터 꺼내기
	List<ReplyVO> repList = (List<ReplyVO>) request.getAttribute("repList");

	Gson gson = new Gson();
	String result = gson.toJson(repList);
	
	out.print(result);
	out.flush();
%>

[board.jsp]

<%@page import="com.google.gson.Gson"%>
<%@page import="kr.or.ddit.member.vo.MemberVO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page isELIgnored="true" %>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>게시판 리스트</title>
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link href="<%= request.getContextPath() %>/css/style07.css" rel="stylesheet" />
		<script src="<%= request.getContextPath() %>/js/jquery-3.7.1.min.js"></script>
		<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
		<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
		<script defer src="<%= request.getContextPath() %>/js/script.js"></script>
		<% 
			// 로그인 상태 체크
			MemberVO mvo = (MemberVO) session.getAttribute("loginok");
			String jsonVo = null;
			Gson gson = new Gson();
			if(mvo != null) jsonVo = gson.toJson(mvo);
			/*  
				jsonVo = {
					"mem_id" : "a001",
					"mem_pass" : "asdfasdf",
					"mem_name" : "김은대"
				}
			*/
		%>
		<script>
		
			var mvo = <%= jsonVo %>; /* mvo.mem_id, mvo.mem_pass, mvo.mem_name */
			//console.log(mvo);
		
			var mypath = "<%= request.getContextPath() %>";
			var currentpage = 1;
			
			var reply = {}; // 객체형 변수, 동적으로 추가 가능. - reply.bonum, reply.name, reply.cont
			
			var idx = ""; // idx 전역변수 선언
			
			var target = ""; // .action의 this 전역변수
			
			$(function(){
				
				// 실행하자마자 리스트 출력 - stype, sword 없다.
				$.listBoardServer(currentpage);
				
				// 페이지 번호 클릭 이벤트
				$(document).on("click", ".pageno", function(){
					currentpage = parseInt( $(this).text().trim() );
					$.listBoardServer(currentpage);
				})
				
				// 다음 클릭 이벤트
				$(document).on("click", "#next", function(){
					currentpage = parseInt( $(".pageno").last().text().trim() ) + 1;
					$.listBoardServer(currentpage);
				});
				
				// 이전 클릭 이벤트
				$(document).on("click", "#prev", function(){
					currentpage = parseInt( $(".pageno").first().text().trim() ) - 1;
					$.listBoardServer(currentpage);
				});
				
				// Search 검색 이벤트
				$(document).on("click", "#search", function(){
					currentpage = 1;
					$.listBoardServer(currentpage);
				});
				
				// 게시글 수정, 게시글 삭제, 댓글 등록, 제목 클릭, 댓글 삭제, 댓글 수정 이벤트
				$(document).on("click", ".action", function(){
					var thisIs = $(this);
					target = thisIs;
					var name = thisIs.attr("name");
					idx = thisIs.attr("idx");
					
					if(name == "delete"){
						//alert(idx + "번 글을 삭제합니다.");
					}else if(name == "title"){
						//alert(idx + "번 글의 댓글을 출력합니다.");
						
						// 댓글 출력 ajax 수행
						$.replyList();
					}else if(name == "modify"){
						//alert(idx + "번 글을 수정합니다.");
					}else if(name == "reply"){
						//alert(idx + "번 댓글을 등록합니다.");
						
						//입력한 내용 가져오기
						var cont = thisIs.prev().val();
						//console.log(cont);
						
						reply.name = mvo.mem_name;
						reply.bonum = idx;
						reply.cont = cont;
						//console.log(reply);
						
						// 댓글 입력 ajax 수행
						$.replyInsert();
						
						// 입력 값 비우기
						thisIs.prev().val("");
					}
				});
				
			});
			
		</script>
	</head>
	<body>
	
		<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
		  <div class="container-fluid">
		    <a class="navbar-brand" href="javascript:void(0)">Logo</a>
		    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mynavbar">
		      <span class="navbar-toggler-icon"></span>
		    </button>
		    <div class="collapse navbar-collapse" id="mynavbar">
		      <ul class="navbar-nav me-auto">
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		      </ul>
		      <form class="d-flex">
		      	<select class="form-select" id="stype" name="stype" style="margin-right: 5px;">
		      		<option value="">전체</option>
		      		<option value="writer">작성자</option>
		      		<option value="subject">제목</option>
		      		<option value="content">내용</option>
		      	</select>
		        <input class="form-control me-2" id="sword" name="sword" type="text" placeholder="Search">
		        <button id="search" class="btn btn-primary" type="button">Search</button>
		      </form>
		    </div>
		  </div>
		</nav>
		
		<br /><br />
		
		<div id="result"></div>
		<br />
		<br />
		<div id="pageList"></div>
		
	</body>
</html>

[script.js]

$.replyList = function(){
	$.ajax({
		url : `${mypath}/replyList.do`,
		type : 'post',
		data : { "bonum" : idx },
		success : function(res){
			
			//console.log(res);
			
			var recode = "";
			
			$.each(res, function(i, v){
				var cont = v.cont;
				cont = cont.replaceAll(/\n/g, "<br />");
				recode += `<div class="reply-body">
							<div class="divContBox">
								<div class="divCont1">
									작성자 <span class="wr">${v.name}</span> 
									날짜 <span class="date">${v.redate}</span>
								</div>
								<div class="divCont2">
									<button idx="${v.renum}" class="action btn btn-warning btn-sm" type="button" name="rep_modify">댓글수정</button>
									<button idx="${v.renum}" class="action btn btn-danger btn-sm" type="button" name="rep_delete">댓글삭제</button>
								</div>
							</div>
							<div>${cont}</div>
						</div>`;
			});
			
			// 댓글 출력
			//target.parent().next().find(".card-body").append(recode);
			//target.parents(".card").find(".reply-body").empty();
			target.parents(".card").find(".reply-body").remove();
			target.parents(".card").find(".card-body").append(recode);
			
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

$.replyInsert = function(){
	$.ajax({
		url : `${mypath}/replyInsert.do`,
		type : 'post',
		data : reply, /* name, bonum, cont */
		success : function(res){
			//console.log(res);
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

$.listBoardServer = function(cpage){
	
	var stype = $("#stype option:selected").val().trim();
	var sword = $("#sword").val().trim();
	
	//console.log("stype : " + stype);
	//console.log("sword : " + sword);
	
	// 실행하자마자 리스트 출력
	$.ajax({
		url : `${mypath}/boardList.do`,
		type : 'post',
		data : {
			'page' : cpage,
			'stype' : stype,
			'sword' : sword
		},
		success : function(res){
			
			var code = "<div class='container mt-3'>";
			code += "<div id='accordion'>";
			
			$.each(res.data, function(i, v){
				code += `<div class="card">
							<div class="card-header">
								<a class="btn action" name="title" idx="${v.num}" data-bs-toggle="collapse" href="#collapse${v.num}">
									${v.subject}
								</a>
							</div>
							<div id="collapse${v.num}" class="collapse" data-bs-parent="#accordion">
								<div class="card-body">
									<div class="divContBox">
										<div class="divCont1">
											작성자 <span class="wr">${v.writer}</span> 
											이메일 <span class="em">${v.mail}</span> 
											조회수 <span class="fit">${v.hit}</span> 
											날짜 <span class="date">${v.wdate}</span>
										</div>
										<div class="divCont2">
											<button idx="${v.num}" class="action btn btn-warning btn-sm" type="button" name="modify">수정</button>
											<button idx="${v.num}" class="action btn btn-danger btn-sm" type="button" name="delete">삭제</button>
										</div>
									</div>
									<div>${v.content}</div>
									<div>
										<textarea rows="2" cols="50"></textarea>
										<button style="height: 50px; vertical-align: top; padding: 0px 10px;" class="action" name="reply" type="button" idx="${v.num}">등록</button>
									</div>
								</div>
							</div>
						</div>`;
			}); // 반복문 끝
			
			code += "</div>";
			code += "</div>";
			
			$("#result").html(code);
			
			// 페이지 번호 만들기
			var paging = pageList(res.startPage, res.endPage, res.totalPage);
			
			// 출력
			$("#pageList").html(paging);
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});

} // 리스트 끝

var pageList = function(sp, ep, tp){
	
	var paging = `<ul class="pagination">`;
	
	// 이전
	if(sp > 1){
		paging += `<li class="page-item"><a id="prev" class="page-link" href="javascript:void(0)">Previous</a></li>`;
	}
	
	// 페이지 번호
	for(var i=sp; i<=ep; i++){
		if(i == currentpage){
			paging += `<li class="page-item active"><a class="page-link pageno" href="javascript:void(0)">${i}</a></li>`;
		}else{
			paging += `<li class="page-item"><a class="page-link pageno" href="javascript:void(0)">${i}</a></li>`;
		}
	}
	
	// 다음
	if(ep < tp){
		paging += `<li class="page-item"><a id="next" class="page-link" href="javascript:void(0)">Next</a></li>`;
	}
	
	paging += `</ul>`;
	
	return paging;
	
}

- http://localhost/boardpro/member/index.jsp


[reply.xml]

	<!-- 댓글 리스트 결과 : vo, param : 게시판번호(int), id이름 = 메소드 이름 -->
	<select id="listReply" parameterType="int" resultType="replyVo">
		select * from replytab 
		where bonum = #{bonum} 
		order by renum desc
	</select>

[board.jsp]

<%@page import="com.google.gson.Gson"%>
<%@page import="kr.or.ddit.member.vo.MemberVO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page isELIgnored="true" %>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>게시판 리스트</title>
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link href="<%= request.getContextPath() %>/css/style07.css" rel="stylesheet" />
		<script src="<%= request.getContextPath() %>/js/jquery-3.7.1.min.js"></script>
		<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
		<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
		<script defer src="<%= request.getContextPath() %>/js/script.js"></script>
		<% 
			// 로그인 상태 체크
			MemberVO mvo = (MemberVO) session.getAttribute("loginok");
			String jsonVo = null;
			Gson gson = new Gson();
			if(mvo != null) jsonVo = gson.toJson(mvo);
			/*  
				jsonVo = {
					"mem_id" : "a001",
					"mem_pass" : "asdfasdf",
					"mem_name" : "김은대"
				}
			*/
		%>
		<script>
		
			var mvo = <%= jsonVo %>; /* mvo.mem_id, mvo.mem_pass, mvo.mem_name */
			//console.log(mvo);
		
			var mypath = "<%= request.getContextPath() %>";
			var currentpage = 1;
			
			var reply = {}; // 객체형 변수, 동적으로 추가 가능. - reply.bonum, reply.name, reply.cont
			
			var idx = ""; // idx 전역변수 선언
			
			var target = ""; // .action의 this 전역변수
			
			$(function(){
				
				// 실행하자마자 리스트 출력 - stype, sword 없다.
				$.listBoardServer(currentpage);
				
				// 페이지 번호 클릭 이벤트
				$(document).on("click", ".pageno", function(){
					currentpage = parseInt( $(this).text().trim() );
					$.listBoardServer(currentpage);
				})
				
				// 다음 클릭 이벤트
				$(document).on("click", "#next", function(){
					currentpage = parseInt( $(".pageno").last().text().trim() ) + 1;
					$.listBoardServer(currentpage);
				});
				
				// 이전 클릭 이벤트
				$(document).on("click", "#prev", function(){
					currentpage = parseInt( $(".pageno").first().text().trim() ) - 1;
					$.listBoardServer(currentpage);
				});
				
				// Search 검색 이벤트
				$(document).on("click", "#search", function(){
					currentpage = 1;
					$.listBoardServer(currentpage);
				});
				
				// 게시글 수정, 게시글 삭제, 댓글 등록, 제목 클릭, 댓글 삭제, 댓글 수정 이벤트
				$(document).on("click", ".action", function(){
					var thisIs = $(this);
					target = thisIs;
					var name = thisIs.attr("name");
					idx = thisIs.attr("idx");
					
					if(name == "delete"){
						//alert(idx + "번 글을 삭제합니다.");
					}else if(name == "title"){
						//alert(idx + "번 글의 댓글을 출력합니다.");
						
						// 댓글 출력 ajax 수행
						$.replyList();
					}else if(name == "modify"){
						//alert(idx + "번 글을 수정합니다.");
					}else if(name == "reply"){
						//alert(idx + "번 댓글을 등록합니다.");
						
						//입력한 내용 가져오기
						var cont = thisIs.prev().val();
						//console.log(cont);
						
						reply.name = mvo.mem_name;
						reply.bonum = idx;
						reply.cont = cont;
						//console.log(reply);
						
						// 댓글 입력 ajax 수행
						$.replyInsert();
						
						// 댓글을 화면에 추가
						// $.replyInsert(); ==> success에서 수행
						
						// 입력 값 비우기
						thisIs.prev().val("");
					}
				});
				
			});
			
		</script>
	</head>
	<body>
	
		<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
		  <div class="container-fluid">
		    <a class="navbar-brand" href="javascript:void(0)">Logo</a>
		    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mynavbar">
		      <span class="navbar-toggler-icon"></span>
		    </button>
		    <div class="collapse navbar-collapse" id="mynavbar">
		      <ul class="navbar-nav me-auto">
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		      </ul>
		      <form class="d-flex">
		      	<select class="form-select" id="stype" name="stype" style="margin-right: 5px;">
		      		<option value="">전체</option>
		      		<option value="writer">작성자</option>
		      		<option value="subject">제목</option>
		      		<option value="content">내용</option>
		      	</select>
		        <input class="form-control me-2" id="sword" name="sword" type="text" placeholder="Search">
		        <button id="search" class="btn btn-primary" type="button">Search</button>
		      </form>
		    </div>
		  </div>
		</nav>
		
		<br /><br />
		
		<div id="result"></div>
		<br />
		<br />
		<div id="pageList"></div>
		
	</body>
</html>

[script.js]

// 댓글 리스트 가져오기
$.replyList = function(){
	$.ajax({
		url : `${mypath}/replyList.do`,
		type : 'post',
		data : { "bonum" : idx },
		success : function(res){
			
			//console.log(res);
			
			var recode = "";
			
			$.each(res, function(i, v){
				var cont = v.cont;
				cont = cont.replaceAll(/\n/g, "<br />");
				recode += `<div class="reply-body">
							<div class="divContBox">
								<div class="divCont1">
									작성자 <span class="wr">${v.name}</span> 
									날짜 <span class="date">${v.redate}</span>
								</div>
								<div class="divCont2">
									<button idx="${v.renum}" class="action btn btn-warning btn-sm" type="button" name="rep_modify">댓글수정</button>
									<button idx="${v.renum}" class="action btn btn-danger btn-sm" type="button" name="rep_delete">댓글삭제</button>
								</div>
							</div>
							<div>${cont}</div>
						</div>`;
			});
			
			// 댓글 출력
			//target.parent().next().find(".card-body").append(recode);
			//target.parents(".card").find(".reply-body").empty();
			target.parents(".card").find(".reply-body").remove();
			target.parents(".card").find(".card-body").append(recode);
			
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

// 댓글 저장
$.replyInsert = function(){
	$.ajax({
		url : `${mypath}/replyInsert.do`,
		type : 'post',
		data : reply, /* name, bonum, cont */
		success : function(res){
			
			//console.log(res);
			
			// 댓글을 화면에 추가하기 위해서
			// 댓글 리스트 가져오기를 수행한다.
			$.replyList();
			
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

$.listBoardServer = function(cpage){
	
	var stype = $("#stype option:selected").val().trim();
	var sword = $("#sword").val().trim();
	
	//console.log("stype : " + stype);
	//console.log("sword : " + sword);
	
	// 실행하자마자 리스트 출력
	$.ajax({
		url : `${mypath}/boardList.do`,
		type : 'post',
		data : {
			'page' : cpage,
			'stype' : stype,
			'sword' : sword
		},
		success : function(res){
			
			var code = "<div class='container mt-3'>";
			code += "<div id='accordion'>";
			
			$.each(res.data, function(i, v){
				code += `<div class="card">
							<div class="card-header">
								<a class="btn action" name="title" idx="${v.num}" data-bs-toggle="collapse" href="#collapse${v.num}">
									${v.subject}
								</a>
							</div>
							<div id="collapse${v.num}" class="collapse" data-bs-parent="#accordion">
								<div class="card-body">
									<div class="divContBox">
										<div class="divCont1">
											작성자 <span class="wr">${v.writer}</span> 
											이메일 <span class="em">${v.mail}</span> 
											조회수 <span class="fit">${v.hit}</span> 
											날짜 <span class="date">${v.wdate}</span>
										</div>
										<div class="divCont2">
											<button idx="${v.num}" class="action btn btn-warning btn-sm" type="button" name="modify">수정</button>
											<button idx="${v.num}" class="action btn btn-danger btn-sm" type="button" name="delete">삭제</button>
										</div>
									</div>
									<div>${v.content}</div>
									<div>
										<textarea rows="2" cols="50"></textarea>
										<button style="height: 50px; vertical-align: top; padding: 0px 10px;" class="action" name="reply" type="button" idx="${v.num}">등록</button>
									</div>
								</div>
							</div>
						</div>`;
			}); // 반복문 끝
			
			code += "</div>";
			code += "</div>";
			
			$("#result").html(code);
			
			// 페이지 번호 만들기
			var paging = pageList(res.startPage, res.endPage, res.totalPage);
			
			// 출력
			$("#pageList").html(paging);
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});

} // 리스트 끝

var pageList = function(sp, ep, tp){
	
	var paging = `<ul class="pagination">`;
	
	// 이전
	if(sp > 1){
		paging += `<li class="page-item"><a id="prev" class="page-link" href="javascript:void(0)">Previous</a></li>`;
	}
	
	// 페이지 번호
	for(var i=sp; i<=ep; i++){
		if(i == currentpage){
			paging += `<li class="page-item active"><a class="page-link pageno" href="javascript:void(0)">${i}</a></li>`;
		}else{
			paging += `<li class="page-item"><a class="page-link pageno" href="javascript:void(0)">${i}</a></li>`;
		}
	}
	
	// 다음
	if(ep < tp){
		paging += `<li class="page-item"><a id="next" class="page-link" href="javascript:void(0)">Next</a></li>`;
	}
	
	paging += `</ul>`;
	
	return paging;
	
}

- http://localhost/boardpro/member/index.jsp


[ReplyDelete.java]

package kr.or.ddit.reply.controller;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import kr.or.ddit.reply.service.IReplyService;
import kr.or.ddit.reply.service.ReplyServiceImpl;


@WebServlet("/replyDelete.do")
public class ReplyDelete extends HttpServlet {
	
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		// 전송 데이터 받기 - renum
		int renum = Integer.parseInt(request.getParameter("renum"));
		// service객체
		IReplyService service = ReplyServiceImpl.getInstance();
		// service 메소드 호출 - int 결과
		int delRepCnt = service.deleteReply(renum);
		// 결과를 request에 저장
		request.setAttribute("delRepCnt", delRepCnt);
		// view 페이지 설정 - forward
		request.getRequestDispatcher("/boardview/delRepResult.jsp").forward(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

[delRepResult.jsp]

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	// 서블릿에서 저장한 데이터 꺼내기
	int delRepCnt = (int) request.getAttribute("delRepCnt");
	if(delRepCnt > 0){
%>
		{
			"msg" : "댓글 삭제 성공"
		}
	<%}else{%>
		{
			"msg" : "댓글 삭제 실패"
		}
<%	
	}
%>

[board.jsp]

<%@page import="com.google.gson.Gson"%>
<%@page import="kr.or.ddit.member.vo.MemberVO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page isELIgnored="true" %>
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>게시판 리스트</title>
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link href="<%= request.getContextPath() %>/css/style07.css" rel="stylesheet" />
		<script src="<%= request.getContextPath() %>/js/jquery-3.7.1.min.js"></script>
		<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
		<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
		<script defer src="<%= request.getContextPath() %>/js/script.js"></script>
		<% 
			// 로그인 상태 체크
			MemberVO mvo = (MemberVO) session.getAttribute("loginok");
			String jsonVo = null;
			Gson gson = new Gson();
			if(mvo != null) jsonVo = gson.toJson(mvo);
			/*  
				jsonVo = {
					"mem_id" : "a001",
					"mem_pass" : "asdfasdf",
					"mem_name" : "김은대"
				}
			*/
		%>
		<script>
		
			var mvo = <%= jsonVo %>; /* mvo.mem_id, mvo.mem_pass, mvo.mem_name */
			//console.log(mvo);
		
			var mypath = "<%= request.getContextPath() %>";
			var currentpage = 1;
			
			var reply = {}; // 객체형 변수, 동적으로 추가 가능. - reply.bonum, reply.name, reply.cont
			
			var idx = ""; // idx 전역변수 선언
			var reidx = ""; // reidx 전역변수 선언
			
			var target = ""; // .action의 this 전역변수
			
			$(function(){
				
				// 실행하자마자 리스트 출력 - stype, sword 없다.
				$.listBoardServer(currentpage);
				
				// 페이지 번호 클릭 이벤트
				$(document).on("click", ".pageno", function(){
					currentpage = parseInt( $(this).text().trim() );
					$.listBoardServer(currentpage);
				})
				
				// 다음 클릭 이벤트
				$(document).on("click", "#next", function(){
					currentpage = parseInt( $(".pageno").last().text().trim() ) + 1;
					$.listBoardServer(currentpage);
				});
				
				// 이전 클릭 이벤트
				$(document).on("click", "#prev", function(){
					currentpage = parseInt( $(".pageno").first().text().trim() ) - 1;
					$.listBoardServer(currentpage);
				});
				
				// Search 검색 이벤트
				$(document).on("click", "#search", function(){
					currentpage = 1;
					$.listBoardServer(currentpage);
				});
				
				// 게시글 수정, 게시글 삭제, 댓글 등록, 제목 클릭, 댓글 삭제, 댓글 수정 이벤트
				$(document).on("click", ".action", function(){
					var thisIs = $(this);
					target = thisIs;
					var name = thisIs.attr("name");
					idx = thisIs.attr("idx");
					reidx = thisIs.attr("reidx");
					
					if(name == "delete"){
						//alert(idx + "번 글을 삭제합니다.");
					}else if(name == "title"){
						//alert(idx + "번 글의 댓글을 출력합니다.");
						
						// 댓글 출력 ajax 수행
						$.replyList();
					}else if(name == "modify"){
						//alert(idx + "번 글을 수정합니다.");
					}else if(name == "reply"){
						//alert(idx + "번 댓글을 등록합니다.");
						
						//입력한 내용 가져오기
						var cont = thisIs.prev().val();
						//console.log(cont);
						
						reply.name = mvo.mem_name;
						reply.bonum = idx;
						reply.cont = cont;
						//console.log(reply);
						
						// 댓글 입력 ajax 수행
						$.replyInsert();
						
						// 댓글을 화면에 추가
						// $.replyInsert(); ==> success에서 수행
						
						// 입력 값 비우기
						thisIs.prev().val("");
					}else if(name == "rep_modify"){
						//alert(reidx + "번 댓글을 수정합니다.");
						
					}else if(name == "rep_delete"){
						//alert(reidx + "번 댓글을 삭제합니다.");
						
						// ajax수행 - 댓글 삭제
						$.replyRemove();
					}
				});
				
			});
			
		</script>
	</head>
	<body>
	
		<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
		  <div class="container-fluid">
		    <a class="navbar-brand" href="javascript:void(0)">Logo</a>
		    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mynavbar">
		      <span class="navbar-toggler-icon"></span>
		    </button>
		    <div class="collapse navbar-collapse" id="mynavbar">
		      <ul class="navbar-nav me-auto">
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		        <li class="nav-item">
		          <a class="nav-link" href="javascript:void(0)">Link</a>
		        </li>
		      </ul>
		      <form class="d-flex">
		      	<select class="form-select" id="stype" name="stype" style="margin-right: 5px;">
		      		<option value="">전체</option>
		      		<option value="writer">작성자</option>
		      		<option value="subject">제목</option>
		      		<option value="content">내용</option>
		      	</select>
		        <input class="form-control me-2" id="sword" name="sword" type="text" placeholder="Search">
		        <button id="search" class="btn btn-primary" type="button">Search</button>
		      </form>
		    </div>
		  </div>
		</nav>
		
		<br /><br />
		
		<div id="result"></div>
		<br />
		<br />
		<div id="pageList"></div>
		
	</body>
</html>

[script.js]

// 댓글 삭제
$.replyRemove = function(){
	$.ajax({
		url : `${mypath}/replyDelete.do`,
		type : 'post',
		data : { "renum" : reidx },
		success : function(res){
			
			//console.log(res);
			
			// DB 삭제 성공 했으면 화면에서 삭제
			target.parents(".reply-body").remove();
			
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

// 댓글 리스트 가져오기
$.replyList = function(){
	$.ajax({
		url : `${mypath}/replyList.do`,
		type : 'post',
		data : { "bonum" : idx },
		success : function(res){
			
			//console.log(res);
			
			var recode = "";
			
			$.each(res, function(i, v){
				var cont = v.cont;
				cont = cont.replaceAll(/\n/g, "<br />");
				recode += `<div class="reply-body">
							<div class="divContBox">
								<div class="divCont1">
									작성자 <span class="wr">${v.name}</span> 
									날짜 <span class="date">${v.redate}</span>
								</div>
								<div class="divCont2">
									<button reidx="${v.renum}" class="action btn btn-warning btn-sm" type="button" name="rep_modify">댓글수정</button>
									<button reidx="${v.renum}" class="action btn btn-danger btn-sm" type="button" name="rep_delete">댓글삭제</button>
								</div>
							</div>
							<div>${cont}</div>
						</div>`;
			});
			
			// 댓글 출력
			//target.parent().next().find(".card-body").append(recode);
			//target.parents(".card").find(".reply-body").empty();
			target.parents(".card").find(".reply-body").remove();
			target.parents(".card").find(".card-body").append(recode);
			
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

// 댓글 저장
$.replyInsert = function(){
	$.ajax({
		url : `${mypath}/replyInsert.do`,
		type : 'post',
		data : reply, /* name, bonum, cont */
		success : function(res){
			
			//console.log(res);
			
			// 댓글을 화면에 추가하기 위해서
			// 댓글 리스트 가져오기를 수행한다.
			$.replyList();
			
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

$.listBoardServer = function(cpage){
	
	var stype = $("#stype option:selected").val().trim();
	var sword = $("#sword").val().trim();
	
	//console.log("stype : " + stype);
	//console.log("sword : " + sword);
	
	// 실행하자마자 리스트 출력
	$.ajax({
		url : `${mypath}/boardList.do`,
		type : 'post',
		data : {
			'page' : cpage,
			'stype' : stype,
			'sword' : sword
		},
		success : function(res){
			
			var code = "<div class='container mt-3'>";
			code += "<div id='accordion'>";
			
			$.each(res.data, function(i, v){
				code += `<div class="card">
							<div class="card-header">
								<a class="btn action" name="title" idx="${v.num}" data-bs-toggle="collapse" href="#collapse${v.num}">
									${v.subject}
								</a>
							</div>
							<div id="collapse${v.num}" class="collapse" data-bs-parent="#accordion">
								<div class="card-body">
									<div class="divContBox">
										<div class="divCont1">
											작성자 <span class="wr">${v.writer}</span> 
											이메일 <span class="em">${v.mail}</span> 
											조회수 <span class="fit">${v.hit}</span> 
											날짜 <span class="date">${v.wdate}</span>
										</div>
										<div class="divCont2">
											<button idx="${v.num}" class="action btn btn-warning btn-sm" type="button" name="modify">수정</button>
											<button idx="${v.num}" class="action btn btn-danger btn-sm" type="button" name="delete">삭제</button>
										</div>
									</div>
									<div>${v.content}</div>
									<div>
										<textarea rows="2" cols="50"></textarea>
										<button style="height: 50px; vertical-align: top; padding: 0px 10px;" class="action" name="reply" type="button" idx="${v.num}">등록</button>
									</div>
								</div>
							</div>
						</div>`;
			}); // 반복문 끝
			
			code += "</div>";
			code += "</div>";
			
			$("#result").html(code);
			
			// 페이지 번호 만들기
			var paging = pageList(res.startPage, res.endPage, res.totalPage);
			
			// 출력
			$("#pageList").html(paging);
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});

} // 리스트 끝

var pageList = function(sp, ep, tp){
	
	var paging = `<ul class="pagination">`;
	
	// 이전
	if(sp > 1){
		paging += `<li class="page-item"><a id="prev" class="page-link" href="javascript:void(0)">Previous</a></li>`;
	}
	
	// 페이지 번호
	for(var i=sp; i<=ep; i++){
		if(i == currentpage){
			paging += `<li class="page-item active"><a class="page-link pageno" href="javascript:void(0)">${i}</a></li>`;
		}else{
			paging += `<li class="page-item"><a class="page-link pageno" href="javascript:void(0)">${i}</a></li>`;
		}
	}
	
	// 다음
	if(ep < tp){
		paging += `<li class="page-item"><a id="next" class="page-link" href="javascript:void(0)">Next</a></li>`;
	}
	
	paging += `</ul>`;
	
	return paging;
	
}

- http://localhost/boardpro/member/index.jsp

댓글 삭제 전...


비동기식으로 댓글 삭제됨...
댓글 삭제 후...


 

'대덕인재개발원 > 대덕인재개발원_Front End' 카테고리의 다른 글

231013_AJAX 강의  (0) 2023.10.13
231012_AJAX 강의  (0) 2023.10.12
231010_AJAX 강의  (0) 2023.10.10
231006_AJAX 강의  (0) 2023.10.06
231005_AJAX 강의  (0) 2023.10.05