[Spring] Spring 웹 사이트 만들기 7 - 게시판(쿼리스트링을 숨길 수 있는 게시글 삭제/수정)

2022. 11. 22. 15:28·📗 self-study/📗 KH정보교육원 당산지원

 

🔥 게시글 삭제 🔥 

 

⌨️ boardDetailView.jsp

👉🏻 로그인 한 유저와 글을 쓴 유저가 일치할 때만 수정/삭제 버튼이 보이게끔 조건 설정 및 링크 걸기

👉🏻 수정/삭제 많이 해 봤고 링크 뻔하니까 함께 걸어 봄 ^^*

             <c:if test="${ loginUser.userId eq b.boardWriter }">
	            <div align="center">
	                <!-- 수정하기, 삭제하기 버튼은 이 글이 본인이 작성한 글일 경우에만 보여져야 함 -->
	                <a class="btn btn-primary" href="updateForm.bo?bno=${ b.boardNo }">수정하기</a>
	                <a class="btn btn-danger" href="delete.bo?bno=${ b.boardNo }">삭제하기</a>
	            </div>
	            <br><br>
            </c:if>

현재 delete.bo를 만들지 않았기 때문에 404 뜨는 것은 당연함!

 

 

 

📍 그런데 여기서 잠깐! 코딩 좀 할 줄 아는 놈이 나쁜 맘 먹고 아래 코드를 쳐 버린다면?

http://localhost:8006/spring/delete.bo?bno=100

boardNo: 100의 글이 실제로 삭제될 것! 😱

 

즉, 해킹의 위험에서 벗어나기 위해 쿼리스트링이 노출되지 않도록 하고 싶음!

 

 

 

💻 boardDetailView.jsp

            <h2>게시글 상세보기</h2>
            <br>

            <a class="btn btn-secondary" style="float:right;" href="list.bo">목록으로</a>
            <br><br>

            <table id="contentArea" algin="center" class="table">
                <tr>
                    <th width="100">제목</th>
                    <!-- 여기서 잘못 넣어 주면 getter 오류 남! -->
                    <td colspan="3">${ b.boardTitle }</td>
                </tr>
                <tr>
                    <th>작성자</th>
                    <td>${ b.boardWriter }</td>
                    <th>작성일</th>
                    <td>${ b.createDate }</td>
                </tr>
                <tr>
                    <th>첨부파일</th>
                    <td colspan="3">
                    	<c:choose>
                    		<c:when test="${ empty b.originName }">
                    			첨부파일이 없습니다.
                    		</c:when>
                    		<c:otherwise>
                    			<!-- 그냥 download로만 적어도 다운로드는 가능하지만, 그렇게 되면 수정명으로 파일 다운로드되므로 원본 파일명으로 처리해 줄 것! -->
                        		<a href="${ b.changeName }" download="${ b.originName }">${ b.originName }</a>
                    		</c:otherwise>
                    	</c:choose>
                    </td>
                </tr>
                <tr>
                    <th>내용</th>
                    <td colspan="3"></td>
                </tr>
                <tr>
                    <td colspan="4"><p style="height:150px;">${ b.boardContent }</p></td>
                </tr>
            </table>
            <br>
            
            <c:if test="${ loginUser.userId eq b.boardWriter }">
	            <div align="center">
	                <!-- 수정하기, 삭제하기 버튼은 이 글이 본인이 작성한 글일 경우에만 보여져야 함 -->
	                <a class="btn btn-primary" onclick="postFormSubmit(1);">수정하기</a>
	                <a class="btn btn-danger" onclick="postFormSubmit(2);">삭제하기</a>
	            </div>
	            <br><br>
	            
	            <!-- action 속성 비워 놓고 아래 script에서 action 속성만 바꿔치기 하는 게 포인트! -->
	            <form id="postForm" action="" method="post">
	            	<input type="hidden" name="bno" value="${ b.boardNo }">
	            	<!-- input 태그의 submit 버튼도 만들지 않음! -->
	            </form>
	            
	            <script>
	            	function postFormSubmit(num) {
						
	            		// action 속성값을 부여 후 연이어서 곧바로 submit 시키기
	            		if(num == 1) { // 수정하기 버튼 클릭 시 num == 1 : updateForm.bo
	            			
	            			// location.href="updateForm.bo"; // get방식이기 때문에 url 노출됨! 우리의 목적에 적합하지 않음
	            			$("#postForm").attr("action", "updateForm.bo").submit();
	            			
	            		} else { // 삭제하기 버튼 클릭 시 num == 2 : delete.bo
	            			
	            			$("#postForm").attr("action", "delete.bo").submit();
	            			// 폼 태그 선택, action 속성을 바꾸고, 메소드 체이닝을 통해 submit
	            			
	            		}
	            	}
	            </script>
            </c:if>

이제 더 이상 url에 게시글 번호가 노출되지 않음!

보안 풀충 완료 🔋

 

💻 BoardController

	@RequestMapping("delete.bo")
	public String deleteBoard(int bno, String filePath, HttpSession session, Model model) {
		
		// System.out.println(bno);
		
		int result = boardService.deleteBoard(bno);
		
		if(result > 0) { // 게시글 삭제 성공
			
			// 첨부파일이 있었을 경우 => 수정명 찍힘
			// 첨부파일이 없었을 경우 => 빈 문자열 찍힘
			// filePath에는 해당 게시글의 수정파일명이 들어 있음, 빈문자열이 아니라면 첨부파일이 있었던 경우일 것!
			if(!filePath.equals("")) { // 첨부파일이 있었을 경우
				
				String realPath = session.getServletContext().getRealPath(filePath);
				new File(realPath).delete();
			}
			
			// 게시판 리스트 페이지 url 재요청
			session.setAttribute("alertMsg", "성공적으로 게시글이 삭제되었습니다.");
			
			return "redirect:/list.bo";
			
		} else { // 게시글 삭제 실패
			
			model.addAttribute("errorMsg", "게시글 삭제 실패");
			
			return "common/errorPage";
		}
		
	}

 

💻 BoardServiceImpl

	@Override
	public int deleteBoard(int boardNo) {
		return boardDao.deleteBoard(sqlSession, boardNo);
	}

 

💻 BoardDao

	public int deleteBoard(SqlSessionTemplate sqlSession, int boardNo) {
		
		return sqlSession.update("boardMapper.deleteBoard", boardNo);
		
	}

 

💻 board-mapper.xml

	<update id="deleteBoard" parameterType="_int">
		UPDATE BOARD
		   SET STATUS = 'N'
		WHERE BOARD_NO = #{boardNo}
	</update>
더보기

👉🏻 여기 17번째의 게시글이 있습니다

👉🏻 삭제하기 버튼 클릭 시 alert창이 잘 뜸

 

 👉🏻 삭제 성공 시 list.bo로 url를 연결해 주었기 때문에 리스트가 다시 보이고, 삭제된 17번은 보이지 않게 됨!

 

 👉🏻 STS의 uploadFiles 폴더를 refresh 했을 때 첨부파일이 보이지 않게 됨!

 

 👉🏻 STS의 uploadFiles 폴더를 refresh 했을 때 첨부파일이 보이지 않게 됨! 

 

 

 

 

🔥 게시글 수정 전 해당 게시글의 정보가 담긴 수정 폼 띄우기 🔥 

⌨️ boardUpdateForm.jsp 생성 후 하드코딩

👉🏻 미리 만들어진 폼에서 head, body 영역 붙여 넣기

👉🏻 head의 3가지 링크는 include 할 header와 중복이기 때문에 삭제

👉🏻  head의 style 태그 중 content, innerOuter 역시 header와 중복이기 때문에 삭제

👉🏻  header, footer.jsp include 하기

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        #updateForm>table {width:100%;}
        #updateForm>table * {margin:5px;}
    </style>
</head>
<body>
        
    <jsp:include page="../common/header.jsp" />

    <div class="content">
        <br><br>
        <div class="innerOuter">
            <h2>게시글 수정하기</h2>
            <br>

            <form id="updateForm" method="post" action="" enctype="">
                <table algin="center">
                    <tr>
                        <th><label for="title">제목</label></th>
                        <td><input type="text" id="title" class="form-control" value="게시판제목임ㅋㅋ" name="" required></td>
                    </tr>
                    <tr>
                        <th><label for="writer">작성자</label></th>
                        <td><input type="text" id="writer" class="form-control" value="user01" name="" readonly></td>
                    </tr>
                    <tr>
                        <th><label for="upfile">첨부파일</label></th>
                        <td>
                            <input type="file" id="upfile" class="form-control-file border" name="">
                            현재 업로드된 파일 : 
                            <a href="" download="">flower.jpg</a>
                        </td>
                    </tr>
                    <tr>
                        <th><label for="content">내용</label></th>
                        <td><textarea id="content" class="form-control" rows="10" style="resize:none;" name="" required>여긴내용쓰</textarea></td>
                    </tr>
                </table>
                <br>

                <div align="center">
                    <button type="submit" class="btn btn-primary">수정하기</button>
                    <button type="button" class="btn btn-danger" onclick="javascript:history.go(-1);">이전으로</button>
                </div>
            </form>
        </div>
        <br><br>

    </div>
    
    <jsp:include page="../common/footer.jsp" />
    
</body>
</html>

 

💻 boardUpdateForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        #updateForm>table {width:100%;}
        #updateForm>table * {margin:5px;}
    </style>
</head>
<body>
        
    <jsp:include page="../common/header.jsp" />

    <div class="content">
        <br><br>
        <div class="innerOuter">
            <h2>게시글 수정하기</h2>
            <br>

            <form id="updateForm" method="post" action="update.bo" enctype="multipart/form-data">
                <table algin="center">
                    <tr>
                        <th><label for="title">제목</label></th>
                        <td><input type="text" id="title" class="form-control" value="${ b.boardTitle }" name="" required></td>
                    </tr>
                    <tr>
                        <th><label for="writer">작성자</label></th>
                        <td><input type="text" id="writer" class="form-control" value="${ b.boardWriter }" name="" readonly></td>
                    </tr>
                    <tr>
                        <th><label for="upfile">첨부파일</label></th>
                        <td>
                            <input type="file" id="upfile" class="form-control-file border" name="">
                         	
                         	<!--
                         		c:if 태그의 test 속성에는 originName, changeName 중 무엇이 들어가도 상관없음!
                         		어차피 둘 중에 하나라도 있으면 첨부파일 있는 거고, 없으면 둘 다 없을 것이기 때문에!
                         	-->
                         	<c:if test="${ not empty b.originName }">
	                         	현재 업로드된 파일 : 
	                            <a href="${ b.changeName }" download="${ b.originName }">${ b.originName }</a>
                         	</c:if>

                        </td>
                    </tr>
                    <tr>
                        <th><label for="content">내용</label></th>
                        <td><textarea id="content" class="form-control" rows="10" style="resize:none;" name="" required>${ b.boardContent }</textarea></td>
                    </tr>
                </table>
                <br>

                <div align="center">
                    <button type="submit" class="btn btn-primary">수정하기</button>
                    <button type="button" class="btn btn-danger" onclick="javascript:history.go(-1);">이전으로</button>
                </div>
            </form>
        </div>
        <br><br>

    </div>
    
    <jsp:include page="../common/footer.jsp" />
    
</body>
</html>

 

💻 BoardController

👉🏻 DB에 가기 전에 먼저 해당 게시글 정보부터 조회해 옴 (= 조회한 내용으로 수정 폼을 띄우는 역할)

	@RequestMapping("updateForm.bo")
	public String updateForm(int bno, Model model) {
		
		// 게시글 수정 페이지를 포워딩 하기 전에 우선적으로 해당 게시글 정보를 조회해 올 것
		Board b = boardService.selectBoard(bno); // 기존의 상세보기 서비스를 재활용
		
		model.addAttribute("b", b);
		
		return "board/boardUpdateForm";
		
	}

👉🏻 수정 폼에 내가 수정하고자 하는 글의 정보가 잘 담겨 있음

 

 

 

🔥 게시글 수정 🔥 

💻 boardUpdateForm

👉🏻 name 속성 지정해 주기

👉🏻 input type="hidden"으로 boardNo, originName, changeName도 함께 넘겨 주기

👉🏻 이때, name 속성도 필드명과 맞춰 줌으로써 Controller에서 커맨드 객체 방식 이용할 것

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        #updateForm>table {width:100%;}
        #updateForm>table * {margin:5px;}
    </style>
</head>
<body>
        
    <jsp:include page="../common/header.jsp" />

    <div class="content">
        <br><br>
        <div class="innerOuter">
            <h2>게시글 수정하기</h2>
            <br>

            <form id="updateForm" method="post" action="update.bo" enctype="multipart/form-data">
                <input type="hidden" name="boardNo" value="${ b.boardNo }">
                <table algin="center">
                    <tr>
                        <th><label for="title">제목</label></th>
                        <td><input type="text" id="title" class="form-control" value="${ b.boardTitle }" name="boardTitle" required></td>
                    </tr>
                    <tr>
                        <th><label for="writer">작성자</label></th>
                        <td><input type="text" id="writer" class="form-control" value="${ b.boardWriter }" name="boardWriter" readonly></td>
                    </tr>
                    <tr>
                        <th><label for="upfile">첨부파일</label></th>
                        <td>
                            <input type="file" id="upfile" class="form-control-file border" name="reupfile">
                         	
                         	<!--
                         		c:if 태그의 test 속성에는 originName, changeName 중 무엇이 들어가도 상관없음!
                         		어차피 둘 중에 하나라도 있으면 첨부파일 있는 거고, 없으면 둘 다 없을 것이기 때문에!
                         	-->
                         	<c:if test="${ not empty b.originName }">
	                         	현재 업로드된 파일 : 
	                            <a href="${ b.changeName }" download="${ b.originName }">${ b.originName }</a>
	                            
	                            <!-- 수정을 하기 위해서는 만약 기존 파일이 있었다면 기존 파일에 대한 정보도 넘겨 줘야 함! -->
	                            <input type="hidden" name="originName" value="${ b.originName }">
	                            <input type="hidden" name="changeName" value="${ b.changeName }">
                         	</c:if>

                        </td>
                    </tr>
                    <tr>
                        <th><label for="content">내용</label></th>
                        <td><textarea id="content" class="form-control" rows="10" style="resize:none;" name="boardContent" required>${ b.boardContent }</textarea></td>
                    </tr>
                </table>
                <br>

                <div align="center">
                    <button type="submit" class="btn btn-primary">수정하기</button>
                    <button type="button" class="btn btn-danger" onclick="javascript:history.go(-1);">이전으로</button>
                </div>
            </form>
        </div>
        <br><br>

    </div>
    
    <jsp:include page="../common/footer.jsp" />
    
</body>
</html>

 

⌨️ BoardController

	@RequestMapping("update.bo")
	public void updateBoard(Board b, MultipartFile reupfile) {
		
		if(!reupfile.getOriginalFilename().equals("")) { // 새로 넘어온 첨부파일이 있는 경우 == reupfile이 빈 문자열이 아니라면
			
            // 1. 기존 첨부파일이 있었을 경우 => 기존 첨부파일 찾아서 삭제
			System.out.println(b);
			
		} else { // 새로 넘어온 첨부파일이 없는 경우
			
		}
		
	}
Board(boardNo=20, boardTitle=어라 내 첨부파일 게시글, boardWriter=admin, boardContent=어데 갔냐,
originName=0_스프링 환경구축.pdf, changeName=resources/uploadFiles/2022112215585925151.pdf, count=0, createDate=null, status=null)

 

✔️ 현재 콘솔에 찍힌 내용을 확인해 보면

 

👉🏻 b의 boardNo: 내가 수정하고자 하는 게시글의 번호 (WHERE절)
👉🏻 b의 boardTitle: 수정할 제목 (SET절)
👉🏻 b의 boardContent: 수정할 내용 (SET절)
👉🏻 b의 originName: 기존 첨부파일의 원본명
👉🏻 b의 changeName: 기존 첨부파일의 수정명

 

✔️ 첨부파일의 원본명, 수정명이 기존의 파일의 것으로 그대로 나옴!

👉🏻 b의 originName, changeName: jsp에서 input type="hidden"으로 넘겼기 때문에 기존의 파일 정보가 나옴

👉🏻 Controller에서 새로운 reupfile에 대한 정보로 바꿔치기 해 줘야 함!

 

더보기

현재 b에 무조건 담겨 있는 내용

👉🏻 boardNo, boardTitle, boardContent

 

추가적으로 고려해야 할 경우의 수

1. 새로 첨부된 파일 X, 기존 첨부파일 x

👉🏻 originName: null

👉🏻 changeName: null

 

2. 새로 첨부된 파일 X, 기존 첨부파일 O


👉🏻 originName: 기존 첨부파일 원본명
👉🏻 changeName: 기존 첨부파일 수정명 + 파일 경로
   

3. 새로 첨부된 파일 O, 기존 첨부파일 X

👉🏻 originName: 새로 첨부된 파일의 원본명
👉🏻 changeName: 새로 첨부된 파일의  수정명 + 파일 경로


4. 새로 첨부된 파일 O, 기존 첨부파일 O

👉🏻 기존 파일 삭제, 새로 전달된 파일을 서버에 업로드
👉🏻 originName: 새로 첨부된 파일의 원본명

👉🏻 changeName: 새로 첨부된 파일의  수정명 + 파일 경로

 

💻 BoardController

	@RequestMapping("update.bo")
	public String updateBoard(Board b, MultipartFile reupfile, HttpSession session, Model model) {
		
		if(!reupfile.getOriginalFilename().equals("")) { // 새로 넘어온 첨부파일이 있는 경우 == reupfile이 빈 문자열이 아니라면

			// 1. 기존 첨부파일이 있었을 경우 => 기존 첨부파일 찾아서 삭제
			if(b.getOriginName() != null) {
				String realPath = session.getServletContext().getRealPath(b.getChangeName());
				new File(realPath).delete();
			}
			
			// 2. 새로 넘어온 첨부파일을 수정명으로 바꾸고 서버에 업로드 시키기
			String changeName = saveFile(reupfile, session);
			
			// 3. b 객체에 새로 넘어온 첨부파일에 대한 원본명, 수정 파일명 필드에 담기
			b.setOriginName(reupfile.getOriginalFilename()); // 원본 파일명 덮어 씌움
			b.setChangeName("resources/uploadFiles/" + changeName);
			
		} // 새롭게 들어온 첨부파일이 없다면 굳이 파일의 원본/수정명을 바꿀 필요 없으므로 else문 없이 끝!

				// Service단으로 b 보내기
				int result = boardService.updateBoard(b);
				
				if(result > 0) { // 게시글 수정 성공
					
					session.setAttribute("alertMsg", "성공적으로 게시글이 수정되었습니다.");
					
					// 게시글 상세보기 페이지로 url 재요청
					return "redirect:/detail.bo?bno=" + b.getBoardNo();
					
				} else { // 게시글 수정 실패
					
					model.addAttribute("errorMsg", "게시글 수정 실패");
					return "common/errorMsg";
					
				}

 

💻 BoardService

👉🏻 매개변수를 int boardNo에서 Board b로 수정해야 함!

👉🏻 처음 로직을 생각했을 때는 boardNo만 바꿔 주면 될 줄 알았는데 내용 수정, 제목 수정, 첨부파일 삭제/추가 등 넘길 정보가 많으므로 Board 객체로 넘기는 게 맞다는 판단 때문!

	// 게시글 수정 서비스
	int updateBoard(Board b);

 

수정했더니 즉각 반응 오는 BoardServiceImpl의 매개변수도 바꿔 주자!

 

💻 BoardServiceImpl

	@Override
	public int updateBoard(Board b) {
		return boardDao.updateBoard(sqlSession, b);
	}

 

💻 BoardDao

	public int updateBoard(SqlSessionTemplate sqlSession, Board b) {
		
		return sqlSession.update("boardMapper.updateBoard", b);
		
	}

 

💻 board-mapper.xml

	<update id="updateBoard" parameterType="board">
		UPDATE BOARD
		   SET BOARD_TITLE = #{boardTitle}
			 , BOARD_CONTENT = #{boardContent}
			 , ORIGIN_NAME = #{originName}
			 , CHANGE_NAME = #{changeName}
		WHERE BOARD_NO = #{boardNo}
	</update>
더보기

👉🏻 기존 게시글 상세보기에서 수정하기 버튼을 클릭해 보면

 

👉🏻 작성자 제외 내용들을 수정할 수 있음! 내용 수정 중... 첨부파일 첨부 중...

👉🏻 완료 후 수정하기 버튼 클릭

 

👉🏻 alert창 뜨고

 

👉🏻 수정이 완료된 모습이다

저작자표시 비영리 변경금지 (새창열림)
'📗 self-study/📗 KH정보교육원 당산지원' 카테고리의 다른 글
  • [Spring] Spring에서 Ajax 사용하기 1 활용 - 🔥 아이디 중복체크 기능 🔥
  • [Spring] Spring에서 Ajax 사용하기 1 - 기본 설정과 Ajax 사용법 2가지(HttpSession 이용/응답할 데이터를 문자열 타입으로 반환)
  • [Spring] 스프링에서 파일을 업로드(첨부파일) 하기 위한 라이브러리 2가지
  • [Spring] Spring 웹 사이트 만들기 7 - 게시판(게시글 작성/상세 조회)
천재강쥐
천재강쥐
  • 천재강쥐
    디버거도 버거다
    천재강쥐
  • 전체
    오늘
    어제
    • Category (467)
      • 진짜 너무 궁금한데 이걸 나만 몰라...? (0)
      • 💾 Portfolio (2)
      • 🐤 CodingTest (28)
        • Java (20)
        • ᕕ(ꐦ°᷄д°᷅)ᕗ❌ (5)
      • 🚀 from error to study (142)
        • AI (1)
        • Cloud (2)
        • DB (12)
        • Front-End (16)
        • Github (14)
        • Java (39)
        • Mac (7)
        • Normal (29)
        • Server (22)
      • 📘 certificate (44)
        • 📘 리눅스마스터1급 (1)
        • 📘⭕️ 정보처리기사 (40)
        • 📘⭕️ SQLD (3)
      • 📗 self-study (234)
        • 📗 inflearn (35)
        • 📗 생활코딩 (8)
        • 📗 KH정보교육원 당산지원 (190)
      • 🎨 Scoop the others (0)
        • 📖 Peeking into other people.. (0)
        • 🇫🇷 (0)
        • 📘⭕️ 한국사능력검정시험 심화 (11)
        • 오블완 (4)
  • 인기 글

  • hELLO· Designed By정상우.v4.10.1
천재강쥐
[Spring] Spring 웹 사이트 만들기 7 - 게시판(쿼리스트링을 숨길 수 있는 게시글 삭제/수정)
상단으로

티스토리툴바