Last update: @3/15/2023
에디터 적용하기
•
NHN에서 개발한 오픈소스 에디터로, 깃허브 별이 1만개 이상 찍힌 유명한 에디터
•
연습용 게시판을 만드는 중 심플하고 예쁜 국산 에디터 Toast UI Editor를 적용해보기로 함
사용 스펙
•
순수 JavaScript(ES6+) (vanilla JS)
◦
라이브러리 import 수단으로 CDN(Content Delivery Network) 사용
•
Thymeleaf(템플릿 엔진)
•
Bootstrap 5
에디터 적용 예시 - 게시글 작성 화면
<!DOCTYPE html>
<html>
<head xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- TOAST UI Editor CSS -->
<link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor.min.css" />
</head>
<body>
<!-- 글 등록 -->
<div class="container">
<form th:action="@{/posts}" method="post">
<!-- 제목 -->
<div class="input-group mb-3">
<span class="input-group-text" id="inputGroup-sizing-default">제목</span>
<input name="title" type="text" class="form-control" aria-label="title input"/>
</div>
<!-- 본문 -->
<div id="editor"></div>
<!-- 버튼 -->
<div class="d-flex">
<button type="button" class="btn btn-md btn-primary ms-auto my-3" onclick="submitPost(this)">글 등록
</button>
<a class="btn btn-md btn-outline-secondary ms-2 my-3" href="/posts" value="취소">취소</a>
</div>
<input type="hidden" name="content">
<input type="hidden" name="userId" id="userId" th:value="${user.id}"/>
</form>
</div>
<!-- TOAST UI Editor CDN -->
<script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
<!-- 한국어 패치 -->
<script src="https://uicdn.toast.com/editor/latest/i18n/ko-kr.min.js"></script>
<script>
const Editor = toastui.Editor;
const editor = new Editor({
el: document.querySelector('#editor'),
height: '500px',
initialEditType: 'wysiwyg',
previewStyle: 'vertical',
language: "ko-KR"
});
function submitPost(button) {
console.log("submit");
let form = button.closest('form');
let inputs = form.getElementsByTagName('input');
for (let input of inputs) {
if(input.name === 'content') {
input.value = editor.getHTML();
}
}
form.submit();
}
</script>
</body>
</html>
JavaScript
복사
•
CSS부분 - head 태그에 넣어줌
<!-- TOAST UI Editor CSS -->
<link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor.min.css" />
HTML
복사
•
에디터 부분 - 에디터가 들어갈 자리에 넣어줌
<div id="editor"></div>
HTML
복사
•
라이브러리 import 부분(CDN) - 한국어 패치 포함
<!-- TOAST UI Editor CDN -->
<script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
<!-- 한국어 패치 -->
<script src="https://uicdn.toast.com/editor/latest/i18n/ko-kr.min.js"></script>
HTML
복사
◦
특정 버전을 사용하고 싶으면 latest 대신 버전 입력
•
HTML form으로 전송
◦
검색해도 잘 안나왔던 부분이라 내 마음대로 구현해 봄
<button type="button" class="btn btn-md btn-primary ms-auto my-3" onclick="submitPost(this)">글 등록</button>
<input type="hidden" name="content">
JavaScript
복사
◦
button에는 submitPost(this) 함수를 걸어두고, form 태그 안에 컨텐츠를 담을 hidden input 태그를 하나 둠
▪
참고로 type=”button”을 넣지 않으면 HTML5부터는 button을 누를 때 submit이 되니 주의해야 함
function submitPost(button) {
let form = button.closest('form');
let contentInput = form.querySelector('input[name=content]');
contentInput.value = editor.getHTML();
if (validatePost(form)) {
form.submit();
}
}
JavaScript
복사
◦
제출 버튼이 눌리면 editor.getHTML()통해 에디터 내부에 작성한 HTML을 받아서 hidden input에 넣은 후 submit()하는 방식으로 구현
▪
Editor 3.x 버전은 getHTML()
▪
Editor 2.x 버전은 getHtml()
•
이미지는 base64로 인코딩돼서 텍스트 형태로 들어가는데, DB에 부담이 될 수 있기 때문에 image hook 옵션으로 따로 파일 업로드 작업을 해줘야 함
에디터 적용 예시 - 게시글 수정 화면
•
게시글 작성과 모든 게 동일하고, 생성시 옵션에 initialValue만 추가됨
<script th:inline="javascript">
const Editor = toastui.Editor;
const editor = new Editor({
el: document.querySelector('#editor'),
height: '500px',
initialEditType: 'wysiwyg',
//initialValue: [[${post.content}]], // 이상하게 적용이 안 됨
language: "ko-KR"
});
editor.setHTML([[${post.content}]]);
...생략...
</script>
JavaScript
복사
•
initialValue만으로도 기존 게시글 내용이 편집기에 들어가야 하는데, 무슨 이유에선가 안 됨
◦
개발자도구 console 창에서 editor.options.initialValue를 찍어보면 값이 잘 나오는데, 에디터에는 안 들어가 있음(..)
◦
그래서 editor.setHTML([[${post.content}]])로 에디터에 직접 넣어줌
뷰어(viewer) 적용 예시 - 게시글 조회 화면
•
뷰어는 Editor보다 가벼우면서 마크다운 문법을 지원함
<!DOCTYPE html>
<html>
<head xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- TOAST UI Editor Viewer CSS -->
<link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor-viewer.min.css" />
</head>
<body>
<!-- Toast Editor Viewer-->
<div id="viewer"></div>
<!-- Toast Editor Viewer CDN -->
<script src="https://uicdn.toast.com/editor/latest/toastui-editor-viewer.js"></script>
<script th:inline="javascript">
const viewer = new toastui.Editor({
el: document.querySelector('#viewer'),
height: 'auto',
initialValue: [[${post.content}]]
});
</script>
</body>
</html>
JavaScript
복사
•
CSS, div 태그, CDN 선언 방법은 에디터와 같고 URL만 다름
•
Javascript는 new Editor로 생성했던 에디터와 다르게 new toastui.Editor로 선언해야 함
•
뷰어에서는 initialValue 옵션이 잘 적용됨