-
이번에 프로젝트를 진행하면서 관리 페이지나 커뮤니티 페이지 등에 썸머노트를 적용하기로 했다.
썸머노트란?
오픈소스 이지윅 에디터로 실제 페이지 레이아웃과 유사한 형식으로 웹페이지를 작성할 수 있는 HTML 편집기의 한 종류
Summernote - Super Simple WYSIWYG editor
Super Simple WYSIWYG Editor on Bootstrap Summernote is a JavaScript library that helps you create WYSIWYG editors online.
summernote.org
이렇게 별다른 CSS나 Javascript소스를 직접 만들 필요 없이 실제로 사용하는 에디터툴을 사용할 수 있게 해준다.
위 링크에서 관련 소스코드들을 받아 프로젝트에 넣고 HTML에 임포트한 후, textarea태그를 형성하여 썸머노트를 불러오는 javascript코드를 작성해주면 끝!
썸머노트 에디터를 페이지에 불러와 해당 내용들을 받는 것까지는 성공적이었으나, 이후 프로젝트를 진행하면서 썸머노트를 사용한 제품들을 불러올 때 로딩시간이 지나치게 길어지는 문제가 발생했다.
썸머노트의 경우, 내용에 이미지 파일을 삽입하면 Base64형태로 저장한다.
Base64란 8비트 이진 데이터를 문자 코드에 영향을 받지 않는 공통 ASCII 영역의 문자들로만 이루어진 일련의 문자열로 바꾸는 인코딩 방식을 뜻하는데, 즉 이 형태로 이미지 파일을 저장하게 되면 서버에 큰 무리가 간다는 것이다.
이를 해결하기 위해 썸머노트에 이미지가 업로드되는 이벤트가 발생하는 순간, 해당 이미지를 서버에 별도로 저장하고, 이 경로를 받아와 이미지 태그의 src에 적용하는 방법을 사용하기로 했다.
썸머노트 이미지 업로드 시 서버에 별도로 저장하기
https://summernote.org/deep-dive/#onimageupload
Summernote - Super Simple WYSIWYG editor
Super Simple WYSIWYG Editor on Bootstrap Summernote is a JavaScript library that helps you create WYSIWYG editors online.
summernote.org
썸머노트에는 이미지 업로드 관련 콜백 함수 onImageUpload가 있다.
이를 사용하면 간단하게 이미지 업로드 관련 이벤트를 지정할 수 있다.
12345678910111213141516171819202122232425262728293031323334353637383940414243<script>let imgs = "";$(document).ready(function() {//이벤트 등록 썸머노트$('#summernoteEnroll').summernote({height: 300,minHeight: 300,maxHeight: 300,focus: false,lang: "ko-KR",placeholder: '최대 2048자까지 쓸 수 있습니다',callbacks:{onImageUpload : function(files){uploadSummernoteImageFile(files[0], this);}}});function uploadSummernoteImageFile(file, editor){const data = new FormData();data.append("file", file);data.append("keyword", "promotion");$.ajax({data : data,type : "POST",url : "${pageContext.request.contextPath}/uploadSummernoteImageFile",contentType : false,processData : false,success(data){console.log(data);//imgs 변수 안에 /filename 추가. /는 구분자imgs += "/" + data["filename"]; //resources/upload/promotion/dfjklsa$('#summernoteEnroll').summernote('insertImage', "${pageContext.request.contextPath}/resources/upload/promotion/"+data["filename"]);},error : console.log});}});</script>cs 이미지 업로드 이벤트가 실행되는 순간, uploadSummernoteImageFile메소드가 실행된다.
새로운 FormData객체를 만들어 여기에 매개변수로 전달해준 file객체를 담는다.
이벤트 관련 페이지뿐 아니라 다른 관리 페이지에서도 사용할 수 있도록 keyword를 지정한다.
123456789101112131415161718192021222324252627@PostMapping(value="/uploadSummernoteImageFile")@ResponseBodypublic Map<String, Object> uploadSummernoteImageFile(@RequestParam("file") MultipartFile upFile, @RequestParam String keyword) {log.debug("file = {}, keyword = {}", upFile, keyword);Map<String, Object> map = new HashMap<>();String saveDirectory = application.getRealPath("/resources/upload/"+keyword);// /resources/upload/promotionString originalFilename = upFile.getOriginalFilename();String renamedFilename = DevrunUtils.getRenamedFilename(originalFilename);//20210111129083908File dest = new File(saveDirectory, renamedFilename);try {InputStream fileStream = upFile.getInputStream();FileUtils.copyInputStreamToFile(fileStream, dest);map.put("url", saveDirectory);map.put("filename", renamedFilename);map.put("responseCode", "success");} catch (IOException e) {FileUtils.deleteQuietly(dest);map.put("responseCode", "error");e.printStackTrace();}return map;}cs Controller에서 서버에 업로드할 파일과 키워드값을 받는다.
저장소 위치와 변경 파일 이름을 지정하고, InputStream을 통해 파일을 읽어낸 후 서버에 변경 파일명으로 해당 파일을 저장한다.
저장에 성공하면 파일 위치와 이름, 성공 여부를 담은 Map객체를 리턴한다.
성공하면 다시 Client단에서 썸머노트의 insertImage메소드를 통해 받아온 경로에 위치한 파일을 insert한다.
파일을 서버에 저장해 썸머노트의 textarea에 불러오는 것은 성공했지만 다른 문제가 있었다.
글을 작성하다가 해당 페이지를 벗어난 경우는?
필요없어진 이미지 파일이 서버에 지속적으로 쌓이게 되고, 이게 이후에 더미 데이터가 될 거란 생각이 들었다.
이를 해결하기 위해서는 사용자가 창을 벗어나려 하는 순간(글 작성 버튼을 누른 경우 예외) 이벤트를 발생시켜 서버에 저장된 파일을 삭제해야 했다.
페이지를 벗어날 시 서버에 저장되어 있는 이미지 파일 삭제하기
여기서는 Javascript의 beforeunload이벤트를 사용했다.
https://developer.mozilla.org/ko/docs/Web/API/Window/beforeunload_event
Window: beforeunload 이벤트 - Web API | MDN
beforeunload 이벤트는 문서와 그 리소스가 언로드 되기 직전에 window에서 발생합니다.
developer.mozilla.org
해당 이벤트는 사용자가 새로고침, 뒤로 가기, 브라우저 닫기, 폼 제출 등의 액션을 취하면 발동한다.
12345678910111213141516171819202122<script>//페이지 벗어날 때 썸머노트 안의 이미지 파일을 서버 상에서 삭제$(document).ready(function () {//페이지 이동 전에 beforeunload함수 실행$(window).bind('beforeunload',function (e) {//비동기 요청을 통해 서버에 저장된 이미지 파일 삭제$.ajax({url : "${pageContext.request.contextPath}/deleteSummernoteImageFile",data : {imgs : imgs,keyword : "promotion"},method : "POST",success(data){console.log(data);},error : console.log});//크롬은 문자열을 리턴해야만 페이지를 나가겠냐는 confirm창이 뜬다.return "";});});</script>cs 위에서 빈 문자열로 선언해 두었던 imgs변수, 이미지를 업로드하면 구분자를 더한 파일명을 계속 더해나가도록 조치해둔 이 변수가 이때 쓰인다.
해당 문자열과 키워드를 담아 deleteSummernoteImageFile url을 호출한다.
123456789101112131415@PostMapping(value="/deleteSummernoteImageFile")@ResponseBodypublic void deleteSummernoteImageFile(@RequestParam("imgs") String imgs, @RequestParam String keyword) {log.debug("imgs = {}", imgs);String saveDirectory = application.getRealPath("/resources/upload/"+keyword);//csv형식으로 받은 이미지 업로드 파일 이름들 분리해서 String배열에 담기String[] filenames = imgs.split("/"); // filenamefilenamefilname//특정 이름을 가진 파일이 디렉토리에 있을 경우 파일 삭제for(String filename : filenames) {File file = new File(saveDirectory + "/" + filename);if(file.exists()) file.delete();}}cs Controller단에서 split메소드를 이용해 이미지 파일 이름들을 String배열에 담고, for문을 돌려 해당 이름을 가진 파일이 존재하는 경우 서버에서 삭제를 요청했다.
이 코드의 경우 여러 파일 업로드 및 삭제에 대한 테스트가 이루어지지 않았다.
리팩토링의 필요성을 느끼고 있다... 파이팅!
'도움닫기 중입니다' 카테고리의 다른 글
Gmail SMTP로 자바에서 이메일 보내기 (0) 2022.02.10 Java로 쇼핑몰 좋아요 기능 구현하기 (0) 2022.02.10 아임포트(iamport) API로 결제부터 취소까지 (0) 2022.01.25 댓글