본문 바로가기
멀티캠퍼스 풀스택 과정/백엔드

Spring:12 게시판 글 수정, 삭제(한 레코드와 여러 레코드) 구현 -4

by 이쟝 2022. 3. 19.

1. 수정 기능 추가하기

1. 글 내용 보는 폼(view)에서 수정과 삭제를 추가한다.

수정과 삭제가 로그인이 되었을 때 보여야 하고, 아이디가 맞을 때 보여야 한다. 

bulletinBrdView.jsp

	<!-- 로그인이 아이디와 작성자가 같은 경우 -->
	<div><!-- MemberController에서 임의로 생성한것처럼 현재 유저의 id를 logId로 임의 생성 -->
		<c:if test="${logId==vo.userid}">
			<a href="/myappy/board/bulletinBrdEdit?no=${vo.no}">수정</a>
			<a href="">삭제</a>
		</c:if>
	</div>

2. 글 수정 폼을 만든다. 

- checkEdit를 사용한다. (글 등록할 때 처럼)

bulletinBrdEdit.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<script src="//cdn.ckeditor.com/4.17.2/standard/ckeditor.js"></script>
<script>
	$(function(){
		CKEDITOR.replace("content");
		
		$("#EditFrm").on(submit, function(){
			if($("#subject").val()==''){
				alert("글제목을 입력하세요");
				return false;
			}
			if(CKEDITOR.instance.content.getData()==''){
				alert("글내용을 입력하세요"); 
				return false;
			}
		});
	});
</script>
<div class="container">
	<h1>글수정 폼</h1>
	<form method="post" action="/myappy/board/boardEditOk" id="EditFrm">
		<input type="hidden" name="no"/>
		<ul>
			<li><input type="text" name="subject" id="subject"/></li>
			<li><textarea name="content" id="content"></textarea></li>
			<li><input type="submit" value="글수정"/></li>
		</ul>
	</form>
</div>

3. Controller에 view에 있는 주소와 맵핑하기

BulletinBrdController

이전에 만들어놓은 boardSelect를 이용해서 레코드 번호(no)에 맞는 데이터를 얻어오고 그것을 BulletBrdVO에 넣어놓는다. 

addObject에서의 "vo"는 bulletinBrdEdit.jsp파일에서 사용한다. 
	// 글 수정 폼 보기
	@GetMapping("boardEdit")  // bulletinBrdView.jsp
	public ModelAndView boardEdit(int no) {
		ModelAndView mav = new ModelAndView();
		BulletinBrdVO bvo = service.boardSelect(no);
		
		mav.addObject("vo", bvo);
		mav.setViewName("board/bulletinBrdEdit");
		return mav;
	}


2. 글 수정을 submit 하면 수정되어야 한다.(DB 연동)

1. Mapper.xml에 수정하기 위한 쿼리문을 작성한다.

수정이니까 update문을 사용한다.
수정할 때 레코드 번호(no)와 userid가 같아야 수정할 수 있다. 
서버로 넘어가는 값은 객체로 no와 userid가 넘어가고 int형으로 결과가 반환된다. BulletinBrdVO에 데이터가 담겨 클라이언트로 넘어온다. 
update board set subject=#{db에 저장된 subject}, content=#{db에 저장된 content} where no=${no} and userid=#{userid}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mycampus.myappy.dao.BulletinBrdDAO">
	<update id="boardUpdate">
		update board set subject=#{subject}, content=#{content} 
		where no=${no} and userid=#{userid}
	</update>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mycampus.myappy.dao.BulletinBrdDAO">
	<update id="boardUpdate">
		update board set subject=#{subject}, content=#{content} 
		where no=${no} and userid=${userid}
	</update>
</mapper>

2. DAO, Service, ServiceImpl에 boardUpdate메서드 구현하기

package com.mycampus.myappy.dao;
import com.mycampus.myappy.vo.BulletinBrdVO;
public interface BulletinBrdDAO {
	// 글 수정 폼
	public int boardUpdate(BulletinBrdVO vo);
}
package com.mycampus.myappy.service;
import com.mycampus.myappy.vo.BulletinBrdVO;
public interface BulletinBrdService {
	// 글 수정 폼
	public int boardUpdate(BulletinBrdVO vo);
}
package com.mycampus.myappy.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.mycampus.myappy.dao.BulletinBrdDAO;

@Service
public class BulletinBrdServiceImpl implements BulletinBrdService {

	@Autowired
	BulletinBrdDAO dao;
	
	@Override
	public int boardUpdate(BulletinBrdVO vo) {
		return dao.boardUpdate(vo);
	}
}

3. Controller에서 DB와 연동해서 글 수정하는 주소를 맵핑한다. 

ResponseEntity는 HttpEntity를 상속 받음으로써 HttpStatus, HttpHeaders, HttpBody를 포함한다. 
- 기본적으로 Body, Headers, Status 코드를 기준으로 작성을 한다.
- 객체로 생성 시 Status 코드는 필수 값이다.

글 등록 폼을 DB와 연동할 때와 유사하다. (매개변수로 VO와 , 세션에 저장된 아이디를 가져와야 한다.)

[insert, update, delete에는 resultType이 없고 row의 개수를 반환한다.]
- insert의 경우는 삽입된 행의 개수를 반환
- update의 경우는 수정에 성공한 행의 개수를 반환(실패 시 0 반환)
- delete의 경우는 삭제한 행의 개수를 반환
	// 글 수정 DB연동
	@PostMapping(value="boardEditOk")
	public ResponseEntity<String> boardEditOk(BulletinBrdVO vo, HttpSession session) {
		
		ResponseEntity<String> entity = null; // 데이터와 처리 상태를 가진다.
		
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(new MediaType("text","html", Charset.forName("UTF-8")));
		
		// 세션에서 id를 가져와서 vo에 setUserid 
		vo.setUserid((String)session.getAttribute("logId"));
		try {
			int result = service.boardUpdate(vo);
			
			if(result>0) { // 수정 성공
				entity = new ResponseEntity<String>(EditSucMsg(vo.getNo()), headers, HttpStatus.OK);
			}else { // 수정 실패
				entity = new ResponseEntity<String>(EditFailMsg(), headers, HttpStatus.BAD_REQUEST);
			}
			
		}catch(Exception e) {
			e.printStackTrace();
			// 수정 실패
			entity = new ResponseEntity<String>(EditFailMsg(), headers, HttpStatus.BAD_REQUEST);
		}
		return entity;
	}
	
	// 글 수정 실패 메시지
	public String EditFailMsg() {
		String msg ="<script>alert('글 수정에 실패하였습니다.\\n수정폼으로 이동합니다.');";
		msg += " history.back();</script> ";
		return msg;
	}
	
	// 글 수정 성공 메시지
	public String EditSucMsg(int no) {  //boardView에서 레코드 번호(no)
		String msg ="<script>alert('글 수정에 성공하였습니다.\\n글내용 보기로 이동합니다.');";
		msg += " location.href='/myappy/board/bulletinBrdView?no="+no+"' ";
		msg += " </script> ";
		return msg;
	}

4. view 페이지에서 DB에 있는 데이터를 보이게 한다.

vo에 저장되어 있는 레코드 번호, 제목, 내용을 가져온다. (레코드 번호는 안 보이게 한다.)

bulletinBrdEdit.jsp

	<form method="post" action="/myappy/board/boardEditOk" id="EditFrm">
		<input type="hidden" name="no" value="${vo.no}"/>
		<ul>
			<li><input type="text" name="subject" id="subject" value="${vo.subject}"/></li>
			<li><textarea name="content" id="content">${vo.content}</textarea></li>
			<li><input type="submit" value="글수정"/></li>
		</ul>
	</form>

3. 삭제 기능 추가하기

1. view페이지에 삭제하기 위한 delete주소를 연결한다.

delete 파일을 만들지 않고, JS로 구현한다. controller와 맵핑되는 주소는 boardDelete이다.

<script>
	function deleteChk(){
		// 사용자가 확인(true), 취소(false)를 선택할 수 있는 대화상자 
		if(confirm("삭제하시겠습니까?")){
			// 확인 버튼 선택시 
			location.href="/myappy/board/boardDelete?no=${vo.no}";
			// 맵핑주소를 정하고, 삭제를 위해서 레코드번호를 가져가야 한다.(no)
		}
	}
</script>
<!-- 로그인이 아이디와 작성자가 같은 경우 -->
<div><!-- MemberController에서 임의로 생성한것처럼 현재 유저의 id를 logId로 임의 생성 -->
	<c:if test="${logId==vo.userid}">
		<a href="/myappy/board/boardEdit?no=${vo.no}">수정</a>
		<a href="javascript:deleteChk()">삭제</a>
	</c:if>
</div>

2. Mapper.xml에 수정하기 위한 쿼리문을 작성한다.

삭제는 클라이언트에서 넘어가는 레코드 번호(no)를 사용해 삭제하고, 아이디(userid)를 사용해 아이디가 같은 사람 것을 삭제한다. 
delete from board where no=#{param1} and userid=#{userid}

bltenBrdMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mycampus.myappy.dao.BulletinBrdDAO">
	<delete id="boardDelete">
		delete from board where no=#{param1} and userid=#{param2}
	</delete>
</mapper>

 

3. DAO, Service, ServiceImpl에 boardUpdate메서드 구현하기

package com.mycampus.myappy.dao;
public interface BulletinBrdDAO {
	// 글 삭제
	public int boardDelete(int no, String userid); // id가 같은 사람 것을 삭제
}
package com.mycampus.myappy.service;
public interface BulletinBrdService {
	// 글 삭제
	public int boardDelete(int no, String userid); 
}
package com.mycampus.myappy.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.mycampus.myappy.dao.BulletinBrdDAO;
@Service
public class BulletinBrdServiceImpl implements BulletinBrdService {

	@Autowired
	BulletinBrdDAO dao;
    
	@Override
	public int boardDelete(int no, String userid) {
		return dao.boardDelete(no, userid);
	}
}

4. Controller에 삭제하려는 주소를 맵핑한다. 

	// 글 수정 삭제 메시지
	@GetMapping("boardDelete")
	public ModelAndView boardDel(int no, HttpSession session) { //view에서 레코드 번호 매개변수로 보냄
		String userid = (String)session.getAttribute("logId");
		int result = service.boardDelete(no, userid);
		
		ModelAndView mav = new ModelAndView();
		if(result>0) { // 삭제 성공
			mav.setViewName("redirect:bulletinBrdList"); // 서버에서 바로 boardList 컨트롤러로 이동해서 안에 내용실행
			
		}else { // 삭제 실패
			mav.setViewName("redirect:bulletinBrdView");
		}
		return mav;
	}

4. boardList에서 checkbox를 선택해서 여러 레코드를 삭제한다. 

1. view페이지(게시판 리스트)에 checkbox를 추가한다.

전체선택 체크박스와 선택삭제 버튼을 추가하고, 
forEach문으로 만든 리스트에 체크박스를 추가하고 여러 데이터이기 때문에 클래스(chk)로 정의한다.

JS로 구현
전체선택 체크박스를 클릭하면 클래스(chk)의 checked되게 하고 전체선택 체크박스도 check되게 한다.
하나도 선택을 하지 않고 선택삭제를 누르는 경우 알림창이 뜨게 한다.(반복문으로 돌리면서 체크 됐는지 확인)
<script>
	$(function(){
		// 리스트 전체 선택하기
		$("#allCheck").on('click', function(){
			$(".chk").prop("checked", $("#allCheck").prop("checked"));
		});
		
		// 선택을 안하고 선택삭제를 누르는 경우
		$("#multiChkDel").click(function(){
			let cnt = 0;
			$(".chk").each(function(index, obj){ //체크박스객체의 인덱스, 체크박스객체
				if(obj.checked){ // 체크박스가 체크상태일때(true일때)
					cnt++;
				}
			});
			if(cnt<=0){ // 체크박스가 하나도 선택되지 않은 경우
				alert("목록을 선택 후 삭제하세요");
				return false;
			}
			$("#listFrm").submit();
		});
	});
</script>

<h1>게시판 목록</h1>
	<div><a href="<%=request.getContextPath()%>/board/bulletinBrdWrite">글쓰기</a></div>
	<br/>
	<div>총 레코드 수 : ${pVO.totalRecord} / 총 페이지 개수 : ${pVO.totalPage} /
		현재 페이지 번호 : ${pVO.pageNum}</div>
	<br/>
	<div> <!-- 전체선택과 선택삭제 버튼 추가-->
		<input type="checkbox" id="allCheck">전체선택
		<input type="button" value="선택삭제" id="multiChkDel"/>
	</div>
	<br/>
	<form method="post" action="myappy/board/multiChkDel" id="listFrm">
		<ul class="boardList">
			<li>&nbsp;</li> <!--추가-->
			<li>번호</li>
			<li>제목</li>
			<li>작성자</li>
			<li>조회수</li>
			<li>등록일</li>

			<c:forEach var="vo" items="${list}">     <!--여러 레코드를 선택하기 위해 class로 생성-->
				<li><input type="checkbox" name="recordNoList" value="${vo.no}" class="chk"/></li>
				<li>${vo.no}</li>
				<li><a href="/myappy/board/bulletinBrdView?no=${vo.no}">${vo.subject}</a></li>
				<li>${vo.userid}</li>
				<li>${vo.views}</li>
				<li>${vo.writedate}</li>
			</c:forEach>
		</ul>
	</form>

2. VO에 여러 레코드를 담은 recodNoList를 배열의 형태로 getter setter를 만든다. 

List<Integer>로 만들어도 된다. 

이 작업이 없으면 db와 연동이 안되기 때문에 데이터를 담아서 가져오지 못한다. 

package com.mycampus.myappy.vo;

public class BulletinBrdVO {

	//private List<Integer> recordNoList;
	private int[] recordNoList;
	
	public int[] getRecordNoList() {
		return recordNoList;
	}
	public void setRecordNoList(int[] recordNoList) {
		this.recordNoList = recordNoList;
	}

}

3. Mapper.xml에 쿼리문을 작성한다.

여러 레코드를 삭제하는 것이기 때문에 일반 delete와는 다르게 작동한다. 
https://mybatis.org/mybatis-3/ko/dynamic-sql.html 동적SQL -> foreach 예제 참고!
collection 전달받은 인자/변수 즉, list 같은 배열 형식의 변수
item 배열에서의 데이터 값(list에 있는 데이터 값이 item에 넘어가게 된다.)
index 현재 for문을 돌고 있는 index
open foreach문이 시작될 때 앞에 삽입될 문자열
separator foreach문에서 반복되는 값 사이에 넣어줄 문자열
close foreach문이 종료될 때 앞에 삽입될 문자열

bltnBrdMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mycampus.myappy.dao.BulletinBrdDAO">
	<delete id="boardMultiDel">
		delete from board
		<where>
			<foreach item="item" collection="recordNoList" open="no in (" separator="," close=")">
				#{item}
			</foreach>
			and userid=#{userid}
		</where>
	</delete>
</mapper>

4. DAO, Service, ServiceImpl에 boardUpdate메서드 구현하기

package com.mycampus.myappy.dao;
import com.mycampus.myappy.vo.BulletinBrdVO;
public interface BulletinBrdDAO {
	// 선택된 레코드 삭제 
	public int boardMultiDel(BulletinBrdVO vo);
}
package com.mycampus.myappy.service;
import com.mycampus.myappy.vo.BulletinBrdVO;
public interface BulletinBrdService {
	// 선택된 레코드 삭제 
	public int boardMultiDel(BulletinBrdVO vo);
}
package com.mycampus.myappy.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.mycampus.myappy.dao.BulletinBrdDAO;
import com.mycampus.myappy.vo.BulletinBrdVO;

@Service
public class BulletinBrdServiceImpl implements BulletinBrdService {

	@Autowired
	BulletinBrdDAO dao;

	@Override
	public int boardMultiDel(BulletinBrdVO vo) {
		return dao.boardMultiDel(vo);
	}
}

5. Controller에 삭제하려는 주소를 맵핑한다. 

bulletinBrdList에서 서버로 넘어갈 주소를 맵핑한다.
<form method="post" action="myappy/board/multiChkDel" id="listFrm">

mav.setViewName("board/bulletinBrdList")는 오류난다!!!(자세한 건 모르겠지만... 현재 페이지에서 굳이 페이지 이동을 하지 않아도 되기 때문에 redirect로 서버에서만 실행하고 다시 돌아오는 것 같다.)
	// 선택한 여러 레코드 삭제
	@PostMapping(value="multiChkDel")
	public ModelAndView multiDelete(BulletinBrdVO vo, HttpSession session) {
		vo.setUserid((String)session.getAttribute("logId")); // 아이디 구하기
		
		ModelAndView mav = new ModelAndView(); //mav 객체 생성
		service.boardMultiDel(vo);
		mav.setViewName("redirect:bulletinBrdList"); // 서버에서 바로 boardList 컨트롤러로 이동해서 안에 내용실행

		return mav;
	}