무료 에디터 사용하기
네이버 구글 등 api가 존재하지만 간편하고 깔끔하게 사용하기 좋은 것 같아서 summernote를 선택했습니다.
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
1. 필요 파일 설치
경로 : hompage > 'get started > 'download compiled'
사용가능한 소스들이 모두 들어있는데 그 중에서 자신의 프로젝트에 맞는 파일들을 선택해서 넣어주면 됩니다.
부트스트랩과 함께 사용할 수도 있지만 저는 기본 라이트한 내용을 적용했습니다.
summernote-lite.css , summernote-lite.js
언어 적용도 필요할 경우, summernote-ko-KR.js 파일도 같이 추가해줍니다.
<link rel="stylesheet" href="${contextPath}/resource/summernote/summernote-lite.css">
<script src="${contextPath}/resource/summernote/summernote-lite.js"></script>
<script src="${contextPath}/resource/summernote/summernote-ko-KR.js"></script>
2. html 구조
<div id="summernote">Hello Summernote</div>
<form method="post">
<textarea id="summernote" name="editordata"></textarea>
</form>
summernote 실행
3. 스크립트로 적용 및 설정
적용한 높이 포커스,언어 이외에도 사이트에서 많은 옵션들을 확인 할 수 있습니다.
$('#summernote').summernote({
height: 300, // set editor height
minHeight: null, // set minimum height of editor
maxHeight: null, // set maximum height of editor
focus: true // set focus to editable area after initializing summernote
lang: 'ko-KR' // default: 'en-US'
});
4. 게시글 이미지 업로드 구현
기존 에디터에서 이미지를 업로드 해보면 base64 텍스트형태로 포함 되는 것을 확인 할 수 있습니다.
인코딩된 문자열이 이미지로 들어가 문서내에 이미지가 포함되는 것입니다.
하지만 데이터가 많이 늘어날 뿐만 아니라 이미지 관리를 할 수 없는 문제점이 있습니다.
이미지를 따로 관리하기 위헤 썸머노트에서 콜백함수를 사용할 수 있습니다.
4-1. 화면 콜백함수
이미지를 업로드해주는 함수를 따로 추가하여 썸머노트의 콜백함수에서 불러와주었습니다.
- 비동기 방식으로 POST 타입으로 데이터를 전달하고 있는데, 프로젝트 내에서 스프링 시큐리티를 사용하고 있어서 헤더에 토큰을 함께 전송해주었습니다.
- FormData 는 폼을 쉽게 보내도록 도와주는 객체입니다. file 객체를 form 으로 만들어서 전송해줍니다.
$('#summernote').summernote({
placeholder: '기억에 남는 여행 순간들을 기록해보세요.',
tabsize: 2,
height: 300,
minHeight: null, // 최소 높이
maxHeight: null, // 최대 높이
focus: true, // 에디터 로딩후 포커스를 맞출지 여부
lang: "ko-KR", // 한글 설정
toolbar: [
['style', ['style']],
['font', ['bold', 'underline', 'clear']],
['color', ['color']],
['para', ['ul', 'ol', 'paragraph']],
['table', ['table']],
['insert', ['link', 'picture', 'video']],
['view', ['fullscreen', 'codeview', 'help']]
],
callbacks: {
onImageUpload: function (files, editor, welEditable) {
// 파일 업로드 (다중 업로드를 위해 반복문 사용)
for (var i = files.length - 1; i >= 0; i--) {
var fileName = files[i].name
// 이미지 alt 속성 삽일을 위한 설정
var caption = prompt('이미지 설명 :', fileName)
if (caption == '') {
caption = '이미지'
}
uploadSummernoteImageFile(files[i], this, caption)
}
},
}
});
$('#summernote').summernote('code',boardCont.html());
/* Post 메서드 시큐리티 추가 */
let headerName = document.getElementsByClassName("csrf_input")[1].getAttribute("name");
let token = document.getElementsByClassName("csrf_input")[0].getAttribute("value");
// 이미지 업로드 함수 ajax 활용
function uploadSummernoteImageFile(file, el, caption) {
let formData = new FormData();
formData.append('file', file)
console.log(formData);
$.ajax({
type: 'post',
data: formData,
url: "/fileApi/categoryImgSave",
dataType: 'json',
contentType: false,
enctype: 'multipart/form-data',
processData: false,
async: true, //동기, 비동기 여부
cache :false, // 캐시 여부
beforeSend : function(xhr)
{
xhr.setRequestHeader(headerName, token);
},
focus: true,
success: function (data) {
console.log(data.url)
$(el).summernote('insertImage', '/boardApi/boardImg?url='+data.url, function($image) {
$image.css('width', "100%");
$image.attr('alt', caption)
});
},
error:function (data){
console.log(data)
}
});
}
4-2. 컨트롤러
이미지를 실제 저장하는 서비스 로직에 파일을 전달하고 이미지가 저장된 경로를 전달받아 문자열로 변환해 화면에 다시 전송합니다.
📑FileApi.java
package com.example.travel.controller.api;
...
@Log4j2
@RestController
@RequiredArgsConstructor
@RequestMapping("/fileApi")
public class FileApi {
final BoardFileService boardFileService;
@PostMapping(value = "/categoryImgSave")
@ResponseBody
public String categoryImageUpload(@RequestParam("file") MultipartFile files) {
log.info("컨텐츠 이미지 저장------------------------");
JsonObject categoryImg = boardFileService.createImage(files, "categoryImg");
String txt = categoryImg.toString();
return txt;
}
}
4-3. 이미지 저장 서비스 로직
- 서비스 로직에서 JsonObject 객체를 사용하려면 `build.gradle` 에 아래 라이브러리를 추가해주어야한다.
자바 오브젝트를 쉽게 JSON으로 변환시켜주는 심플한 라이브러리 입니다.
📑build.gradle
// https://mvnrepository.com/artifact/commons-codec/commons-codec
implementation group: 'commons-codec', name: 'commons-codec', version: '1.9'
📑BoardFileService.java
package com.example.travel.service;
...
@RequiredArgsConstructor
@Service
@Log4j2
public class BoardFileService{
@Value("${spring.servlet.multipart.location}")
private String uploadPath;
final CategoryImageRepository categoryImageRepository;
public JsonObject createImage(MultipartFile uploadFile,String folderPath) {
log.info("일반 컨텐츠 이미지 저장 ==========================");
//0. 저장 후 전달할 객체
JsonObject jsonObject = new JsonObject();
//1. 파일 경로 폴더를 생성
String folderPathMake = makeFolder(folderPath);
//2. 경로와 이름을 나눠야함.
//실제 파일 이름 ie 나 edge는 전체 경로가 전달된다.
String uploadFileName = uploadFile.getOriginalFilename();
String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("\\") + 1 );
log.info("fileName : " + fileName);
//UUID
String uuid = UUID.randomUUID().toString();
String originalName = uuid + "_"+ fileName;
//저장할 파일 이름 중간에 _ 를 이용하여 구분
String saveName = uploadPath + File.separator + folderPathMake + File.separator + originalName;
String url = "/"+ folderPathMake + "/" + originalName;
Path savePath= Paths.get(saveName);
try {
//실제 이미지 저장
uploadFile.transferTo(savePath);
log.info("url: {}",url);
//화면에 전달할 DTO 생성
jsonObject.addProperty("url", url);
jsonObject.addProperty("responseCode", "succcess");
} catch (IOException e) {
jsonObject.addProperty("responseCode", "error");
e.printStackTrace();
log.warn("업로드 폴더 생성 실패: " + e.getMessage());
}
return jsonObject;
}
@Transactional
public JsonObject createImageThumbnail(MultipartFile uploadFile, String folderPath, CategoryBoard board) {
log.info("썸네일 이미지 저장 ==========================");
...
return jsonObject;
}
protected String makeFolder(String folder) {
String folderPath = folder.replace("/", File.separator);
File uploadPathFolder = new File(uploadPath,folderPath);
if (uploadPathFolder.exists() == false){
uploadPathFolder.mkdirs();
}
return folderPath;
}
//파일 제거
public ResponseEntity<Boolean> removeFile(String fileName,String folderPath){
log.info("이미지 제거 로직");
...
}
}
'Project · Etc > Project' 카테고리의 다른 글
[ Project · ArtMarket ] 알림 · 파일 기능 분석과 DB 설계 (0) | 2023.10.06 |
---|---|
[ Project · ArtMarket ] 채팅 기능 분석 및 DB 설계 (0) | 2023.10.05 |
[ Project ] AWS 가입 및 프로젝트 배포하기 (0) | 2023.08.21 |
[ Project · Travel Road ] JAVA JSON으로 전달 받은 String 값 List로 변경 (0) | 2023.07.01 |
[ Project · Travel Road ] 새로고침 시 데이터 중복 저장 막기, 컨트롤러 구분 (0) | 2023.06.26 |