관리 메뉴

DeseoDeSeo

[Spring] MVC07. 답글 작성, 게시글삭제, XSS대비 본문

spring

[Spring] MVC07. 답글 작성, 게시글삭제, XSS대비

deseodeseo 2023. 10. 23. 19:25
get.jsp
로그인 되어있을 때만 답글 수정 가능하도록
<tr>
	<td colspan="2" style="text-align:center;" >
		<c:if test="${not empty mvo}">   
     	<button data-btn="reply" class="btn btn-sm btn-primary">답글</button>
        <button data-btn="modify" class="btn btn-sm btn-success">수정</button>
		</c:if>

		<c:if test="${empty mvo}">   
		<button disabled="disabled" class="btn btn-sm btn-primary">답글</button>
		<button disabled="disabled" class="btn btn-sm btn-success">수정</button>
		</c:if>
								
		<button data-btn="list" class="btn btn-sm btn-warning">목록</button>
	</td>
</tr>
modify.jsp
수정, 삭제는 해당 글의 작성자만 가능하도록
<tr>
	<td colspan="2" style="text-align:center;" >
    <c:if test="${not empty mvo && mvo.memID eq vo.memID }">
    <button type="submit" class="btn btn-sm btn-primary">수정완료</button>
    <button data-btn="remove" type="button" class="btn btn-sm btn-success">삭제</button>
	</c:if>
	<c:if test="${empty mvo or mvo.memID ne vo.memID }">
	<button disabled="disabled" type="submit" class="btn btn-sm btn-primary">수정완료</button>
	<button disabled="disabled" type="button" class="btn btn-sm btn-success">삭제</button>
	</c:if>
	<button data-btn="list" type="button"  class="btn btn-sm btn-warning">목록</button>
	<!-- cpath는 절대경로임. -->
	</td>
</tr>

group은 내림차순, Sequence는 오름차순

BoardController.java
@PostMapping("/reply")
	public String reply(Board vo) {
		service.reply(vo);
		return "redirect:/board/list";
	}
reply.jsp
: 답글 작성하는 페이지
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
	<%@ taglib prefix ="c" uri="http://java.sun.com/jsp/jstl/core" %>
	<%@ taglib prefix ="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<c:set var="cpath" value="${pageContext.request.contextPath}" />	
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script
	src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script
	src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
	<div class="container">
		<h2>Spring MVC07</h2>
		<div class="panel panel-default">
			<div class="panel-heading">Board</div>
			<div class="panel-body">
				<form action="${cpath}/board/reply" method="post">
					<input type="hidden" name="memID" value="${mvo.memID}">
					<!-- 부모글의 게시글 번호 -->
					<input type="hidden" name="idx" value="${vo.idx}">
						
					<div class="form-group">
						<label>제목</label>
						<input value="<c:out value='${vo.title}'/>" type="text" name="title" class="form-control">
					</div>
					
					<div class="form-group">
						<label>답변</label>
						<textarea rows="10" cols="" name="content" class="form-control"></textarea>
					</div>
					
					<div class="form-group">
						<label>작성자</label>
						<input value="${mvo.memName}" readonly="readonly" type="text" name="writer" class="form-control">
					</div>
					<button type="submit" class="btn btn-default btn-sm">등록</button>
					<button type="reset"  class="btn btn-default btn-sm">취소</button>
					<button data-btn="list"  type="button" class="btn btn-default btn-sm">목록</button>
					
				</form>
				
				<form id="frm" method="get" action="">
					<input id="idx" type="hidden" name="idx" value="${vo.idx}">
				</form>
				
				
			</div>
			<div class="panel-footer">스프링게시판-뇽뇽이</div>
		</div>
	</div>

	<script type="text/javascript">
	$(document).ready(function(){
		$("button").on("click",function(e){ 
			var formData=$("#frm");
			var btn =$(this).data("btn");
			
			//버튼이 reply이면 action을 아래 경로로 바꿔줌.
			if(btn=="list"){
				formData.attr("action","${cpath}/board/list");
				formData.find("#idx").remove();
				//list는 idx가 필요 없으니까 찾아서 없애줌.
			}
			formData.submit();
		}); //버튼 눌렀을 떄
		
	});
	
	</script>
</body>
</html>
BoardService.java
public void reply(Board vo);
BoardServiceImpl.java
@Override
	public void reply(Board vo) {
	    // 답글 만들기
		// vo :부모글의 번호, 로그인한 아이디, 제목, 답글, 작성자 이름
		// 부모글의 정보를 가져오기
		Board parent = mapper.read(vo.getIdx());
		// 부모글의 boardGroup값을 답긆 vo에 저장하기.
		vo.setBoardGroup(parent.getBoardGroup());
		//sequence와 레벨은 부모글에서 +1
		vo.setBoardSequence(parent.getBoardSequence()+1);
		vo.setBoardLevel(parent.getBoardLevel()+1);
		
		//현재 추가하려는 답글을 제외한 기존에 같은 그룹의 댓글 시퀀스 값을 1씩 올려줘야한다,
		//왜냐면 최신순으로 답글이 출력되기 때문에.
		mapper.replySeqUpdate(parent);
		
		//답변 저장기능
		mapper.replyInsert(vo);
	}
BoardMapper.java
public void replySeqUpdate(Board parent);
BoardMapper.xml
<update id="replySeqUpdate" parameterType="kr.spring.entity.Board">
		UPDATE TBLBOARD
		SET BOARDSEQUENCE = BOARDSEQUENCE +1
		WHERE BOARDGROUP = #{boardGroup} 
		AND BOARDSEQUENCE > #{boardSequence}
	</update>

 

BoardMapper.java
public void replyInsert(Board vo);
BoardMapper.xml
: 
답변 입력
<insert id="replyInsert" parameterType="kr.spring.entity.Board">
<!-- id,boardGroup만 가져와서 board타입에 넣고 before= 이전에 실행하겠다. -->
			<selectKey keyProperty ="idx" resultType="kr.spring.entity.Board" order="BEFORE">
				SELECT IFNULL(MAX(IDX)+1, 1) as  idx
				       FROM TBLBOARD
			</selectKey>
		INSERT INTO TBLBOARD(IDX, MEMID, TITLE, CONTENT, WRITER, BOARDGROUP, BOARDSEQUENCE, BOARDLEVEL, BOARDAVAILABLE)
		VALUES(#{idx},#{memID},#{title},#{content},#{writer},
		#{boardGroup},#{boardSequence},#{boardLevel},1)
		
</insert>

 

BoardMapper.xml
: 게시글 가져오기
<select id="getList" resultType="kr.spring.entity.Board">
		SELECT * FROM TBLBOARD
		ORDER BY BOARDGROUP DESC, BOARDSEQUENCE ASC
</select>

 

list.jsp
:
이렇게 하면 답글에 들여쓰기가 된다.
<c:if test="${vo.boardLevel > 0 }">
	<c:forEach begin="0" end="${vo.boardLevel}" step="1">
		<span style="padding-left:15px"></span>
	</c:forEach>
	ㄴ[RE]
</c:if>

 

게시글 삭제하기
BoardMapper.xml
<update id="delete" parameterType="int">
		UPDATE TBLBOARD
		SET BOARDAVAILABLE=0 
		WHERE IDX=#{idx}
</update>
list.jsp
<c:if test="${vo.boardAvailable ==0}">
			<a href="javascript:alert('삭제된 게시글 입니다. ')"> 
			<c:if test="${vo.boardLevel > 0 }">
				<c:forEach begin="0" end="${vo.boardLevel}" step="1">
					<span style="padding-left:15px"></span>
			</c:forEach>
			ㄴ[RE]
			</c:if>
									
	   삭제된 게시물 입니다.
			</a>
</c:if>
<c:if test="${vo.boardAvailable > 0}">
	<a href="${cpath}/board/get?idx=${vo.idx}">
	  <c:if test="${vo.boardLevel > 0 }">
			<c:forEach begin="0" end="${vo.boardLevel}" step="1">
				<span style="padding-left:15px"></span>
			</c:forEach>
			ㄴ[RE]
		<!-- el(= ${vo.title} )식을 바로 사용하면 xss에 취약함. -->
	  </c:if>
	<c:out value="${vo.title}"/>
	</a>
</c:if>

 

XSS( Cross Site Scripting) 대비
: 게시글 작성할 때, 고의적으로 제목이나 내용에 스크립트를 넣어서 공격하는 기법에 대비
➜ jstl에 c:out을 사용.
list.jsp
이런식으로 작성한다.
<c:out value="${vo.title}"/>

 

modify.jsp
➜ 작성자가 작성할 수 있는 부분의 el식을 바꿔준다.
<tr>
	<td>제목</td>
	<td><input value="<c:out value='${vo.title}'/>" name="title" type="text" class="form-control"></td>
</tr>
<tr>
	<td>내용</td>
	<td>
		<textarea class="form-control" name="content" rows="10" cols=""><c:out value="${vo.content}"/></textarea>
	</td>
</tr>

 

get.jsp
<c:if test="${empty mvo}">   
	<button onclick="location.href='${cpath}/board/reply?idx=${vo.idx}'" class="btn btn-sm btn-primary">답글</button>
	<button  onclick="location.href='${cpath}/board/modify?idx=${vo.idx}' class="btn btn-sm btn-success">수정</button>
</c:if>

상단의 코드를 하단의 코드처럼 onclick의 location.href기능을 삭제한다. ( 코드가 길어져서 가독성이 떨어짐)

 

 html5 중에서 요소를 구분하기 위한 data 속성 사용. (data- 옵션 뒤에 원하는 이름을 줄 수 있다.)

<c:if test="${not empty mvo}">   
	<button data-btn="reply" class="btn btn-sm btn-primary">답글</button>
	<button data-btn="modify" class="btn btn-sm btn-success">수정</button>
</c:if>

<c:if test="${empty mvo}">   
	<button disabled="disabled" class="btn btn-sm btn-primary">답글</button>
	<button disabled="disabled" class="btn btn-sm btn-success">수정</button>
</c:if>

 아래의 코드는 페이지 이동을 위해서 idx값을 보내는 것.

	</table>
	<form id="frm" method="get" action="">
		<input id="idx" type="hidden" name="idx" value="${vo.idx}">
	</form>
<script type="text/javascript">
		//10.11 링크처리(가독성, 보안(url노출 안됨.),유지보수 편리)//e:클릭했을 때, 요소를 감지하겠다.
		$(document).ready(function(){
			$("button").on("click",function(e){ 
				var formData=$("#frm");
				var btn =$(this).data("btn");
				
				//버튼이 reply이면 action을 아래 경로로 바꿔줌.
				if(btn=="reply"){
					formData.attr("action","${cpath}/board/reply");
				}else if(btn=="modify"){
					formData.attr("action","${cpath}/board/modify");
				}else if(btn=="list"){
					formData.attr("action","${cpath}/board/list");
					formData.find("#idx").remove();
					//list는 idx가 필요 없으니까 찾아서 없애줌.
				}
				
		formData.submit();
  }); //버튼 눌렀을 떄
			
});
		
	
</script>