관리 메뉴

거니의 velog

231013_AJAX 강의 본문

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

231013_AJAX 강의

Unlimited00 2023. 10. 13. 08:21

[UpdateHit.java]

package kr.or.ddit.board.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.board.service.BoardServiceImpl;
import kr.or.ddit.board.service.IBoardService;


@WebServlet("/updateHit.do")
public class UpdateHit 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"));
		
		// 서비스 호출
		IBoardService service = BoardServiceImpl.getInstance();
		int result = service.updateHit(bonum);
		
		// 결과값을 request에 저장
		// result를 키 값으로 /boardview/result.jsp에 공통 키값으로 넘길 것이다(서블릿 변환 과정을 줄이기 위함).
		request.setAttribute("result", result);
		
		// view 페이지 설정 - forward
		request.getRequestDispatcher("/boardview/result.jsp").forward(request, response);
		
	}

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

}

[script.js]

// 게시판 조회수 증가
$.updateHit = function(){
	$.ajax({
		url : `${mypath}/updateHit.do`,
		type : 'post',
		data : { "bonum" : idx },
		success : function(res){
			if(res.msg == "성공"){
				// 화면부분의 조회수를 변경시킨다.
				// 공통조상
				var hitTxt = target.parents(".card").find(".fit").text();
				var hitInt = parseInt(hitTxt);
				hitInt = hitInt + 1;
				target.parents(".card").find(".fit").text(hitInt);
			}
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

// 게시판 글 삭제
$.deleteBoard = function(){
	$.ajax({
		url : `${mypath}/boardDelete.do`,
		type : 'post',
		data : { "bonum" : idx },
		success : function(res){
			if(res.msg == "성공"){
				$.listBoardServer(currentpage);
			}
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

// 게시판 글 저장
$.insertBoard = function(){
	$.ajax({
		url : `${mypath}/boardInsert.do`,
		type : 'post',
		data : datas,
		success : function(res){
			if(res.msg == "성공"){
				currentpage = 1;
				$.listBoardServer(currentpage);
			}
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

// 댓글 삭제
$.replyRemove = function(){
	$.ajax({
		url : `${mypath}/replyDelete.do`,
		type : 'post',
		data : { "renum" : reidx },
		success : function(res){
			
			//console.log(res);
			
			// DB 삭제 성공 했으면 화면에서 삭제
			if(res.msg == "댓글 삭제 성공"){
				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>`;
								
								if(mvo != null && (mvo.mem_name == v.name)){
									recode +=`<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>`;
								}
								
					recode += `</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){
				
				var cont = v.content;
				cont = cont.replaceAll(/\n/g, "<br />");
				
				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>`;
										
										if(mvo != null && (mvo.mem_name == v.writer)) {
											code += `<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>`;
										}
										
							code += `</div>
									<div>${cont}</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>`;
	}
	
	// 페이지 번호
	
	// currentpage는 게시판 글 삭제시 변동값이 된다.
	// 이때 비교값을 currentpage로 하지 않고 tp = res.totalPage;로 변경한다.
	
	// 마지막 페이지-currentpage : 8에 한 개의 데이터만 남아있고,
	// 그 한개의 데이터를 지웠을 때 현재 페이지 번호-currentpage : 8는 없는 번호가 된다.
	if(currentpage > tp) {currentpage = tp;}
	
	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;
	
}

[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 + "번 글을 삭제합니다.");
						
						console.log("idx : " + idx);
						console.log("reidx : " + reidx);
						
						//ajax 실행
						//$.deleteBoard();
						//$.replyRemove();
					}else if(name == "title"){
						//alert(idx + "번 글의 댓글을 출력합니다.");
						
						// 댓글 출력 ajax 수행
						$.replyList();
						
						// aria-expanded
						var areaex = thisIs.attr("aria-expanded");
						if(areaex == "true"){
							//alert("조회수 증가");
							$.updateHit();
						}
					}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();
					}
				});
				
				// 게시판 글 쓰기 이벤트
				$("#boardWriteBtn").on("click", function(){
					
					// 로그인 체크
					if(mvo == null){
						alert("로그인 후 사용 가능합니다.");
						return false;
					}
					
					$("#boardWriteModal").modal("show");
					
					// 로그인한 사람의 이름을 모달 창에 띄운다.
					$("#bWriter").val(mvo.mem_name);
					//$("#bWriter").attr("readonly", "readonly");
					$("#bWriter").prop("readonly", true);
					//$("#bWriter").prop("disabled", true);
					
				}); // 글쓰기 끝...
				
				// 모달창에서 글쓰기 후에 submit 이벤트
				$("#boardSubmit").on("click", function(){
					
					// 입력한 모든 값을 가져와야 한다.
					/* var bWriter = $("#bWriter").val();
					var bSubject = $("#bSubject").val();
					var bPassword = $("#bPassword").val();
					var bEmail = $("#bEmail").val();
					var bContent = $("#bContent").val();
					// data만들기
					var data = {
						"writer" : bWriter,
						"subject" : bSubject,
						"password" : bPassword,
						"mail" : bEmail,
						"content" : bContent
					} */
					
					// 입력한 모든 값을 가져와야 한다. - data 만들기
					datas = $("#bWriteForm").serializeArray();
					//var datas = $("#bWriteForm").serialize();
					//console.log(datas);
					
					// ajax실행 - 외부 js에서 만들기
					$.insertBoard();
					
					// 모달창 닫기
					$("#boardWriteModal").modal("hide");
					
					// 모달창 입력 내용 지우기
					$("#boardWriteModal .txt").val("");
					
				}); // submit 종료...
				
			}); // document 이벤트 종료
			
		</script>
	</head>
	<body>
		<!-- <button id="boardWriteBtn" type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#boardWriteModal">글쓰기</button> -->
		<button id="boardWriteBtn" type="button" class="btn btn-primary">글쓰기</button>
		<br /><br /><br />
	
		<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>
		
		<!-- 모달창 시작 -->
		<div class="modal fade" id="boardWriteModal">
		  <div class="modal-dialog modal-lg">
		    <div class="modal-content">
		
		      <!-- Modal Header -->
		      <div class="modal-header">
		        <h4 class="modal-title">게시판 글 작성</h4>
		        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
		      </div>
		
		      <!-- Modal body -->
		      <div class="modal-body">
			      <form id="bWriteForm" name="bWriteForm">
			      	<label for="bWriter" class="form-label">작성자</label>
			      	<input type="text" id="bWriter" name="writer" class="txt form-control" />
			      	<br />
			      	<label for="bSubject" class="form-label">제목</label>
			      	<input type="text" id="bSubject" name="subject" class="txt form-control" />
			      	<br />
			      	<label for="bPassword" class="form-label">비밀번호</label>
			      	<input type="password" id="bPassword" name="password" class="txt form-control" />
			      	<br />
			      	<label for="bEmail" class="form-label">이메일</label>
			      	<input type="text" id="bEmail" name="mail" class="txt form-control" />
			      	<br />
			      	<label for="bCont" class="form-label">내용</label>
			      	<textarea id="bContent" name="content" rows="10" cols="50" class="txt form-control"></textarea>
			      	<br />
			      	<button id="boardSubmit" class="btn btn-info" type="button">전송</button>
			      </form>
		      </div>
		
		      <!-- Modal footer -->
		      <div class="modal-footer">
		        <button type="button" class="btn btn-danger" data-bs-dismiss="modal">창 닫기</button>
		      </div>
		
		    </div>
		  </div>
		</div>
		<!-- 모달창 끝 -->
		
	</body>
</html>

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

클릭할 때마다 자동으로 조회수가 1씩 증가한다.
DB에도 실시간으로 잘 반영된다.


새로운 라이브러리 추가
BeanUtils.populate() 메서드를 사용하려면 input name과 vo 멤버변수의 이름이 일치해야 한다.

[UpdateBoard.java]

package kr.or.ddit.board.controller;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

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 org.apache.commons.beanutils.BeanUtils;

import kr.or.ddit.board.service.BoardServiceImpl;
import kr.or.ddit.board.service.IBoardService;
import kr.or.ddit.board.vo.BoardVO;


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

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		request.setCharacterEncoding("utf-8");
		
		// 전송 데이터 받기 - vo에 저장하기.
		BoardVO bvo = new BoardVO();
		
		// BeanUtils.populate() 메서드로 화면에서 request.getParameter로 받을 값들을 한번에 vo에 세팅.
		try {
			BeanUtils.populate(bvo, request.getParameterMap());
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
		bvo.setWip(request.getRemoteAddr());
		
		System.out.println("bvo : " + bvo.toString());
		
		// service 객체 -> 메소드 실행
		IBoardService service = BoardServiceImpl.getInstance();
		int result = service.updateBoard(bvo);
		
		// 결과값을 request에 저장
		// result를 키 값으로 /boardview/result.jsp에 공통 키값으로 넘길 것이다(서블릿 변환 과정을 줄이기 위함).
		request.setAttribute("result", result);
		
		// view 페이지 설정 - forward
		
		request.getRequestDispatcher("/boardview/result.jsp").forward(request, response);
	}

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

}

js 추가

[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>
		<script src="<%= request.getContextPath() %>/js/jquery.serializejson.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 + "번 글을 삭제합니다.");
						
						console.log("idx : " + idx);
						console.log("reidx : " + reidx);
						
						//ajax 실행
						$.deleteBoard();
					}else if(name == "title"){
						//alert(idx + "번 글의 댓글을 출력합니다.");
						
						// 댓글 출력 ajax 수행
						$.replyList();
						
						// aria-expanded
						var areaex = thisIs.attr("aria-expanded");
						if(areaex == "true"){
							//alert("조회수 증가");
							$.updateHit();
						}
					}else if(name == "modify"){
						//alert(idx + "번 글을 수정합니다.");
						
						$("#boardModifyModal").modal("show");
						
						var parents = thisIs.parents(".card");
						var modWriter = parents.find(".wr").text().trim();
						var modTitle = parents.find("a").text().trim();
						var modPass = parents.find(".boUpdPass").text().trim();
						var modEmail = parents.find(".em").text().trim();
						var modCont = parents.find(".divCont3").html(); // <br /> 태그가 포함
						modCont = modCont.replace(/<br>/g, "");
						
						// 수정 모달 창에 출력하기
						$("#boardModifyModal #bModWriter").val(modWriter);
						$("#boardModifyModal #bModSubject").val(modTitle);
						$("#boardModifyModal #bModPassword").val(modPass);
						$("#boardModifyModal #bModEmail").val(modEmail);
						$("#boardModifyModal #bModCont").html(modCont);
						// 글번호를 hidden으로 모달창에 설정
						$("#boardModifyModal #bModBonum").val(idx);
						
						$("#boardModifyModal #bModWriter").prop("readonly", true);
						
					}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();
					}
				});
				
				// 게시판 글 쓰기 이벤트
				$("#boardWriteBtn").on("click", function(){
					
					// 로그인 체크
					if(mvo == null){
						alert("로그인 후 사용 가능합니다.");
						return false;
					}
					
					$("#boardWriteModal").modal("show");
					
					// 로그인한 사람의 이름을 모달 창에 띄운다.
					$("#bWriter").val(mvo.mem_name);
					//$("#bWriter").attr("readonly", "readonly");
					$("#bWriter").prop("readonly", true);
					//$("#bWriter").prop("disabled", true);
					
				}); // 글쓰기 끝...
				
				// 모달창에서 글쓰기 후에 submit 이벤트
				$("#boardSubmit").on("click", function(){
					
					// 입력한 모든 값을 가져와야 한다.
					/* var bWriter = $("#bWriter").val();
					var bSubject = $("#bSubject").val();
					var bPassword = $("#bPassword").val();
					var bEmail = $("#bEmail").val();
					var bContent = $("#bContent").val();
					// data만들기
					var data = {
						"writer" : bWriter,
						"subject" : bSubject,
						"password" : bPassword,
						"mail" : bEmail,
						"content" : bContent
					} */
					
					// 입력한 모든 값을 가져와야 한다. - data 만들기
					datas = $("#bWriteForm").serializeArray();
					//var datas = $("#bWriteForm").serialize();
					//console.log(datas);
					
					// ajax실행 - 외부 js에서 만들기
					$.insertBoard();
					
					// 모달창 닫기
					$("#boardWriteModal").modal("hide");
					
					// 모달창 입력 내용 지우기
					$("#boardWriteModal .txt").val("");
					
				}); // submit 종료...
				
				// 값을 변경하여 입력하고 전송버튼 클릭 - 이벤트
				$("#boardModifyModal #boardModSubmit").on("click", function(){
					// 수정입력된 모든 내용을 가져온다.
					//datas2 = $("#bModifyForm").serializeArray();
					datas2 = $("#bModifyForm").serializeJSON();
					console.log(datas2);
					
					// ajax - 게시판 DB 수정 완료 성공하면 datas2의 내용을 본문의 내용을 바꾼다.
					$.updateBoard();
					
					// 모달창 닫기
					$("#boardModifyModal").modal("hide");
					
					// 모달창 입력 내용 지우기
					$("#boardModifyModal .txt").val("");
				});
				
			}); // document 이벤트 종료
			
		</script>
	</head>
	<body>
		<!-- <button id="boardWriteBtn" type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#boardWriteModal">글쓰기</button> -->
		<button id="boardWriteBtn" type="button" class="btn btn-primary">글쓰기</button>
		<br /><br /><br />
	
		<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>
		
		<!-- 게시글 글 쓰기 모달창 시작 -->
		<div class="modal fade" id="boardWriteModal">
		  <div class="modal-dialog modal-lg">
		    <div class="modal-content">
		
		      <!-- Modal Header -->
		      <div class="modal-header">
		        <h4 class="modal-title">게시판 글 작성</h4>
		        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
		      </div>
		
		      <!-- Modal body -->
		      <div class="modal-body">
			      <form id="bWriteForm" name="bWriteForm">
			      	<label for="bWriter" class="form-label">작성자</label>
			      	<input type="text" id="bWriter" name="writer" class="txt form-control" />
			      	<br />
			      	<label for="bSubject" class="form-label">제목</label>
			      	<input type="text" id="bSubject" name="subject" class="txt form-control" />
			      	<br />
			      	<label for="bPassword" class="form-label">비밀번호</label>
			      	<input type="password" id="bPassword" name="password" class="txt form-control" />
			      	<br />
			      	<label for="bEmail" class="form-label">이메일</label>
			      	<input type="text" id="bEmail" name="mail" class="txt form-control" />
			      	<br />
			      	<label for="bCont" class="form-label">내용</label>
			      	<textarea id="bContent" name="content" rows="10" cols="50" class="txt form-control"></textarea>
			      	<br />
			      	<button id="boardSubmit" class="btn btn-info" type="button">전송</button>
			      </form>
		      </div>
		
		      <!-- Modal footer -->
		      <div class="modal-footer">
		        <button type="button" class="btn btn-danger" data-bs-dismiss="modal">창 닫기</button>
		      </div>
		
		    </div>
		  </div>
		</div>
		<!-- 게시글 글 쓰기 모달창 끝 -->
		
		<!-- 게시글 글 수정 모달창 시작 -->
		<div class="modal fade" id="boardModifyModal">
		  <div class="modal-dialog modal-lg">
		    <div class="modal-content">
		
		      <!-- Modal Header -->
		      <div class="modal-header">
		        <h4 class="modal-title">게시판 글 수정</h4>
		        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
		      </div>
		
		      <!-- Modal body -->
		      <div class="modal-body">
			      <form id="bModifyForm" name="bModifyForm">
			      	<label for="bModWriter" class="form-label">작성자</label>
			      	<input type="text" id="bModWriter" name="writer" class="txt form-control" />
			      	<br />
			      	<label for="bModSubject" class="form-label">제목</label>
			      	<input type="text" id="bModSubject" name="subject" class="txt form-control" />
			      	<br />
			      	<label for="bModPassword" class="form-label">비밀번호</label>
			      	<input type="password" id="bModPassword" name="password" class="txt form-control" />
			      	<br />
			      	<label for="bModEmail" class="form-label">이메일</label>
			      	<input type="text" id="bModEmail" name="mail" class="txt form-control" />
			      	<br />
			      	<label for="bModCont" class="form-label">내용</label>
			      	<textarea id="bModCont" name="content" rows="10" cols="50" class="txt form-control"></textarea>
			      	<br />
			      	<input id="bModBonum" class="txt" type="hidden" name="num" />
			      	<button id="boardModSubmit" class="btn btn-info" type="button">전송</button>
			      </form>
		      </div>
		
		      <!-- Modal footer -->
		      <div class="modal-footer">
		        <button type="button" class="btn btn-danger" data-bs-dismiss="modal">창 닫기</button>
		      </div>
		
		    </div>
		  </div>
		</div>
		<!-- 게시글 글 수정 모달창 끝 -->
		
	</body>
</html>

[script.js]

// 게시판 글 수정
$.updateBoard = function(){
	$.ajax({
		url : `${mypath}/updateBoard.do`,
		type : 'post',
		data : datas2,
		success : function(res){
			if(res.msg == "성공"){
				// data2의 내용으로 화면의 본문 내용을 변경한다.
				var parents = target.parents(".card");
				parents.find(".wr").text(datas2.writer);
				parents.find("a").text(datas2.subject);
				parents.find(".boUpdPass").text(datas2.password);
				parents.find(".em").text(datas2.mail);
				var cont = datas2.content; // \r\n
				cont = cont.replace(/\n/g, "<br>");
				parents.find(".divCont3").html(cont); // <br /> 태그가 포함
				//parents.find(".divCont3").html(datas2.content); // <br /> 태그가 포함
			}
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

// 게시판 조회수 증가
$.updateHit = function(){
	$.ajax({
		url : `${mypath}/updateHit.do`,
		type : 'post',
		data : { "bonum" : idx },
		success : function(res){
			if(res.msg == "성공"){
				// 화면부분의 조회수를 변경시킨다.
				// 공통조상
				var hitTxt = target.parents(".card").find(".fit").text();
				var hitInt = parseInt(hitTxt);
				hitInt = hitInt + 1;
				target.parents(".card").find(".fit").text(hitInt);
			}
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

// 게시판 글 삭제
$.deleteBoard = function(){
	$.ajax({
		url : `${mypath}/boardDelete.do`,
		type : 'post',
		data : { "bonum" : idx },
		success : function(res){
			if(res.msg == "성공"){
				$.listBoardServer(currentpage);
			}
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

// 게시판 글 저장
$.insertBoard = function(){
	$.ajax({
		url : `${mypath}/boardInsert.do`,
		type : 'post',
		data : datas,
		success : function(res){
			if(res.msg == "성공"){
				currentpage = 1;
				$.listBoardServer(currentpage);
			}
		},
		error : function(xhr){
			alert("상태 : " + xhr.status);
		},
		dataType : 'json'
	});
}

// 댓글 삭제
$.replyRemove = function(){
	$.ajax({
		url : `${mypath}/replyDelete.do`,
		type : 'post',
		data : { "renum" : reidx },
		success : function(res){
			
			//console.log(res);
			
			// DB 삭제 성공 했으면 화면에서 삭제
			if(res.msg == "댓글 삭제 성공"){
				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>`;
								
								if(mvo != null && (mvo.mem_name == v.name)){
									recode +=`<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>`;
								}
								
					recode += `</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){
				
				var cont = v.content;
				cont = cont.replaceAll(/\n/g, "<br />");
				
				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>
											<span class="boUpdPass" style="display: none;">${v.password}</span>
										</div>`;
										
										if(mvo != null && (mvo.mem_name == v.writer)) {
											code += `<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>`;
										}
										
							code += `</div>
									<div class="divCont3">${cont}</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>`;
	}
	
	// 페이지 번호
	
	// currentpage는 게시판 글 삭제시 변동값이 된다.
	// 이때 비교값을 currentpage로 하지 않고 tp = res.totalPage;로 변경한다.
	
	// 마지막 페이지-currentpage : 8에 한 개의 데이터만 남아있고,
	// 그 한개의 데이터를 지웠을 때 현재 페이지 번호-currentpage : 8는 없는 번호가 된다.
	if(currentpage > tp) {currentpage = tp;}
	
	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' 카테고리의 다른 글

231016_AJAX 강의  (0) 2023.10.16
231012_AJAX 강의  (0) 2023.10.12
231011_AJAX 강의  (0) 2023.10.11
231010_AJAX 강의  (0) 2023.10.10
231006_AJAX 강의  (0) 2023.10.06