게시물 수정에서 첨부파일은 수정이라는 개념보다는 삭제 후 다시 추가한다는 개념으로 접근해야 한다.
1. 화면에서 첨부파일 수정
게시물의 수정은 views 폴더 내에 /board/modify.jsp에서 이루어진다. 게시물의 수정은 게시물의 조회화면과 유사하지만 1) 원본 이미지 확대나 다운로드 기능이 필요하지 않다는 점. 2) 게시물 조회와 달리 삭제 버튼이 있어야 하는 점이 다르다.
1-1. 첨부파일 데이터 보여주기
modify.jsp 파일에서 페이지가 로딩되면 첨부파일을 가져오는 작업을 먼저 처리한다.
첨부파일을 보여주는 부분의 <div>를 추가하고, get.jps에서 사용한 <style> 태그의 내용을 그대로 사용한다.
<div class='bigPictureWrapper'>
<div class='bigPicture'>
</div>
</div>
<style>
.uploadResult {
width:100%;
background-color: gray;
}
.uploadResult ul{
display:flex;
flex-flow: row;
justify-content: center;
align-items: center;
}
.uploadResult ul li {
list-style: none;
padding: 10px;
align-content: center;
text-align: center;
}
.uploadResult ul li img{
width: 100px;
}
.uploadResult ul li span {
color:white;
}
.bigPictureWrapper {
position: absolute;
display: none;
justify-content: center;
align-items: center;
top:0%;
width:100%;
height:100%;
background-color: gray;
z-index: 100;
background:rgba(255,255,255,0.5);
}
.bigPicture {
position: relative;
display:flex;
justify-content: center;
align-items: center;
}
.bigPicture img {
width:600px;
}
</style>
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">Files</div>
<!-- /.panel-heading -->
<div class="panel-body">
<div class='uploadResult'>
<ul>
</ul>
</div>
</div>
<!-- end panel-body -->
</div>
<!-- end panel-body -->
</div>
<!-- end panel -->
</div>
<!-- /.row -->
jQuery의 $(document).ready()를 이용해서 첨부파일을 보여주는 작업을 처리한다.
$(document).ready(function() {
(function(){
var bno = '<c:out value = "${board.bno}"/>';
$.getJSON("/board/getAttachList", {bno:bno}, function(arr){
console.log(arr);
var str = "";
$(arr).each(function(i, attach){
// image type
if(attach.fileType){
var fileCallPath = encodeURIComponent(attach.uploadPath + "/s_" + attach.uuid + "_" + attach.fileName);
str+= "<li data-path = '" + attach.uploadPath + "' data-uuid = '" + attach.uuid + "'data-filename='"
+ attach.fileName + "' data-type = '" + attach.fileType + "'><div>";
str+= "<img src = '/display?fileName=" + fileCallPath + "'>";
str+= "</div>";
str+ "</li>";
}else {
str += "<li data-path = '" + attach.uploadPath + "' data-uuid = '" + attach.uuid + "'data-filename = '"
+ attach.fileName + "'data-type = '" + attach.fileType + "'><div>";
str += "<span>" + attach.fileName + "</span><br/>";
str += "<img src = '/resources/img/attach.png'></a>";
str + "</li>";
}
});
$(".uploadResult ul").html(str);
});
})();
});
게시물의 조회화면에서 수정/삭제 화면으로 이동하면 첨부된 파일들을 볼 수 있다.
첨부파일을 수정하기 위해서는 게시물을 등록할 때 사용했던 버튼과 파일을 교체하기 위한 <inpur type='file'>이 필요하다.
<!-- /.panel-heading -->
<div class="panel-body">
<div class = "form-group uploadDiv">
<input type = "file" name = 'uploadFile' multiple="multiple">
</div>
화면에서는 첨부파일이 보여지는 영역에 파일을 추가할 수 있는 <input> 태그가 추가된다.
이미 등록되어 있는 첨부파일을 수정하려면 우선 기존의 특정한 파일을 삭제할 수 있도록 화면을 변경한다.
Ajax로 첨부파일의 데이터를 가져온 부분을 아래와 같이 수정한다.
$(arr).each(function(i, attach){
// image type
if(attach.fileType){
var fileCallPath = encodeURIComponent(attach.uploadPath + "/s_" + attach.uuid + "_" + attach.fileName);
str+= "<li data-path = '" + attach.uploadPath + "' data-uuid = '" + attach.uuid + "'data-filename='"
+ attach.fileName + "' data-type = '" + attach.fileType + "'><div>";
str+= "<span> " + attach.fileName + "</span>";
str+= "<button type = 'button' data-file=\'" + fileCallPath + "\' data-type='image'";
str+= "class = 'btn btn-warning btn-circle'><i class = 'fa fa-times'></i></button><br>";
str+= "<img src = '/display?fileName=" + fileCallPath + "'>";
str+= "</div>";
str+ "</li>";
}else {
str += "<li data-path = '" + attach.uploadPath + "' data-uuid = '" + attach.uuid + "'data-filename = '"
+ attach.fileName + "'data-type = '" + attach.fileType + "'><div>";
str += "data-filename='" + attach.fileName + "' data-type = '" + attach.fileType + "'><div>";
str += "<span>" + attach.fileName + "</span><br/>";
str+= "<button type = 'button' data-file = \'" + fileCallPath + "\' data-type = 'file'";
str+= " class = 'btn btn-warning btn-circle'><i class = 'fa fa-times'></i></button><br>";
str += "<img src = '/resources/img/attach.png'></a>";
str += "</div>";
str + "</li>";
}
});
1-2. 첨부파일의 삭제 이벤트
첨부파일 처리에서 가장 신경 쓰이는 부분은 사용자가 이미 있던 첨부파일 중에 일부를 삭제한 상태에서 게시물을 수정하지 않고 빠져나가는 상황이다. 만일 사용자가 특정 첨부파일을 삭제했을 때 Ajax를 통해서 업로드된 파일을 삭제하게 되면 나중에 게시물을 수정하지 않고 빠져나갔을 때 파일은 삭제된 상태가 되는 문제가 생긴다. 이를 방지하려면 사용자가 특정 첨부파일을 삭제했을 때 화면에서만 삭제하고, 최종적으로 게시물을 수정했을 때 이를 반영하는 방식을 이용해야 한다.
우선은 간단히 첨부파일의 'x' 버튼을 클릭하면 사용자의 확인을 거쳐 화면상에 사라지도록 한다.
$(".uploadResult").on("click", "button", function(e){
console.log("delete file");
if(confirm("Remove this file? ")){
var targetLi = $(this).closest("li");
targetLi.remove();
}
});
실제 파일의 삭제는 게시물의 수정 작업 시 이루어져야 하기 때문에 만일 사용자가 특정 첨부파일을 삭제했다면 삭제하는 파일에 대한 정보를 보관할 필요가 있따. 다행히도 <li> 태그 내에 필요한 모든 정보가 들어 있으므로, 이를 이용해서 <input type = 'hidden'> 태그를 생성해 둔다.
실제 파일 삭제는 게시물의 수정 버튼을 누르고 처리되는 과정에서 이루어지도록 한다. 데이터베이스 정보와 비교해서 수정된 게시물에 포함된 항목들 중에 기존에는 존재했으나 수정하면서 빠지는 항목이 있다면 이는 사용자가 해당 파일을 삭제하기 위하는 것이다. 만일 사용자가 화면에서 특정 첨부파일을 삭제했더라도 게시물의 수정을 하지 않았다면 화면상에서만 파일에 안 보이는 것뿐이므로 나중에 다시 조회하면 원래의 첨부파일들을 확인할 수 있다.
1-3. 첨부파일 추가
첨부파일 추가는 기존의 게시물 등록 시의 처리와 동일하다. 서버에 파일을 업로드하고, 이를 화면에 섬네일이나 파일의 아이콘으로 보이게 처리한다.
- modify.jsp의 일부에 아래의 코드를 추가한다 (register.jsp에 있음)
var regex = new RegExp("(.*?)\.(exe|sh|zip|alz)$");
var maxSize = 5242880; //5MB
function checkExtension(fileName, fileSize){
if(fileSize >= maxSize){
alert("파일 사이즈 초과");
return false;
}
if(regex.test(fileName)){
alert("해당 종류의 파일은 업로드할 수 없습니다.");
return false;
}
return true;
}
$("input[type='file']").change(function(e){
var formData = new FormData();
var inputFile = $("input[name='uploadFile']");
var files = inputFile[0].files;
for(var i = 0; i < files.length; i++){
if(!checkExtension(files[i].name, files[i].size) ){
return false;
}
formData.append("uploadFile", files[i]);
}
$.ajax({
url: '/uploadAjaxAction',
processData: false,
contentType: false,data:
formData,type: 'POST',
dataType:'json',
success: function(result){
console.log(result);
showUploadResult(result); //업로드 결과 처리 함수
}
}); //$.ajax
});
function showUploadResult(uploadResultArr){
if(!uploadResultArr || uploadResultArr.length == 0){ return; }
var uploadUL = $(".uploadResult ul");
var str ="";
$(uploadResultArr).each(function(i, obj){
if(obj.image){
var fileCallPath = encodeURIComponent( obj.uploadPath+ "/s_"+obj.uuid +"_"+obj.fileName);
str += "<li data-path='"+obj.uploadPath+"'";
str +=" data-uuid='"+obj.uuid+"' data-filename='"+obj.fileName+"' data-type='"+obj.image+"'"
str +" ><div>";
str += "<span> "+ obj.fileName+"</span>";
str += "<button type='button' data-file=\'"+fileCallPath+"\' "
str += "data-type='image' class='btn btn-warning btn-circle'><i class='fa fa-times'></i></button><br>";
str += "<img src='/display?fileName="+fileCallPath+"'>";
str += "</div>";
str +"</li>";
}else{
var fileCallPath = encodeURIComponent( obj.uploadPath+"/"+ obj.uuid +"_"+obj.fileName);
var fileLink = fileCallPath.replace(new RegExp(/\\/g),"/");
str += "<li "
str += "data-path='"+obj.uploadPath+"' data-uuid='"+obj.uuid+"' data-filename='"+obj.fileName+"' data-type='"+obj.image+"' ><div>";
str += "<span> "+ obj.fileName+"</span>";
str += "<button type='button' data-file=\'"+fileCallPath+"\' data-type='file' "
str += "class='btn btn-warning btn-circle'><i class='fa fa-times'></i></button><br>";
str += "<img src='/resources/img/attach.png'></a>";
str += "</div>";
str +"</li>";
}
});
uploadUL.append(str);
}
1-4. 게시물 수정 이벤트 처리
실제 게시물의 첨부파일을 수정하는 모든 작업은 서버에서 처리되도록 하므로, 게시물을 수정할 때는 게시물 등록 작업과 같이 모든 첨부파일 정보를 같이 전송해야 한다. 기존의 소스 코드에서 수정 버튼을 클릭했을 때 아래와 같은 내용의 수정이 필요하다.
formObj.empty();
formObj.append(pageNumTag);
formObj.append(amountTag);
formObj.append(keywordTag);
formObj.append(typeTag);
}else if(operation === 'modify'){
console.log("submit clicked");
var str = "";
$(".uploadResult ul li").each(function(i,obj){
var jobj = $(obj);
console.dir(jobj);
str+= "<input type = 'hidden' name = 'attachList["+i+"].fileName' value = '" + jobj.data("filename")+"'>";
str+= "<input type = 'hidden' name = 'attachList["+i+"].uuid' value = '" + jobj.data("uuid")+"'>";
str+= "<input type = 'hidden' name = 'attachList["+i+"].uploadPath' vlaue = '" + jobj.data("path")+"'>";
str+= "<input type = 'hidden' name = 'attachList["+i+"].fileType' value = '"+jobj.data("type")+"'>";
});
formObj.append(str).submit();
}
formObj.submit();
2. 서버 측 게시물 수정과 첨부파일
게시물을 수정할 때 첨부파일의 처리는 생각보다 복잡하다. 가장 큰 이유는 기존의 첨부파일 중에 어떤 파일을 수정했고, 어떤 파일이 삭제되었는지 알아야 하기 때문이다. 예제에서 이에 대한 처리는 우선 간단한 방법으로 게시물의 모든 첨부파일 목록을 삭제하고, 다시 첨부파일 목록을 추가하는 형태로 처리하는 것이다.
이 경우 데이터베이스 상에는 문제가 없는데, 실제로 파일이 업로드된 폴더에는 삭데된 파일이 남아 있는 문제가 생긴다. 이에 대한 처리는 주기적으로 파일과 데이터베이스를 비교하는 등의 방법을 활용해서 처리할 수 있다.
2-1. BoardService의 수정
BoardService에서 게시물의 수정은 우선 기존의 첨부파일 관련 데이터를 삭제한 후에 다시 첨부파일 데이터를 추가하는 방식으로 동작한다.
- BoardServiceImpl.java
@Transactional
@Override
public boolean modify(BoardVO board) {
log.info("modify................." + board);
attachMapper.deleteAll(board.getBno());
boolean modifyResult = mapper.update(board) == 1;
if(modifyResult && board.getAttachList() != null && board.getAttachList().size() > 0) {
board.getAttachList().forEach(attach -> {
attach.setBno(board.getBno());
attachMapper.insert(attach);
});
}
return modifyResult;
}
첨부파일은 수정이라기 보다는 삭제 후에 다시 추가한다는 개념이므로 게시물의 수정 전과 후의 데이터베이스에 정상적으로 변경이 되는지 확인하는 것이 필요하다.
'Spring' 카테고리의 다른 글
[39] spring web security를 이용한 로그인 처리 - spring web security 란? (1) | 2020.01.28 |
---|---|
[38] 파일 업로드 처리 - 잘못 업로드된 파일 삭제 (Quartz 라이브러리) (0) | 2020.01.20 |
org.springframework.jdbc.UncategorizedSQLException: ### Error updating database. Cause: java.sql.SQLException: 해당 위치에 지원되지 않는 SQL92 토큰: 31 (0) | 2020.01.10 |
[35] 파일 업로드 처리 - 게시물의 조회와 첨부파일 (0) | 2020.01.10 |
[34] 파일 업로드 처리 - 프로젝트의 첨부파일 - 등록 (0) | 2020.01.08 |