Search
🍞

Toast UI Editor 및 Viewer 적용하기 (vanilla JS)

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 대신 버전 입력
적용 코드
const Editor = toastui.Editor; const editor = new Editor({ el: document.querySelector('#editor'), height: '500px', initialEditType: 'wysiwyg', previewStyle: 'vertical', language: 'ko-KR' });
JavaScript
복사
여러가지 옵션 및 플러그인 등이 있는데, 아래 공식 API 문서에서 확인 가능함
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 옵션이 잘 적용됨

References