Chapter 28

ChangWoo·2023년 12월 12일
0
post-thumbnail

Part 28. 게시물의 수정과 첨부파일

  • 게시물을 수정할 때 첨부파일과 관련된 작업은 사실상 게시물 등록 작업과 상당히 유사하다.
  • 첨부파일이라는 개념 자체가 수정이 아닌 기존 파일을 삭제하고, 새로운 파일을 추가하기 때문이다.
  • 게시물 수정에서 첨부파일은 수정이라는 개념보다는 삭제 후 다시 추가한다는 개념으로 접근해야 한다.
  • 게시물의 수정에는 기존의 게시물 테이블을 수정하는 작업과 변경(새롭게 추가된)된 첨부파일을 등록하는 작업으로 이루어진다.

28.1 화면에서 첨부파일 수정

  • 게시물의 수정은 views 폴더 내에 /board/modify.jsp에서 이루어진다.
  • 게시물의 수정은 게시물의 조회화면과 유사하지만 1) 원본 이미지 확대나 다운로드 기능이 필요하지 않다는 점, 2) 게시물 조회와 달리 삭제 버튼이 있어야 하는 점이 다르다.

28.1.1 첨부파일 데이터 보여주기

  • modify.jsp 파일에서 페이지가 로딩되면 첨부파일을 가져오는 작업을 먼저 처리한다.
  • 첨부파일을 보여주는 부분의 < div > 를 추가하고, get.jsp에서 사용한 < style > 태그의 내용을 그대로 사용한다.
< modify.jsp >
<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()를 이용해 첨부파일을 보여주는 작업을 처리한다.
< modify.jsp >
<script>
$(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+"' "
		str +=" 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+"' "
		str += "data-filename='"+attach.fileName+"' data-type='"+attach.fileType+"' ><div>";
		str += "<span> "+ attach.fileName+"</span><br/>";
		str += "<img src='/resources/img/attach.png'></a>";
		str += "</div>";
		str +"</li>";
		}
	});
		$(".uploadResult ul").html(str);
				     });//end getjson
				  })();//end function
 				});
</script>
  • 게시물의 조회화면에서 수정/삭제 화면으로 이동하면 첨부된 파일들을 볼 수 있다.
  • 첨부파일을 수정하기 위해서는 게시물을 등록할 때 사용했던 버튼과 파일을 교체하기 위한 < input type='file' > 이 필요하다.
<!-- /.panel-heading -->
<div class="panel-body">
  <div class="form-group uploadDiv">
  	<input type="file" name='uploadFile' multiple="multiple">
  </div>
  <div class='uploadResult'> 
    <ul>
    </ul>
  </div>
</div>
<!--  end panel-body -->
  • 화면에서는 첨부파일이 보여지는 영역에 파일을 추가할 수 있는 < input > 태그가 추가된다.
  • 이미 등록되어 있는 첨부파일을 수정하려면 우선 기존의 특정한 파일을 삭제할 수 있도록 화면을 변경한다.
  • Ajax로 첨부파일의 데이터를 가져온 부분을 아래와 같이 수정한다.
< modify.jsp >
$(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+"' "
	str +=" 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+"' "
	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>";
	}
});
  • 화면에서는 교체하는 파일을 첨부하는 < input > 태그와 첨부파일의 이름과 삭제가 가능한 버튼이 보이게 된다.

28.1.2 첨부파일의 삭제 이벤트

  • 첨부파일 처리에서 가장 신경 쓰이는 부분은 사용자가 이미 있던 첨부파일 중에 일부를 삭제한 상태에서 게시물을 수정하지 않고 빠져나가는 상황이다.
  • 만일 사용자가 특정 첨부파일을 삭제했을 때 Ajax를 통해 업로드된 파일을 삭제하게 되면 나중에 게시물을 수정하지 않고 빠져나갔을 때 파일은 삭제된 상태가 되는 문제가 생긴다.
  • 이를 방지하려면 사용자가 특정 첨부파일을 삭제했을 때 화면에서만 삭제하고, 최종적으로 게시물을 수정했을 때 이를 반영하는 방식을 이용해야 한다.
  • 우선은 간단히 'x' 버튼을 클릭하면 사용자의 확인을 거쳐 화면상에 사라지도록 한다.
< modify.jsp >
$(".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' > 태그를 생성해 둔다.
  • 실제 파일 삭제는 게시물의 수정 버튼을 누르고 처리되는 과정에서 이루어지도록 한다.
  • 데이터베이스 정보와 비교해 수정된 게시물에 포함된 항목들 중에 기존에는 존재했으나 수정하면서 빠지는 항목이 있다면 이는 사용자가 해당 파일을 삭제하기 원하는 것이다.
  • 만일 사용자가 화면에서 특정 첨부파일을 삭제했더라도 게시물의 수정을 하지 않았다면 화면상에서만 파일이 안 보이는 것뿐이므로 나중에 다시 조회하면 원래의 첨부파일들을 확인할 수 있다.

28.1.3 첨부파일 추가

  • 첨부파일 추가는 기존의 게시물 등록 시의 처리와 동일하다.
  • 서버에 파일을 업로드하고, 이를 화면에 섬네일이나 파일의 아이콘으로 보이게 처리한다.
< modify.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);
  }

28.1.4 게시물 수정 이벤트 처리

  • 실제 기시물의 첨부파일을 수정하는 모든 작업은 서버에서 처리되도록 하므로, 게시물을 수정할 때는 게시물 등록 작업과 같이 모든 첨부파일 정보를 같이 전송해야 한다.
  • 기존의 소스 코드에서 수정 버튼을 클릭했을 때 아래와 같은 내용의 수정이 필요하다.
< modify.jsp >
var formObj = $("form");
 				$('button').on("click", function(e){
 					e.preventDefault();
 					var operation = $(this).data("oper");
 					console.log(operation);
 					if(operation === 'remove'){
 						formObj.attr("action", "/board/remove");
 					}else if(operation === 'list'){
 						// move to list
 							formObj.attr("action", "/board/list").attr("method", "get");
 							var pageNumTag = $("input[name='pageNum']").clone();
 							var amountTag = $("input[name='amount']").clone();
 							var keywordTag = $("input[name='keyword']").clone();
 							var typeTag = $("input[name='type']").clone();
 							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' value='"+jobj.data("path")+"'>";
 				          str += "<input type='hidden' name='attachList["+i+"].fileType' value='"+ jobj.data("type")+"'>";
 				        });
 				        formObj.append(str).submit();
 			        }
 					formObj.submit();
 				});

28.2 서버 측 게시물 수정과 첨부파일

  • 게시물을 수정할 때 첨부파일의 처리는 생각보다 복잡하다.
  • 가장 큰 이유는 기존의 첨부파일 중에 어떤 파일을 수정했고, 어떤 파일이 삭제되었는지 알아야 하기 때문이다.
  • 예제에서 이에 대한 처리는 우선 간단한 방법으로 게시물의 모든 첨부파일 목록을 삭제하고, 다시 첨부파일 목록을 추가하는 형태로 처리를 하는 것이다.
  • 이 경우 데이터베이스 상에는 문제가 없는데, 실제로 파일이 업로드된 폴더에는 삭제된 파일이 남아 있는 문제가 생긴다.
  • 이에 대한 처리는 주기적으로 파일과 데이터베이스를 비교하는 드으이 방법을 활용해 처리할 수 있다.

28.2.1 BoardService(Impl)의 수정

  • BoardService에서 게시물의 수정은 우선 기존의 첨부파일 관련 데이터를 삭제한 후에 다시 첨부파일 데이터를 추가하는 방식으로 동작한다.
< BoardServiceImpl 클래스 >
@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;
	}
  • 첨부파일은 수정이라기 보다는 삭제 후에 다시 추가한다는 개념이므로 게시물의 수정전과 후의 데이터베이스에 정상적으로 변경이 되는지 확인하는 것이 필요하다.
profile
한 걸음 한 걸음 나아가는 개발자

0개의 댓글