쌍용교육(JAVA)/SpringBoot

쌍용교육 -JSP수업 97일차 ch15SpringPage(8)

구 승 2024. 7. 4. 12:49

BoardMapper.xml 내용추가

댓글의 개수를 추가 하기위해 SQL 작성

<!-- 게시판 전체 목록/검색 목록 --> 부분 내용추가

LEFT OUTER JOIN (SELECT COUNT (*) re_cnt, board_num FROM spboard_reply GROUP BY board_num) USING(board_num)

댓글의 개수를 추가 한 모습

이젠 상세페이지에 댓글갯수를 보이게 하려고함

boardView.jsp 

span id를 추가

<!-- 댓글수 -->
		<span id="output_rcount"></span>

board.reply.js 

내용수정

//댓글 수 읽어오기
				displayReplyCount(param.count);

 

/*===================
		댓글수 표시
	===================*/
	function displayReplyCount(count){
		let output;
		if(count>0){
			output = '댓글수('+count+')';
		}else{
			output = '댓글수(0)';
			
		}
		//문서 객체에 추가
		$('#output_rcount').text(output);
	}

댓글수가 추가됨.

//다음 댓글 보기 버튼 클릭시 데이터 추가
	$('.paging-button input').click(function(){
		selectList(currentPage + 1);
		
	});

board.css

댓글들이 너무 붙어있는 부분을 해결하기위해 내용수정

div.item{
	margin-top:10px;
}

댓글 수정 폼 작성

board.reply.js 

/*===================
		댓글 수정
	===================*/
	//댓글 수정 버튼 클릭시 수정폼 노출
	$(document).on('click','.modify-btn',function(){
		//댓글번호
		let re_num = $(this).attr('data-num');
		//댓글 내용
		let re_content = $(this).parent()
							 .find('p')
							 .html()
							 .replace(/<br>/gi,'\r\n') /*부모태그에 p태그를 찾아서 바꾼다.*/
							 /*g: 지정문자열 모두, i: 대소문자 무시 => 즉 대소문자 상관없이 모든 문자열 모두를 줄바꿈해라.*/
		//댓글 수정폼 UI
		let modifyUI = '<form id="mre_form">';
		modifyUI += '<input type="hidden" name="re_num" id="re_num" value="'+re_num+'">';
		modifyUI += '<textarea rows="3" cols="50" name="re_content" id="mre_content" class="rep-content">'+re_content+'</textarea>';
		modifyUI += '<div id="mre_first"><span class="letter-count">300/300</span></div>';
		modifyUI += '<div id="mre_second" class="align-right">';
		modifyUI += ' <input type="submit" value="수정">';
		modifyUI += ' <input type="button" value="취소" class="re-reset">';
		modifyUI += '</div>';
		modifyUI += '<hr size="1" noshade width="96%">';
		modifyUI += '</form>';
		
		//답글이 있는 경우 답글을 초기화
		
		//답글이 있는 경우 답글을 초기화
		
		//이전에 이미 수정하는 댓글이 있을 경우 수정 버튼을 클릭하면
		//숨김 sub-item를 환원시키고 수정폼을 초기화함
		initModifyForm();
		//지금 클릭해서 수정하고자 하는 데이터는 감추기
		//(수정 버튼을 감싸고있는 div)
		$(this).parent().hide(); //삭제가 아닌 감추기 (부모쪽에서 찾은다음 숨기기)
		
		//수정폼을 수정하고자 하는 데이터가 있는 div에 노출
		$(this).parents('.item').append(modifyUI); //parent는 직계부모. parents는 뒤에 이름을 지정해서 여러부모중 한 부모를 찾는것.
		
		//입력한 글자수 셋팅
		let inputLength = $('#mre_content').val().length;
		let remain = 300 - inputLength;
		remain += '/300';
		
		//문서 객체에 반영
		$('#mre_first .letter-count').text(remain);
	});	
	//수정폼에서 취소 버튼 클릭시 수정폼 초기화
	$(document).on('click','.re-reset',function(){
		initModifyForm();
	});
	//댓글 수정폼 초기화
	function initModifyForm(){
		$('.sub-item').show();
		$('#mre_form').remove();
	}

수정 버튼을 누르면 수정폼이 생성된다(아직 수정은안됨)

댓글 수

BoardMapper.java

>>BoardMapper에서 회원번호를 구해서 자신의 댓글과 일치하는지 알아내고,

//댓글 수정,삭제시 작성자 회원번호를 구하기 위해 사용
	@Select("SELECT * FROM spboard_reply WHERE re_num=#{re_num}")
	public BoardReplyVO selectReply(Long re_num);

 수정할 수 있도록 SQL문 작성 

@Update("UPDATE spboard_reply SET re_content=#{re_content},re_ip=#{re_ip},re_mdate=SYSDATE WHERE re_num=#{re_num}")
	public void updateReply(BoardReplyVO boardReply);

BoardServiceImpl

@Override
	public BoardReplyVO selectReply(Long re_num) {

		return boardMapper.selectReply(re_num);
	}
@Override
	public void updateReply(BoardReplyVO boardReply) {
		boardMapper.updateReply(boardReply);
		
	}

BoardAjaxController

댓글수정 포스트매핑으로 받은 후 수정값넣기,로그인 여부 확인등

/*===================
	댓글 수정
	===================*/
	@PostMapping("/board/updateReply")
	@ResponseBody
	public Map<String,String> modifyReply(
									BoardReplyVO boardReplyVO,
									HttpSession session,
									HttpServletRequest request){
		log.debug("<<댓글 수정>> :"+boardReplyVO);
		
		Map<String,String> mapJson = new HashMap<String, String>();
		
		MemberVO user = (MemberVO)session.getAttribute("user"); //로그인을 했는지 확인하기위해 (회원제 서비스이기 떄문에)
		BoardReplyVO db_reply = boardService.selectReply(boardReplyVO.getRe_num());
		
		if(user == null) {
			//로그인이 되지 않은 경우
			mapJson.put("result","logout");
		}else if(user !=null 
				&& user.getMem_num()==db_reply.getMem_num()) {
			//로그인 회원번호와 작성자 회원번호 일치
			
			//ip저장
			boardReplyVO.setRe_ip(request.getRemoteAddr());
			//댓글 수정
			boardService.updateReply(boardReplyVO);
			mapJson.put("result","success");
		}else {
			//로그인 회원번호와 작성자 회원번호 불일치
			mapJson.put("result","wrongAccess");
		}

		return mapJson;
	}

board.reply.js 

수정을 눌렀을 때 수정폼의 있는 수정버튼을 연결해서 수정하고자함.

	//댓글 수정
	$(document).on('submit','#mre_form',function(event){
		if($('#mre_content').val().trim()==''){ //수정폼의 내용이 없을경우
			alert('내용을 입력하세요!');
			$('#mre_content').val().focus();
			return false;
		}
		//폼에 입력한 데이터 반환
		let form_data = $(this).serialize();
		//서버와 통신
		$.ajax({
			url:'updateReply',
			type:'post',
			data:form_data,
			dataType:'json',
			success:function(param){
				if(param.result =='logout'){
					alert('로그인해야 수정할 수 있습니다.');
				}else if(param.result == 'success'){
					$('#mre_form').parent().find('p').html($('#mre_content').val().replace(/</g,'&lt;')
																				  .replace(/>/g,'&gt;')
																				  .replace(/\r\n/g,'<br>')
																				  .replace(/\r/g,'<br>')
																				  .replace(/\n/g,'<br>'));
																				  
					//최근 수정일 처리			
					$('#mre_form').parent().find('modify-date').text('최근 수정일: 5초미만');	
					//수정폼 초기화
					initModifyForm();													  
				}else if(param.result =='wrongAccess'){
					alert('타인의 글은 수정할 수 없습니다.');
				}else{
					alert('댓글 수정 오류 발생');
				}
			},
			error:function(){
				alert('네트워크 오류 발생');
			}
		});
		//기본 이벤트 제거 (작성이 끝나면 이벤트를 없애야되니까)
		event.preventDefault();
	});

수정을 하면 수정한 댓글이 바로 업데이트 된다.
코드를 제목으로 넣으면
리스트에는 적용이된다.

태그 적용되는걸 없애려고함.

BoardMapper.xml

 <!-- 게시판 전체 목록/검색 목록 -->
	 <select id="selectList" parameterType="map" resultType="boardVO">
	 
	 
	 	SELECT
	 		*
	 	FROM (SELECT
	 			a.*,
	 			rownum rnum
	 		  FROM (SELECT
	 		  		  board_num,
	 		  		  <![CDATA[
	 		  		  REPLACE(REPLACE(title,'<','&lt;'),'>','&gt;') title,
	 		  		  ]]>
	 		  		  hit,
	 		  		  filename,
	 		  		  reg_date,
	 		  		  mem_num,
	 		  		  id,
	 		  		  nick_name,
	 		  		  re_cnt,
	 		  		  fav_cnt
	 		  		FROM spboard 
	 		  		LEFT OUTER JOIN (SELECT COUNT (*) re_cnt, board_num FROM spboard_reply GROUP BY board_num) USING(board_num)
	 		  		LEFT OUTER JOIN (SELECT COUNT (*) fav_cnt, board_num FROM spboard_fav GROUP BY board_num) USING(board_num) <!-- fav_cnt는 알리아스 명칭임 이 괄호는 인라인 뷰 -->
	 		  		JOIN spmember USING(mem_num)
	 		  		<include refid="boardSearch"></include>
	 		  		<include refid="boardOrder"></include>
	 		  		)a) 
	<![CDATA[
	WHERE rnum >= #{start} AND rnum <= #{end}
	]]>
	</select>

아래코드 부분을 추가했다.

 board_num,
	 		  		  <![CDATA[
	 		  		  REPLACE(REPLACE(title,'<','&lt;'),'>','&gt;') title,
	 		  		  ]]>
	 		  		  hit,
	 		  		  filename,
	 		  		  reg_date,
	 		  		  mem_num,
	 		  		  id,
	 		  		  nick_name,
	 		  		  re_cnt,
	 		  		  fav_cnt

태그적용이 사라졌다.

BoardMapper.java

댓글 삭제말고도 부모글 삭제시 댓글,답글 자동삭제하는 SQL도 만들어두긴했음.

@Delete("DELETE FROM spboard_reply WHERE re_num=#{re_num}")
	public void deleteReply(Long re_num);
	//부모글 삭제시 댓글이 존재하면 부모글 삭제전 댓글 삭제
	@Delete("DELETE FROM spboard_reply WHERE board_num=#{board_num}")
	public void deleteReplyByBoardNum(Long board_num);
	//부모글 삭제시 댓글의 답글이 존재하면 댓글 번호를 구해서 답글 삭제시 사용
	@Select("SELECT * FROM spboard_reply WHERE board_num=#{board_num}")
	public List<Long> selectReNumsByBoard_num(Long board_num);

boardServiceImpl

@Override
	public void deleteReply(Long re_num) {
		boardMapper.deleteReply(re_num);
		
	}

BoardAjaxController

/*===================
	댓글 삭제
	===================*/
	@PostMapping("/board/deleteReply")
	@ResponseBody
	public Map<String,String> deleteReply(long re_num,
											HttpSession session){
		log.debug("<<댓글 삭제 - re_num>>:"+ re_num);
		
		Map<String,String> mapJson = new HashMap<String, String>();
		MemberVO user = (MemberVO)session.getAttribute("user"); //로그인을 했는지 확인하기위해 (회원제 서비스이기 떄문에)
		BoardReplyVO db_reply = boardService.selectReply(re_num);
		
		if(user == null) {
			//로그인이 되지 않은 경우
			mapJson.put("result","logout");
		}else if(user !=null 
				&& user.getMem_num()==db_reply.getMem_num()) {
			//로그인 회원번호와 작성자 회원번호 일치	
            boardService.deleteReply(re_num);
			mapJson.put("result","success");
		}else {
			//로그인 회원번호와 작성자 회원번호 불일치
			mapJson.put("result","wrongAccess");
		}
		
		return mapJson;
	}

board.reply.js 

/*===================
		댓글 삭제
	===================*/
							//위에서 만들었던 class=delete-btn을 클릭했을 시
	$(document).on('click','.delete-btn',function(){
		//댓글 번호
		let re_num = $(this).attr('data-num');
		//서버와 통신
		$.ajax({
			url:'deleteReply',
			type:'post',
			data:{re_num:re_num},
			dataType:'json',
			success:function(param){
				if(param.result == 'logout'){
					alert('로그인해야 삭제할 수 있습니다.');
				}else if(param.result=='success'){
					alert('삭제완료');
					selectList(1); //1페이지 목록을 호출
				}else if(param.result=='wrongAccess'){
					alert('타인의 글을 삭제할 수 없습니다');
				}else{
					alert('댓글 삭제 오류 발생');
				}
			},
			error:function(){
				alert('네트워크 오류 발생');
			}
		});
	});

아래에 있던 댓글을 삭제시킬 수 있다.

table.sql

sqldeveloper => java에 구동

--댓글 좋아요
create table sqreply_fav(
 re_num number not null,
 mem_num number not null,
 constraint refav_fk1 foreign key (re_num)
 						references spboard_reply (re_num),
 constraint refav_fk2 foreign key (mem_num)
 						references spmember (mem_num)						
);

BoardReFavVO 클래스 생성

package kr.spring.board.vo;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class BoardReFavVO {
	private long re_num;
	private long mem_num;
}

BoardMapper.java

//댓글 좋아요
	public BoardReFavVO selecReFav(BoardReFavVO fav);
	public Integer selectReFavCount(Long re_num);
	public void insertReFav(BoardReFavVO fav);
	public void deleteReFav(BoardReFavVO fav);
	public void deleteReFavByRenum(Long re_num);
	public void deleteReFavByBoardNum(Long board_num);

BoardService에다 위에 4개만 복사해서 넣어준다.

BoardServiceImpl

에 add method를 해준다.

추가로 service에 복사를 안했던 2개의 메서드를 명시해준다.

@Override
	public void deleteBoard(Long board_num) {
		//답글 삭제
		
		//댓글 좋아요 삭제
		boardMapper.deleteReFavByBoardNum(board_num);
@Override
	public void deleteReply(Long re_num) {
		//답글
		
		//댓글 좋아요
		boardMapper.deleteReFavByRenum(re_num);
		//댓글
		boardMapper.deleteReply(re_num);
		
	}

BoardMapper.java

sql문 작성

//댓글 좋아요
	@Select("SELECT * FROM spreply_fav WHERE re_num=#{re_num} AND mem_num=#{mem_num}")
	public BoardReFavVO selecReFav(BoardReFavVO fav);
	@Select("SELECT * COUNT(*) FROM spreply_fav WHERE re_num=#{re_num}")
	public Integer selectReFavCount(Long re_num);

BoardServiceImpl

작성한 sql문을 연결시켜

@Override
	public BoardReFavVO selecReFav(BoardReFavVO fav) {
		return boardMapper.selecReFav(fav);
	}

	@Override
	public Integer selectReFavCount(Long re_num) {
		return boardMapper.selectReFavCount(re_num);
	}

BoardAjaxController

	/*===================
	댓글 좋아요 읽기
	===================*/
	@GetMapping("/board/getReFav")
	@ResponseBody
	public Map<String,Object> getReFav(
								BoardReFavVO fav,
								HttpSession session){
		log.debug("<<댓글 좋아요>>:" +fav);
		
		Map<String,Object> mapJson = new HashMap<String, Object>();
		
		MemberVO user = (MemberVO)session.getAttribute("user"); //로그인을 했는지 확인하기위해 (회원제 서비스이기 떄문에)
		if(user == null) {
			mapJson.put("result","success");
			mapJson.put("result","noFav");
		}else {
			fav.setMem_num(user.getMem_num());
			BoardReFavVO boardReFav = boardService.selecReFav(fav);
			if(boardReFav!=null) {
				mapJson.put("result","success");
				mapJson.put("result","yesFav");
			}else {
				mapJson.put("result","success");
				mapJson.put("result","noFav");
			}
		}
		mapJson.put("count",boardService.selectReFavCount(fav.getRe_num()));
		return mapJson;
	}