[Spring] 22. 게시물 상세/수정 - 파일 업로드/삭제 기능 추가개발자가 되기까지 (2023.08.16~2024.04.15)/[Spring] Basic Web2024. 1. 21. 16:56
Table of Contents
detail.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<jsp:include page="../layout/header.jsp"></jsp:include>
<jsp:include page="../layout/nav.jsp"></jsp:include>
<div class="container-md">
<c:set value="${bdto.bvo }" var="bvo"></c:set>
// ... (기존 코드)
<!-- 파일 line -->
<c:set value="${bdto.flist }" var="flist"></c:set>
<div class="col-12">
<label for="f" class="form-label"></label>
<ul class="list-group list-group-flush">
<c:forEach items="${flist }" var="fvo">
<li class="list-group-item">
<c:choose>
<c:when test="${fvo.fileType == 1 }">
<div>
<img alt="" src="/upload/${fvo.saveDir }/${fvo.uuid}_th_${fvo.fileName}">
</div>
</c:when>
<c:otherwise>
<div>
<!-- 일반 파일을 표시할 아이콘 -->
<a href="/upload/${fvo.saveDir }/${fvo.uuid}_${fvo.fileName}" download="${fvo.fileName }">
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="currentColor" class="bi bi-file-earmark-arrow-down" viewBox="0 0 16 16">
<path d="M8.5 6.5a.5.5 0 0 0-1 0v3.793L6.354 9.146a.5.5 0 1 0-.708.708l2 2a.5.5 0 0 0 .708 0l2-2a.5.5 0 0 0-.708-.708L8.5 10.293V6.5z"/>
<path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2zM9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5v2z"/>
</svg>
</a>
</div>
</c:otherwise>
</c:choose>
<div class="ms-2 me-auto">
<div class="fw-bold">
${fvo.fileName}<br>
<span class="badge rounded-pill text-bg-secondary">${fvo.fileSize }Byte</span>
</div>
</div>
</li>
</c:forEach>
</ul>
</div>
<a href="/board/modify?bno=${bvo.bno }"><button type="submit" class="btn btn-success">수정</button></a>
<a href="/board/remove?bno=${bvo.bno }"><button type="button" class="btn btn-danger">삭제</button></a>
<a href="/board/list"><button type="submit" class="btn btn-primary">목록</button></a>
<br><br><hr>
// ... (기존 코드)
</div>
<script type="text/javascript">
let bnoVal = `<c:out value="${bvo.bno}"/>`;
console.log(bnoVal);
</script>
<script src="/resources/js/boardComment.js"></script>
<script type="text/javascript">
spreadCommentList(bnoVal);
</script>
<jsp:include page="../layout/footer.jsp"></jsp:include>
modify.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<jsp:include page="../layout/header.jsp"></jsp:include>
<jsp:include page="../layout/nav.jsp"></jsp:include>
<div class="container-md">
<c:set value="${bdto.bvo }" var="bvo"></c:set>
// ... (기존 코드)
<!-- file line -->
<c:set value="${bdto.flist }" var="flist"></c:set>
<div class="col-12">
<label for="f" class="form-label"></label>
<ul class="list-group list-group-flush">
<c:forEach items="${flist }" var="fvo">
<li class="list-group-item">
<c:choose>
<c:when test="${fvo.fileType == 1 }">
<div>
<img alt="" src="/upload/${fvo.saveDir }/${fvo.uuid}_th_${fvo.fileName}">
</div>
</c:when>
<c:otherwise>
<div>
<!-- 일반 파일을 표시할 아이콘 -->
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" fill="currentColor" class="bi bi-file-earmark-arrow-down" viewBox="0 0 16 16">
<path d="M8.5 6.5a.5.5 0 0 0-1 0v3.793L6.354 9.146a.5.5 0 1 0-.708.708l2 2a.5.5 0 0 0 .708 0l2-2a.5.5 0 0 0-.708-.708L8.5 10.293V6.5z"/>
<path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2zM9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5v2z"/>
</svg>
</div>
</c:otherwise>
</c:choose>
<div class="ms-2 me-auto">
<div class="fw-bold">
${fvo.fileName}<br>
<span class="badge rounded-pill text-bg-secondary">${fvo.fileSize }Byte</span>
<button type=button data-uuid="${fvo.uuid }" class="btn btn-outline-danger btn-sm file-x">X</button>
</div>
</div>
</li>
</c:forEach>
</ul>
</div>
<!-- file 입력 라인 추가 -->
<div class="mb-3">
<input type="file" name="files" class="form-control" id="files" multiple="multiple" style="display: none"><br>
<!-- 파일 버튼 트리거 사용하기 위해서 주는 버튼 -->
<button type="button" class="btn btn-primary" id="trigger">파일 업로드</button>
</div>
<!-- 파일 목록 표시라인 -->
<div class="mb-3" id="fileZone">
</div>
<button type="submit" class="btn btn-success" id="regBtn">수정</button>
<a href="/board/remove?bno=${bvo.bno }"><button type="button" class="btn btn-danger">삭제</button></a>
<a href="/board/list"><button type="submit" class="btn btn-primary">목록</button></a><br>
</form>
</div>
<script src="/resources/js/boardFile.js"></script>
<jsp:include page="../layout/footer.jsp"></jsp:include>
boardFile.js
// ... (기존 코드)
document.addEventListener('click', (e)=>{
if(e.target.classList.contains('file-x')) {
let uuid = e.target.dataset.uuid;
removeFileToServer(uuid).then(result =>{
if(result == '1') {
console.log("파일 삭제 성공");
e.target.closest('li').remove();
}
})
}
});
async function removeFileToServer(uuid) {
try {
const url = "/board/file/" + uuid;
const config ={
method : "delete"
}
const resp = await fetch(url, config);
const result = await resp.text();
return result;
} catch (error) {
console.log(error);
}
};
BoardController.java
package com.basicWeb.www.controller;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.basicWeb.www.domain.BoardDTO;
import com.basicWeb.www.domain.BoardVO;
import com.basicWeb.www.domain.FileVO;
import com.basicWeb.www.domain.PagingVO;
import com.basicWeb.www.handler.FileHandler;
import com.basicWeb.www.handler.PagingHandler;
import com.basicWeb.www.service.BoardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequestMapping("/board/*")
@RequiredArgsConstructor
@Controller
public class BoardController {
private final BoardService bsv;
private final FileHandler fh;
// ... (기존 코드)
@GetMapping({"/detail", "/modify"})
public void detail (Model m, @RequestParam("bno") long bno) {
m.addAttribute("bdto", bsv.getDetail(bno));
}
@PostMapping("/modify")
public String modify (BoardVO bvo, RedirectAttributes re, @RequestParam(name="files", required = false) MultipartFile[] files) {
List<FileVO> flist = null;
if(files[0].getSize() > 0) {
flist = fh.uploadFiles(files);
}
int isOk = bsv.modify(new BoardDTO(bvo, flist));
re.addAttribute("bno", bvo.getBno());
return "redirect:/board/detail?bno=" +bvo.getBno();
}
@GetMapping("/remove")
public String remove (BoardVO bvo) {
bsv.remove(bvo);
return "redirect:/board/list";
}
@DeleteMapping(value="/file/{uuid}", produces= MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<String> removeFile(@PathVariable("uuid") String uuid) {
int isOk = bsv.removeFile(uuid);
return isOk > 0 ?
new ResponseEntity<String>("1", HttpStatus.OK) :
new ResponseEntity<String>("0", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
BoardService.interface
package com.basicWeb.www.service;
import java.util.List;
import com.basicWeb.www.domain.BoardDTO;
import com.basicWeb.www.domain.BoardVO;
import com.basicWeb.www.domain.PagingVO;
public interface BoardService {
int register(BoardDTO bdto);
List<BoardVO> getList(PagingVO pgvo);
BoardDTO getDetail(long bno);
int modify(BoardDTO bdto);
void remove(BoardVO bvo);
int totalCount(PagingVO pgvo);
int removeFile(String uuid);
}
BoardServiceImpl.java
package com.basicWeb.www.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.basicWeb.www.domain.BoardDTO;
import com.basicWeb.www.domain.BoardVO;
import com.basicWeb.www.domain.FileVO;
import com.basicWeb.www.domain.PagingVO;
import com.basicWeb.www.repository.BoardDAO;
import com.basicWeb.www.repository.CommentDAO;
import com.basicWeb.www.repository.FileDAO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequiredArgsConstructor
@Service
public class BoardServiceImpl implements BoardService{
private final BoardDAO bdao;
private final CommentDAO cdao;
private final FileDAO fdao;
// ... (기존 코드)
@Transactional
@Override
public BoardDTO getDetail(long bno) {
bdao.upReadCount(bno, 1);
BoardVO bvo = bdao.getDetail(bno);
List<FileVO> flist = fdao.getFileList(bno);
BoardDTO bdto = new BoardDTO(bvo, flist);
return bdto;
}
@Transactional
@Override
public int modify(BoardDTO bdto) {
int isOk = bdao.upReadCount(bdto.getBvo().getBno(), -2);
if(bdto.getFlist() == null) {
return isOk;
}
if(isOk > 0 && bdto.getFlist().size() > 0) {
for(FileVO fvo : bdto.getFlist()) {
fvo.setBno(bdto.getBvo().getBno());
isOk += fdao.insertFile(fvo);
}
}
return isOk;
}
// ... (기존 코드)
@Override
public int removeFile(String uuid) {
// TODO Auto-generated method stub
return fdao.deleteFile(uuid);
}
}
BoardDAO.interface
package com.basicWeb.www.repository;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.basicWeb.www.domain.BoardVO;
import com.basicWeb.www.domain.PagingVO;
public interface BoardDAO {
int register(BoardVO bvo);
List<BoardVO> getList(PagingVO pgvo);
BoardVO getDetail(long bno);
void update(BoardVO bvo);
void delete(BoardVO bvo);
int upReadCount(@Param("bno") long bno, @Param("count") int count);
int totalCount(PagingVO pgvo);
void getCmtCount();
long selectOneBno();
}
FileDAO.interface
package com.basicWeb.www.repository;
import java.util.List;
import com.basicWeb.www.domain.FileVO;
public interface FileDAO {
int insertFile(FileVO fvo);
List<FileVO> getFileList(long bno);
int deleteFile(String uuid);
}
fileMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.basicWeb.www.repository.FileDAO">
<insert id="insertFile">
INSERT INTO file (uuid, save_dir, file_name, file_type, bno, file_size)
VALUES (#{uuid}, #{saveDir}, #{fileName}, #{fileType}, #{bno}, #{fileSize})
</insert>
<select id="getFileList" resultType="com.basicWeb.www.domain.FileVO">
SELECT * FROM file WHERE bno = #{bno}
</select>
<delete id="deleteFile">
DELETE FROM file WHERE uuid = #{uuid}
</delete>
</mapper>
<게시물 상세 페이지>
<게시물 수정 페이지>
<게시물 수정 후 페이지>
<게시물 수정 - 사진 삭제>
(+ 게시물 삭제할 때, 업로드 되어있던 파일도 함께 삭제)
BoardServiceImpl.java
package com.basicWeb.www.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.basicWeb.www.domain.BoardDTO;
import com.basicWeb.www.domain.BoardVO;
import com.basicWeb.www.domain.FileVO;
import com.basicWeb.www.domain.PagingVO;
import com.basicWeb.www.repository.BoardDAO;
import com.basicWeb.www.repository.CommentDAO;
import com.basicWeb.www.repository.FileDAO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequiredArgsConstructor
@Service
public class BoardServiceImpl implements BoardService{
private final BoardDAO bdao;
private final CommentDAO cdao;
private final FileDAO fdao;
// ... (기존 코드)
@Transactional
@Override
public BoardDTO getDetail(long bno) {
bdao.upReadCount(bno, 1);
BoardVO bvo = bdao.getDetail(bno);
List<FileVO> flist = fdao.getFileList(bno);
BoardDTO bdto = new BoardDTO(bvo, flist);
return bdto;
}
@Transactional
@Override
public int modify(BoardDTO bdto) {
int isOk = bdao.upReadCount(bdto.getBvo().getBno(), -2);
if(bdto.getFlist() == null) {
return isOk;
}
if(isOk > 0 && bdto.getFlist().size() > 0) {
for(FileVO fvo : bdto.getFlist()) {
fvo.setBno(bdto.getBvo().getBno());
isOk += fdao.insertFile(fvo);
}
}
return isOk;
}
@Override
public void remove(BoardVO bvo) {
cdao.deleteAll(bvo);
fdao.deleteAll(bvo);
bdao.delete(bvo);
}
//... (기존 코드)
}
FileDAO.interface
package com.basicWeb.www.repository;
import java.util.List;
import com.basicWeb.www.domain.BoardVO;
import com.basicWeb.www.domain.FileVO;
public interface FileDAO {
int insertFile(FileVO fvo);
List<FileVO> getFileList(long bno);
int deleteFile(String uuid);
void deleteAll(BoardVO bvo);
}
fileMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.basicWeb.www.repository.FileDAO">
<insert id="insertFile">
INSERT INTO file (uuid, save_dir, file_name, file_type, bno, file_size)
VALUES (#{uuid}, #{saveDir}, #{fileName}, #{fileType}, #{bno}, #{fileSize})
</insert>
<select id="getFileList" resultType="com.basicWeb.www.domain.FileVO">
SELECT * FROM file WHERE bno = #{bno}
</select>
<delete id="deleteFile">
DELETE FROM file WHERE uuid = #{uuid}
</delete>
<delete id="deleteAll">
DELETE FROM file WHERE bno = #{bno}
</delete>
</mapper>
[Spring] 22. 게시물 상세/수정 - 파일 업로드/삭제 기능 추가
(다음 게시물 예고편)
[Spring] 23. Security 설정 (customAuthMemberServic 제외)
728x90
@rlozlr :: 얼렁뚱땅 개발자
얼렁뚱땅 주니어 개발자
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!