DB테이블

  • r_depth = 댓글의 깊이( 댓글 0 , 답글 1 로 구별하기 위해서)
  • r_group =  댓글이 속한 번호

 

<댓글부분>

Controller

 - @RestController을 이용한 방식입니다.

이방식은 @Controller 사용하는 대신 @RequestController을 사용해야 하기 때문에 새로운 클래스를 생성하였습니다.

  • 게시판 상세보기 메서드에 @ModelAttribute("replyVO")ReplyVO replyVO 어노테이션을 추가해서 jsp에서 사용하기 위해서 선언을 하였다
// 댓글목록
	@Override
	@RequestMapping(value = "/getReplyList.do", method = RequestMethod.POST)
	public List<ReplyVO> getReplyList(@RequestParam("bno") int bno) throws Exception {
		boardService.UpdateReplyCount(bno);
		return replyService.getReplyList(bno);
	}

//작성
    @Override
	@RequestMapping(value = "/addReply.do", method = RequestMethod.POST)
	public Map<String, Object> addReply(@RequestBody ReplyVO replyVO) throws Exception {
		Map<String, Object> result = new HashMap<>();

		try {
			replyService.addReply(replyVO);
			result.put("status", "OK");
		} catch (Exception e) {
			e.printStackTrace();
			result.put("status", "false");
		}

		return result;
	}
  
  //수정
  	@Override
	@RequestMapping(value = "/updateReply.do", method = RequestMethod.POST)
	public Map<String, Object> updateReply(@RequestBody ReplyVO replyVO) throws Exception {
		Map<String, Object> result = new HashMap<>();

		try {
			replyService.updateReply(replyVO);
			result.put("status", "ok");
		} catch (Exception e) {
			e.printStackTrace();
			result.put("status", "false");
		}

		return result;
	}
	
    //삭제
    	@Override
	@RequestMapping(value="deleteReply.do" , method=RequestMethod.POST)
	public Map<String,Object>deleteReply(@RequestParam("rno")int rno)throws Exception{
		Map<String,Object>result = new HashMap<>();
		
		try {
			replyService.deleteReply(rno);
			result.put("status", "ok");
		}catch(Exception e) {
			e.printStackTrace();
			result.put("status", "false");
		}
		return result;
	}

Service

	//댓글리스트
	@Override
	public List<ReplyVO> getReplyList(int bno)throws Exception{
		return replyDAO.getReplyList(bno);
	}
    	//댓글작성
	@Override
	public int addReply(ReplyVO replyVO)throws Exception{
		return replyDAO.addReply(replyVO);
	}
	
	//댓글수정
	@Override
	public int updateReply(ReplyVO replyVO)throws Exception{
		return replyDAO.updateReply(replyVO);
	}
	
	//댓글삭제
	@Override
	public int deleteReply(int rno)throws Exception{
		return replyDAO.deleteReply(rno);
	}

 

DAO

 

	// 댓글 리스트
	@Override
	public List<ReplyVO> getReplyList(int bno) throws DataAccessException {
		return session.selectList("mapper.reply.getReplyList", bno);
	}
    	// 댓글 작성
	@Override
	public int addReply(ReplyVO replyVO) throws DataAccessException {	
		
		int result = session.insert("mapper.reply.addReply", replyVO);
		
		//댓글이 작성되면 댓글의 그룹번호 r_group를 rno와 같게 값을 넣기위해 선언
        //답글이 달리면 답글을 작성한 댓글과 묶기 위해
		if(result == 1) {
			int check_update =  session.update("mapper.reply.Re_group",replyVO);
			replyVO.setR_group(check_update);
		}
		
		return result;
	}

	// 댓글 수정
	@Override
	public int updateReply(ReplyVO replyVO) throws DataAccessException {
		return session.update("mapper.reply.updateReply", replyVO);
	}

	// 댓글 삭제
	@Override
	public int deleteReply(int rno) throws DataAccessException {
		return session.delete("mapper.reply.deleteReply", rno);
	}

 

mapper(Oracle)

//목록
<select id="getReplyList" resultMap="ReplyResult">
	<![CDATA[
		select * 
		from b_reply
		where bno = #{bno}
		order by r_group asc , r_depth asc, rno desc
	]]>
</select>

//작성
<insert id="addReply" parameterType="ReplyVO">
	<selectKey resultType="int" keyProperty="rno" order="BEFORE">
		select seq_reply.nextval from dual
	</selectKey>
<![CDATA[
	insert into b_reply(rno, bno, content, writer,r_group)
	values(#{rno},#{bno},#{content},#{writer},#{r_group})
]]>
</insert>

//수정
<update id="updateReply" parameterType="ReplyVO">
	<![CDATA[
		update b_reply 
		set content = #{content}
		where rno = #{rno}
	]]>
</update>

//삭제
<delete id="deleteReply" parameterType="int">
	<![CDATA[
		delete from b_reply
		where rno = #{rno}
	]]>
</delete>

<!-- 부모글 r_group 를 rno와 같게 셋팅 -->
<update id="Re_group" parameterType="ReplyVO">
	<![CDATA[
		update b_reply
		set r_group = rno
		where r_group != rno
		and r_depth = 0
	]]>
</update>

<답글부분>

Controller

	// 답글 작성
	@RequestMapping(value = "write_rereply.do", method = RequestMethod.POST)
	public Map<String,Object> write_rereply(@RequestBody ReplyVO replyVO)throws Exception {
		Map<String,Object>result = new HashMap<>();
		
		//댓글에 답글을 작성할 때 r_group를 댓글 r_group를 가져오기위해 변수를 선언하여 get,set을 하였음.
		//그리고 r_depth 즉 답글이라는 것을 알기 위해 r_depth를 1로 설정하여 답글이 작성되면 자동적으로 1이 데이터값에 들어가기위해 선언
		int rno = replyVO.getRno();		
		
		replyVO.setR_group(rno);		
		replyVO.setR_depth(1);
		try {
			replyService.WriteReReply(replyVO);	
			result.put("status", "ok");
		}catch(Exception e) {
			e.printStackTrace();
			result.put("status", "false");
		}	
		return result;
	}
	
	//답글 수정
	@RequestMapping(value = "update_rereply.do", method = RequestMethod.POST)
	public Map<String,Object> update_rereply(@RequestBody ReplyVO replyVO)throws Exception {
		Map<String,Object>result = new HashMap<>();
		
		//답글 수정을 하기위해 r_group, r_depth를 get, set 하여 일반 댓글과 구분지어 수정이 되게 하였음
		int r_group = replyVO.getR_group();
		int r_depth = replyVO.getR_depth();
	
		replyVO.setR_depth(r_depth);
		replyVO.setR_group(r_group);
		
		try {
			replyService.UpdateReReply(replyVO);	
			result.put("status", "ok");
		}catch(Exception e) {
			e.printStackTrace();
			result.put("status", "false");
		}	
		return result;
	}

Service

	//답글 등록
	@Override
	public int WriteReReply(ReplyVO replyVO)throws Exception{
		return replyDAO.WriteReReply(replyVO);
	}
	
	//답글 수정
	@Override
	public int UpdateReReply(ReplyVO replyVO)throws Exception{
		return replyDAO.updateReply(replyVO);
	}

DAO

	// 답글 작성	
	@Override
	public int WriteReReply(ReplyVO replyVO) throws DataAccessException{
		return session.insert("mapper.reply.ReRePly_write", replyVO);	
		
	}
	
	//답글 수정
	@Override
	public int UpdateReReply(ReplyVO replyVO) throws DataAccessException {
		return session.update("mapper.reply.ReReply_update", replyVO);
	}

Mapper(Oracle)

<!-- 답글 작성 -->
<insert id="ReRePly_write" parameterType="ReplyVO">
	<selectKey resultType="int" keyProperty="rno" order="BEFORE">
		select seq_reply.nextval from dual
	</selectKey>
<![CDATA[
	insert into b_reply(rno, bno, content, writer, r_depth,r_group)
	values(#{rno},#{bno},#{content},#{writer},#{r_depth},#{r_group})
]]>
</insert>

//수정
<update id="ReReply_update" parameterType="ReplyVO">
	<![CDATA[
		update b_reply 
		set content = #{content}
		where rno = #{rno} and r_depth = #{r_depth}
	]]>
</update>

JSP

//목록 부분..
<div class="Reply" style="padding-top: 10px">			
	<h3 class= "ReplyList">댓글</h3>	
	<div id="replyList"></div>				
</div>

//작성부분..
	<br>
		<div class="my-3 p-3 bg-white rounded shadow-sm" style="padding-top: 10px">				
				<form:form name="form" id="form" role="form" modelAttribute="replyVO" method="post">				
				<form:hidden path="bno" id="bno"/>				
			
			<div class="col-sm-2">						
				<form:input path="writer" class="form-control" id="writer" value="${memberVO.id }" type="hidden" />				
			</div>				

			<div class="row">					
				<div class="col-sm-10">						
					<form:textarea path="content" id="content" class="form-control" rows="3" placeholder="댓글을 입력해 주세요" />		
				<c:if test="${not empty memberVO}">	
					<button type="button"id="btnReplyAdd">등록</button>		
				</c:if>				
			</div>									
			</div>				
				</form:form>			
			</div>

 

Script

$(document).ready(function(){ getReplyList(); }):

이 부분은 페이자가 완전히 로딩되면 호출되는 이벤트 부분이다. 따라서 조회 페이지가 로딩이 되고 나면 getReplyList()

함수를 싱행하게 된다

//댓글 리스트
$(document).ready(function(){
	getReplyList();
});

function getReplyList(){
	var url = "${Path}/reply/getReplyList.do";
	var paramData = {"bno": "${board.bno}"};
	var writer = $('#writer').val();
	var id = '${memberVO.id}';
	$.ajax({
		type:'POST',
		url:url,
		data:paramData,
		dataType:'json',
		success: function(data){
			//console.log("댓글 리스트 받아졌나?");
			
			var htmls = "";		
		 
			for(const i in data){//list를 받기위해 for문 사용
			 let rno = data[i].rno;
			 let bno = data[i].bno;
			 let content = data[i].content;
			 let writer = data[i].writer;
			 let reg_date = data[i].reg_date;
			 let r_depth = data[i].r_depth;
			 let r_group = data[i].r_group;
		
			 if(r_depth == 0){ //댓글
				htmls +=  '<div class="media text-muted pt-3" id="rno' + rno + '">';
				htmls += '<p class="media-body pb-3 mb-0 small lh-125 border-bottom horder-gray">';	                     
				htmls += '<span class="d-block">';	               
				htmls += '<strong class="text-gray-dark">' + writer + '</strong>';
				htmls += '&nbsp;&nbsp;'+ reg_date;
				htmls += '<br>'                    
				htmls += content;	 
				htmls += '</span>';	  
				
				if(id != ""){ //로그인시 답글 버튼 나오게하기
					htmls += '<span style="padding-left: 7px; font-size: 9pt">';	                     
					htmls += " <a href='#' class='write_reply_start' data-bs-toggle='collapse' data-bs-target='#re_reply"+ rno +"' aria-expanded='false' aria-controls='collapseExample'>답글</a>";                   
					htmls += '</span>';	
				}
			}else{ //답글
				htmls += "<div class='rereply-content" + rno + " col-7'>";
				htmls += "<div>";	                     
				htmls += '<span>';	               
				htmls += '<b> ⤷ ' + writer + '</b>';
				htmls += '&nbsp;&nbsp;'+ reg_date;
				htmls += '</span>';
				htmls += '<br>';
				htmls += '<span>';             
				htmls += '&nbsp;&nbsp;&nbsp;&nbsp;'+content;	 
				htmls += '</span>';	 
			}
		//---------------
		if(id != ''){//로그인 및 작성자와 id가 동일시 수정 및 삭제버튼 나오게
			if(id == writer){
				htmls += '<span style="padding-left: 7px; font-size: 9pt">';       				
				htmls += " <a href='#' class='update_reply_start' data-bs-toggle='collapse' data-bs-target='#rno"+ rno +"' aria-expanded='false' aria-controls='collapseExample'>수정</a>";	
				htmls += '&nbsp;&nbsp;';
				htmls += '<a href="javascript:void(0)" onclick="fn_deleteReply(' + rno + ',\'' + writer + '\')" >삭제</a>';	                     
				htmls += '</span>';
			}	
		}			
		//---------------
	
			//답글 입력란
			htmls += "<div class='collapse rereply_write' id='re_reply"+ rno +"'>";
			htmls += "<div class='col-1'>"
			htmls += "</div>";
			htmls += "<div class='col-1'>"
			htmls +="<input class ='w-100 input_writer_div form-control' id='input_writer"+ rno +"'  value='${memberVO.id}' type='hidden'>";
			htmls += "</div>";
			htmls += "<div class='col-7'>"
			htmls +="<input class ='w-100 input_rereply_div form-control' id='input_rereply"+ rno +"' type='text' placeholder = '댓글을 입력하세요.'>";
			htmls += "</div>";
			htmls += "<div class='col-3'>"
			//동적으로 넣은 html 태그에서 발생하는 이벤트는 동적으로 처리해줘야 함
			//동적으로 넣은 html 태그에서 발생하는 click 이벤트는 html 태그 안에서 onclick 처리하면 안되고 , jquery에서 클래스명 이나 id값을 받아서 처리하도록 해야함
			htmls += "<button type='button' class='btn btn-success mb-1 write_rereply' rno='" + rno +"' bno= '" + bno +"'>답글 달기</button>";
			htmls += "</div>";
			htmls += "</div>";
			
			//-------------------------------------------------
			//수정 html
			htmls += "<div class='collapse reply_update' id='rno"+ rno +"'>";
			htmls += "<div class='col-1'>"
			htmls += "</div>";
			htmls += "<div class='col-1'>"
			htmls +="<input class ='w-100 input_writer_div form-control' id='input_writer"+ rno +"'  value='${memberVO.id}' type='hidden'>";
			htmls += "</div>";
			htmls += "<div class='col-7'>"
			htmls +="<input class ='w-100 input_content_div form-control' id='input_content"+ rno +"' type='text'>";
			htmls += "</div>";
			htmls += "<div class='col-3'>"
			htmls += "<button type='button' class='btn btn-success mb-1 update_reply' rno='" + rno +"' bno= '" + bno +"'>수정 하기</button>";
			htmls += "<button type='button' class='btn' name='list' >취소</button>"
			htmls += "</div>";
			htmls += "</div>";
	
		 }//end for
	
			$("#replyList").html(htmls);
		 
			//답글 작성 후 답글 달기버튼 를 click event를 아래처럼 jquery로 처리
			$('button.btn.btn-success.mb-1.write_rereply').on('click',function(){				
				WriteReReply($(this).attr('bno'), $(this).attr('rno'));
			});
			//--------------답글 저장 end
			
			$('button.btn.btn-success.mb-1.update_reply').on('click',function(){				
				if($(this).attr('r_depth') == 0){
					UpdateReply($(this).attr('rno'), $(this).attr('bno'));				
				}else{
					UpdateReReply($(this).attr('rno'), $(this).attr('bno'), $(this).attr('r_depth'));	
				}
			});
			
		}//ajax success end
	});//end ajax
}

<댓글부분>

//댓글 저장
$(document).on('click','#btnReplyAdd', function(){	
	var Content = $('#content').val();
	var Writer = $('#writer').val();

	if(Content == ""){
		alert("내용을 입력해주세요.");
		return ;
	}
	
	var paramData = JSON.stringify(
			{"content": Content
			,"writer" : Writer
			,"bno" : '${board.bno}'
	});
	var headers = {"Content-Type":"application/json"
		,"X-HTTP-Method-Override":"POST"};
	$.ajax({
		url:"${Path}/reply/addReply.do"
		,headers : headers
		,data : paramData
		,type : 'POST'
		,dataType : 'text'
		,success:function(result){
			window.location.reload();
			getReplyList();
		}
		,error:function(error){
			console.log("에러:" + error);
		}
	});//end ajax
});//end on

//댓글 수정
UpdateReply = function(rno, bno) {
	var writer = $('#input_writer' + rno).val();
	var content = $('#input_content' + rno).val();
	
	var paramData = JSON.stringify({
		"bno" : bno,
		"rno" : rno,
		"content" : content,
		"writer" : writer
	});

	var headers = {
		"Content-Type" : "application/json",
		"X-HTTP-Method-Override" : "POST"
	};
	
	$.ajax({
		url : "${Path}/reply/updateReply.do",
		headers : headers,
		data : paramData,
		type : 'POST',
		dataType : 'text',
		success : function(result) {
			console.log(result);
			getReplyList();
		},
		error : function(error) {
			console.log("에러:" + error);
		}
	});//end ajax
}

//댓글 삭제
function fn_deleteReply(rno){
	var DelConfirm = confirm('댓글을 삭제 하시겠습니까?');
	
	var paramData = {"rno" : rno};

	if(DelConfirm){	
		alert("댓글이 삭제 되었습니다.");
	}else{
		alert("댓글 삭제가 취소 되었습니다.");
		return;
	}
	
	$.ajax({
		url:"${Path}/reply/deleteReply.do"
		,data : paramData
		,type : 'POST'
		,dataType : 'text'
		,success:function(result){
			getReplyList();
		}
		,error: function(error){
			console.log("에러:" + error);
		}
	});
}

<답글부분>

//-----------------------------------------------
//답글 달기 버튼 클릭시 실행 
WriteReReply = function(bno, rno){
	
	var writer = $('#input_writer' + rno).val();
	var content = $('#input_rereply' + rno).val();
	content = content.trim();
	
	var paramData = JSON.stringify({
		"bno" : bno,
		"rno" : rno,
		"content" : content,
		"writer" : writer
	});

	var headers = {
		"Content-Type" : "application/json",
		"X-HTTP-Method-Override" : "POST"
	};
	
	if(content == ""){
		alert("답글을 입력해주세요.");
	}else{
		$('#input_rereply' + rno).val("");//입력란 비우기
		
		$.ajax({
			url : "${Path}/reply/write_rereply.do"
			,headers : headers
			,data : paramData
			,type : 'POST'
			,dataType : 'text'
			,success:function(result){
				getReplyList();
			},
			error:function(error){
				console.log("에러:"+ error);
			}
		});//end ajax
		
	};
};

//답글 수정
UpdateReReply = function fn_updateReply(rno, bno, r_depth) {
	var writer = $('#input_writer' + rno).val();
	var content = $('#input_content' + rno).val();
	
	var paramData = JSON.stringify({
		"bno" : bno,
		"rno" : rno,
		"r_depth" : r_depth,
		"content" : content,
		"writer" : writer
	});

	var headers = {
		"Content-Type" : "application/json",
		"X-HTTP-Method-Override" : "POST"
	};
	
	$.ajax({
		url : "${Path}/reply/update_rereply.do",
		headers : headers,
		data : paramData,
		type : 'POST',
		dataType : 'text',
		success : function(result) {
			console.log(result);
			getReplyList();
		},
		error : function(error) {
			console.log("에러:" + error);
		}
	});//end ajax
}

//동적 html에서는 onclick을 사용을 못하기에 jquery로 click버튼을 만듬
$(document).ready(function(){
	$(document).on("click", "button[name='list']", function(){
		getReplyList();
	});
});
복사했습니다!