고래씌
[Spring] 8-5. 게시판 수정하기(+ 사진삭제, 사진수정, 사진추가) 본문
1. 일반게시판 수정
=> views/board/boardUpdateEnrollFrom.jsp 파일 생성
▶ boardUpdateEnrollFrom.jsp
- 사진이 없던 곳에 새롭게 추가된 경우 → INSERT
- 사진이 있던 곳에 새롭게 추가된 경우 → UPDATE
- 사진이 있던 곳에 삭제가 된 경우 → DELETE
- 사진이 있거나, 없던 곳에 그대로 없는 경우 → X
- 존재하고 있는 이미지가 제거된 경우, 해당 이미지의 pk(board_img_no)를 input태그에 저장시켜서 value값안에 있는 pk값으로 delete문의 조건식에 활용할 예정
- 만약 value값 안에 (1,3,5)라는 값이 담겨있다 → DELETE FROM BOARD_IMG WHERE BOARD_IMG_NO IN (1,3,5)
=> 이값이 BoardController.java로 넘어감
<%@ 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>Insert title here</title>
<style>
img{
width: 100px;
}
</style>
</head>
<body>
<jsp:include page="/WEB-INF/views/common/header.jsp" />
<div class="content">
<br><br>
<div class="innerOuter">
<h2>게시글 수정하기</h2>
<br>
<form actioin="${contextPath }/boarrd/update/${boardCode}/${boardNo}" id="enrollForm"
method="post" enctype="multipart/form-data">
<!-- 사진도 보내고 해야하기 때문에 multipart 사용 -->
<!-- 첨부파일은 ORIGIN_NAME과 CHANGE_NAME에 컬럼에 저장할 예정 -->
<table align="center">
<tr>
<th>제목</th>
<td><input type="text" id="title" class="form-control" name="boardTitle" value="${board.boardTitle }" required></td>
</tr>
<tr>
<th>작성자</th>
<td>${board.boardWriter}</td>
</tr>
<c:if test="${boardCode ne 'T'}"> <!-- 사진게시판이 아닐때 -->
<tr>
<th>첨부파일</th>
<td><input type="file" id="upfile" class="form-control" name="upfile">${board.originName}
<input type="hidden" name="originName" value="${board.originName}" />
<input type="hidden" name="changeName" value="${board.changeName}" />
</td>
</tr>
</c:if>
<c:if test="${boardCode eq 'T' }"> <!-- 사진게시판 이면 -->
<c:if test="${not empty board.imgList}">
<c:forEach items="${board.imgList}" var="boardImg" varStatus="i"> <!-- varStatus : 현재 반복중인 상태값이 담긴 변수 -->
<c:choose>
<c:when test="${boardImg.imgLevel == 0}">
<c:set var="img0" value="${contextPath}/resources/images/board/T/${boardImg.changeName}" />
<c:set var="img0no" value="${boardImg.boardImgNo}" />
</c:when>
<c:when test="${boardImg.imgLevel == 1}">
<c:set var="img1" value="${contextPath}/resources/images/board/T/${boardImg.changeName}" />
<c:set var="img1no" value="${boardImg.boardImgNo}" />
</c:when>
<c:when test="${boardImg.imgLevel == 2}">
<c:set var="img2" value="${contextPath}/resources/images/board/T/${boardImg.changeName}" />
<c:set var="img2no" value="${boardImg.boardImgNo}" />
</c:when>
<c:when test="${boardImg.imgLevel == 3}">
<c:set var="img3" value="${contextPath}/resources/images/board/T/${boardImg.changeName}" />
<c:set var="img3no" value="${boardImg.boardImgNo}" />
</c:when>
</c:choose>
</c:forEach>
</c:if>
<tr>
<th><label for="image">업로드 이미지1</label></th>
<td>
<img class="preview" src="${img0}">
<input type="file" name="upfiles" class="form-control inputImage" accept="images/*" id="img1">
<span class="delete-image" data-no="${img0no}">×</span>
</td>
</tr>
<tr>
<th><label for="image">업로드 이미지2</label></th>
<td>
<img class="preview" src="${img1}">
<input type="file" name="upfiles" class="form-control inputImage" accept="images/*" id="img2">
<span class="delete-image" data-no="${img1no}">×</span>
</tr>
<tr>
<th><label for="image">업로드 이미지3</label></th>
<td>
<img class="preview" src="${img2}">
<input type="file" name="upfiles" class="form-control inputImage" accept="images/*" id="img3">
<span class="delete-image" data-no="${img2no}">×</span>
</td>
</tr>
<tr>
<th><label for="image">업로드 이미지4</label></th>
<td>
<img class="preview" src="${img3}">
<input type="file" name="upfiles" class="form-control inputImage" accept="images/*" id="img4">
<span class="delete-image" data-no="${img3no}">×</span>
</td>
</tr>
</c:if>
<tr>
<th>내용</th>
<td>
<textarea id="content" style="resize:none;" rows="10" class="form-control"
name="boardContent" required="required">${board.boardContent}</textarea>
</td>
</tr>
</table>
<!--
존재하고 있는 이미지가 제거된 경우, 해당 이미지의 pk(board_img_no)를 input태그에 저장시켜서
value값안에 있는 pk값으로 delete문의 조건식에 활용할 예정
만약 value값 안에 (1,3,5)라는 값이 담겨있다 →
DELETE FROM BOARD_IMG
WHERE BOARD_IMG_NO IN (1,3,5)
-->
<input type="hidden" name="deleteList" id="deleteList" value="" />
<div align="center">
<button type="submit" class="btn btn-primary">수정</button>
<button type="reset" class="btn btn-danger">취소</button>
</div>
</form>
</div>
</div>
<!-- 사진 미리보기 -->
<c:if test="${boardCode eq 'T'}"> <!-- 사진게시판이라면 -->
<script>
const inputImage = document.querySelectorAll('.inputImage'); // input type = file
const preview = document.querySelectorAll('.preview'); // img
const deleteImage = document.querySelectorAll('.delete-image'); // 삭제버튼들
const deleteList = document.querySelector("#deleteList"); // hidden 태그
const deleteSet = new Set();
// 키값이 중복이 안됨. X 버튼이 눌릴때마다 추가추가 될건데 같은 값이 계속 추가되면 안되니 set으로 일차적으로 추가할 예정
inputImage.forEach( function ( value, index ){
//현재 반복중인 file태그
value.addEventListener('change', function(){
if(this.files[0] != undefined){// 선택한 파일이 있는경우
const reader = new FileReader(); // 선택된 파일을 읽을 객체 생성
reader.readAsDataURL(this.files[0]);
// reader가 파일을 다 읽어온 경우
reader.onload = function(e) {
preview[index].setAttribute("src", e.target.result);
}
}else{ // 파일이 선택되지 않았을때 (취소)
preview[index].removeAttribute("src");
}
let no = $(this).next().data('no')
if(no != undefined && no) deleteSet.add(no); // span태그(x버튼) // 매개변수로 no 키값을 가져오고 있음
deleteList.value = [...deleteSet];
})
// X 버튼을 클릭하였을때 사진 삭제하도록 설정
deleteImage[index].addEventListener('click', function(){
// 현재 미리보기가 존재하는 경우에 삭제처리되도록 수정
if(preview[index].getAttribute("src") != "") {
// 미리보기 삭제 + input태그 비워주기
preview[index].removeAttribute("src");
inputImage[index].value = "";
// data.no
let no = this.dataset.no; // 이미지번호 or undefined
if(no != undefined) deleteSet.add(no); // 얻어온 no값을 저장함. no가 undefined가 아닐때만!
// 저장되어있던 이미지를 "삭제"하고자할 때 사용
deleteList.value = [...deleteSet] // 깊은복사 // 객체(set)를 배열로 변환
}
})
})
</script>
</c:if>
<jsp:include page="/WEB-INF/views/common/footer.jsp" />
</body>
</html>
▶ BoardController.java
=> 일반게시판 수정하기기능
=> 첨부파일 / 이미지 수정하기
+ 첨부파일이나 이미지 수정하는 경우, 웹서버에 저장한 파일 신경쓰지 말것. db정보만 바꿔주기
=> 게시글내용(boardContent)은 개행처리 된 상태이기 때문에 <br>태그를 /n로 변경해주는 작업이 필요
=> @RequestParam에 required 옵션을 주어 true일 경우는 필수, false일 경우는 필수가 아닌 것으로 설정 가능하다.
required = false일 때 요청 파라미터에 값이 없으면 null이 저장된다.
=> @RequestParam은 파라미터를 Map으로 조회하기도 가능
@RequestParam Map<String, Object> paramMap
Map에 key, value 형태로 파라미터와 파라미터의 값이 저장되게 된다.
만약 동일한 이름의 파라미터의 값이 2개 이상일 경우 MultiValueMap을 사용하면 된다.
☞ deleteList 에 이렇게 값이 담겨있는 것을 알수 있음. (log.info 결과)
☞ 우리는 이 결과값을 가지고 (deletList) SQL문에서 DELETE문에 활용할 예정(board-mapper.xml)
// 일반게시판 수정하기기능
// 첨부파일 / 이미지 수정하기
// + 첨부파일이나 이미지 수정하는 경우, 웹서버에 저장한 파일 신경쓰지 말것. db정보만 바꿔주기
@GetMapping("/update/{boardCode}/{boardNo}")
public String updateBoard(
@PathVariable(value = "boardCode") String boardCode, // C라는 값이 전달되면 String boardCode에는 C라는 값이 저장됨
@PathVariable("boardNo") int boardNo,
Model model) {
// 게시글 정보(selectBoard), 첨부파일정보 조회후 함께 전달
BoardExt board = boardService.selectBoard(boardNo);
// 게시글내용(boardContent)은 개행처리 된 상태이기 때문에 <br>태그를 /n로 변경해주는 작업이 필요
board.setBoardContent(Utils.newLineClear(board.getBoardContent()));
// => 엔터를 쳤다면 DB에서는 <br>로 보일텐데 우리는 개행문자 처리 함수(Utils.java에서)를 만들었기 때문에 이것을 이용
// PathVariable로 자동으로 request에 등록됨
// model.addAttribute("boardCode", boardCode);
// model.addAttribute("boardNo", boardNo);
model.addAttribute("board", board);
return "board/boardUpdateForm";
}
@PostMapping("/update/{boardCode}/{boardNo}")
public String updateBoard2(
@PathVariable("boardCode") String boardCode,
@PathVariable("boardNo") int boardNo,
Model model,
Board board,
// 리다이렉트할때 request스코프에 저장시킬 데이터를 담아주는 변수
RedirectAttributes ra, // 리다이렉트 할때만 활용해야한다!
// 첨부파일
@RequestParam(value="upfile", required=false) MultipartFile upfile,
// 이미지파일들
@RequestParam(value="upfiles", required=false) List<MultipartFile> upfiles,
String deleteList // [1,2,3] 이런식으로 데이터가 담겨져있을 예정
) {
// 1) Board테이블 정보수정 => UPDATE
board.setBoardNo(boardNo); // update.jsp에는 input 태그에 boardNo가 없기 때문에 따로 넣어줘야함
board.setBoardCd(boardCode);
log.info("board ?? {} deleteList ?? {}", board, deleteList); // boardNo, boardTitle, boardContent 값이 들어가있음
int result = boardService.updateBoard(board, deleteList, upfile, upfiles);
// 2) 이미지파일들 정보수정 => UPDATE, INSERT, DELETE
// 사진이 없던 곳에 새롭게 추가된 경우 → INSERT
// 사진이 있던 곳에 새롭게 추가된 경우 → UPDATE
// 사진이 있던 곳에 삭제가 된 경우 → DELETE
// 사진이 있거나, 없던 곳에 그대로 없는 경우 → X
// 3) 작업결과에 따른 페이지 지정
if(result > 0) {
// 수정 성공시 list로 리다이렉트 되도록 설정(상대경로방식으로)
// board/list/C/
// board/update/C/6
// 현재 디렉토리에서 상위디렉토리로 2번 올라가야함
ra.addFlashAttribute("alertMsg", "게시글 수정 성공"); // 성공메시지
return "redirect:../../list/" + boardCode;
}else {
model.addAttribute("errorMsg", "게시글 수정 실패");
return "common/errorPage";
}
}
▶ BoardService.java
int updateBoard(Board board, String deleteList, MultipartFile upfile, List<MultipartFile> upfiles);
▶ BoardServiceImpl.java
=> 맨위에 application 등록
// 게시판 수정
// 어떠한 에외가 발생한다면 롤백처리하겠다! 트랜잭션 처리를 함
@Transactional(rollbackFor = {Exception.class})
@Override
public int updateBoard(Board board, String deleteList, MultipartFile upfile, List<MultipartFile> upfiles) {
board.setBoardContent(Utils.XSSHandling(board.getBoardContent()));
board.setBoardContent(Utils.newLineHandling(board.getBoardContent()));
board.setBoardTitle(Utils.XSSHandling(board.getBoardTitle()));
// 게시글 업데이트
int result = boardDao.updateBoard(board);
// 이미지 및 첨부파일 등록
String webPath = "/resources/images/board/" + board.getBoardCd() + "/";
String serverFolderPath = application.getRealPath(webPath);
// 게시글 등록이 성공적으로 완료되었을 때
if(result > 0) {
// 업로드된 이미지를 분류작업
List<BoardImg> imgList = new ArrayList();
if(upfiles != null) { // 일반게시판, 자유게시판에서는 upfiles가 null임
for(int i=0; i<upfiles.size(); i++) {
if(!upfiles.get(i).isEmpty()) {
String changeName = Utils.saveFile(upfiles.get(i), serverFolderPath);
// BoardImg객체 생성후, 필요한 값을 추가해서 imgList에 추가
BoardImg bi = new BoardImg();
bi.setChangeName(changeName);
bi.setOriginName(upfiles.get(i).getOriginalFilename());
bi.setRefBno(board.getBoardNo());
bi.setImgLevel(i);
imgList.add(bi);
}
}
}
// x버튼을 눌러서 이미지를 삭제하고자 하는 경우
if(deleteList != null && !deleteList.equals("")) {
// 삭제하기 위해서는 board_img_no가 필요함
result = boardDao.deleteBoardImg(deleteList);
}
// db에서 삭제처리 완료됐거나 혹은 게시판 업데이트 성공시
if(result > 0) {
// BoardImg객체 하나하나 업데이트
for(BoardImg bi : imgList) {
result = boardDao.updateBoardImg(bi);
// result값은 1혹은 0으로 반환
// result == 0 ? 실패 → 기존에 이미지가 없던 경우
// result == 1 ? 성공 → 기존에 이미지가 있던 경우
if(result == 0) { // 기존에 이미지가 없으면 insert문 호출
result = boardDao.insertBoardImg(bi);
// 기존에 있던 boardMapper 이용. changeName, OriginName, refBno, imgLevel 값이 다 있어서 문제없음
}
}
}
}
return result;
}
▶ BoardDao.java
1) 게시판 수정
int updateBoard(Board board);
2) 게시판 이미지 삭제
int deleteBoardImg(String deleteList);
3) 게시판 이미지 수정
int updateBoardImg(BoardImg bi);
▶ BoardDaoImpl.java
1) 게시판 수정
@Override
public int updateBoard(Board board) {
return sqlSession.update("boardMapper.updateBoard", board);
}
2) 게시판 이미지 삭제
@Override
public int deleteBoardImg(String deleteList) {
return sqlSession.delete("boardMapper.deleteBoardImg", deleteList);
}
3) 게시판 이미지 수정
@Override
public int updateBoardImg(BoardImg bi) {
return sqlSession.update("boardMapper.updateBoardImg", bi);
}
▶ board-mapper.xml
1) 게시판 수정
<update id="updateBoard" parameterType="board">
UPDATE BOARD
SET BOARD_TITLE = #{boardTitle},
BOARD_CONTENT = #{boardContent}
WHERE BOARD_NO = #{boardNo}
</update>
2) 게시판 이미지 삭제
- #을 붙이면 preparedstatement 방식에 의해 '' 가 붙혀지는데 ''가 붙혀지면 삭제가 안됨.
- 그래서 $ 기호로 바꿔주어야한다!
- $ 기호는 statement 방식
<delete id="deleteBoardImg" parameterType="string">
DELETE FROM BOARD_IMG
WHERE BOARD_IMG_NO IN (${deleteList})
</delete>
3) 게시판 이미지 수정
<update id="updateBoardImg" parameterType="boardImg">
UPDATE BOARD_IMG
SET ORIGIN_NAME = #{originName},
CHANGE_NAME = #{changeName}
WHERE REF_BNO = #{refBno} AND IMG_LEVEL = #{imgLevel}
</update>
▶ 결과
'Server > Spring' 카테고리의 다른 글
[Spring] 10-1. 채팅방 목록, 채팅방 생성 (0) | 2024.01.29 |
---|---|
[Spring] 9. 댓글 목록, 등록, 수정, 삭제(비동기식) (0) | 2024.01.26 |
[Spring] 8-4. 첨부파일 사진 다운로드 (0) | 2024.01.25 |
[Spring] 8-3. 일반 게시판 상세보기, 조회수 증가 (0) | 2024.01.25 |
[Spring] 8-2. 사진게시판(사진 여러개일 때(다중 insert문)) (0) | 2024.01.24 |