참고
- https://nhn.github.io/tui.editor/latest/tutorial-example16-i18n
- 에디터 저장하고 초기화하기
- Toast UI 에디터로 이미지를 포함한 깃허브 마크다운 저장하기
- Socket.IO로 Toast UI Editor 동시 편집하기
토스트 UI 에디터에서 저장하기 버튼을 누르면 편집한 내용이 저장되도록 해보자.
initialValue 옵션
initalValue에 값을 넣어주면 해당 내용으로 에디터가 시작된다.
let initContents = "Test!!";
...
<Editor
initialValue={initContents}
height="400px"
placeholder="Please Enter Text."
...
/>
useRef로 콘텐츠 내용 가져오기
useRef를 이용하면 현재 에디터의 내용을 HTML이나 Markdown으로 가져올 수 있다.
먼저 ref를 추가하자.
import React, { useRef } from "react";
const editorRef = useRef(null);
에디터의 ref에 useRef로 만든 editorRef를 추가한다.
<Editor
ref={editorRef}
initialValue={initContents}
아래 함수를 추가한다.
editorRef의 current Instance에서 markdown과 HTML을 얻을 수 있다.
const handleSave = () => {
let markDownContent = editorRef.current.getInstance().getMarkdown();
let htmlContent = editorRef.current.getInstance().getHTML();
console.log(markDownContent);
console.log("================================================");
console.log(htmlContent);
};
버튼을 추가해서 위의 함수를 호출해 보자.
<Button
variant="outlined"
color="primary"
sx={{ m: 1 }}
onClick={handleSave}
>
저장하기
</Button>
콘솔에 정상적으로 HTML과 markdown이 출력되었다.
위에서 편집한 내용의 markdown은 다음과 같다.
# 제목
***~~<span style="color: #EE2323">내용 1</span>~~***
* [x] 체크
* [ ] 체크 해제
| 테이블 1 | 테이블 2 |
| ----- | ----- |
| 11111 | 22222 |
| 11111 | 2222 |
HTML은 다음과 같다.
<h1>제목</h1><p><strong><em><del><span style="color: #EE2323">내용 1</span></del></em></strong></p><p><br></p><ul><li class="task-list-item checked" data-task="true" data-task-checked="true"><p>체크 </p></li><li class="task-list-item" data-task="true"><p>체크 해제</p></li></ul><table><thead><tr><th><p>테이블 1</p></th><th><p>테이블 2</p></th></tr></thead><tbody><tr><td><p>11111</p></td><td><p>22222</p></td></tr><tr><td><p>11111</p></td><td><p>2222</p></td></tr></tbody></table>
initContents에 markdown을 넣어보자.
let initContents = `# 제목
***~~<span style="color: #EE2323">내용 1</span>~~***
* [x] 체크
* [ ] 체크 해제
| 테이블 1 | 테이블 2 |
| ----- | ----- |
| 11111 | 22222 |
| 11111 | 2222 |`;
다시 에디터를 실행하면 이전에 저장한 내용이 출력된다.
HTML의 경우도 테스트해 보자.
let initContents = `<h1>제목</h1><p><strong><em><del><span style="color: #EE2323">내용 1</span></del></em></strong></p><p><br></p><ul><li class="task-list-item checked" data-task="true" data-task-checked="true"><p>체크 </p></li><li class="task-list-item" data-task="true"><p>체크 해제</p></li></ul><table><thead><tr><th><p>테이블 1</p></th><th><p>테이블 2</p></th></tr></thead><tbody><tr><td><p>11111</p></td><td><p>22222</p></td></tr><tr><td><p>11111</p></td><td><p>2222</p></td></tr></tbody></table>`;
HTML은 markdown으로 변경하면 테이블과 설정한 내용이 깨지는 버그가 발생한다.
따라서 내용을 저장할 땐 markdown으로 저장해야 한다.
전체 코드는 다음과 같다.
import React, { useEffect, useRef, useState } from "react";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
// Toast UI Editor
import "@toast-ui/editor/dist/toastui-editor.css";
import { Editor } from "@toast-ui/react-editor";
// Dark Theme 적용
// import '@toast-ui/editor/dist/toastui-editor.css';
// import '@toast-ui/editor/dist/theme/toastui-editor-dark.css';
// Color Syntax Plugin
import "tui-color-picker/dist/tui-color-picker.css";
import "@toast-ui/editor-plugin-color-syntax/dist/toastui-editor-plugin-color-syntax.css";
import colorSyntax from "@toast-ui/editor-plugin-color-syntax";
// Table Merged Cell Plugin
import "@toast-ui/editor-plugin-table-merged-cell/dist/toastui-editor-plugin-table-merged-cell.css";
import tableMergedCell from "@toast-ui/editor-plugin-table-merged-cell";
const colorSyntaxOptions = {
preset: [
"#333333", "#666666", "#FFFFFF", "#EE2323", "#F89009", "#009A87", "#006DD7", "#8A3DB6",
"#781B33", "#5733B1", "#953B34", "#FFC1C8", "#FFC9AF", "#9FEEC3", "#99CEFA", "#C1BEF9",
],
};
const App = () => {
const editorRef = useRef(null);
let initContents = `# 제목
***~~<span style="color: #EE2323">내용 1</span>~~***
* [x] 체크
* [ ] 체크 해제
| 테이블 1 | 테이블 2 |
| ----- | ----- |
| 11111 | 22222 |
| 11111 | 2222 |`;
const handleSave = () => {
let markDownContent = editorRef.current.getInstance().getMarkdown();
let htmlContent = editorRef.current.getInstance().getHTML();
console.log(markDownContent);
console.log("================================================");
console.log(htmlContent);
};
return (
<div>
<Box sx={{ m: 2 }}>
<h1>Toast UI Editor</h1>
<Button
variant="outlined"
color="primary"
sx={{ m: 1 }}
onClick={handleSave}
>
저장하기
</Button>
<Editor
ref={editorRef}
initialValue={initContents}
height="400px"
placeholder="Please Enter Text."
previewStyle="tab" // or vertical
initialEditType="wysiwyg" // or markdown
// hideModeSwitch={true} // 하단 숨기기
toolbarItems={[
// 툴바 옵션 설정
["heading", "bold", "italic", "strike"],
["hr", "quote"],
["ul", "ol", "task", "indent", "outdent"],
["table", /* "image", */ "link"],
["code", "codeblock"],
]}
//theme="dark"
//useCommandShortcut={false} // 키보드 입력 컨트롤 방지 ex ctrl z 등
usageStatistics={false} // 통계 수집 거부
plugins={[[colorSyntax, colorSyntaxOptions], tableMergedCell]}
/>
</Box>
</div>
);
};
export default App;
로컬 스토리지를 이용하여 저장하기 구현
이제 로컬 스토리지를 이용하여 편집기의 내용을 저장해 보자.
참고로 useEffect를 이용하여 아래와 같이 initalValue를 불러와도 정상적으로 콘텐츠가 초기화되지 않는다.
const [initContents, setInitContents] = useState("");
...
useEffect(() => {
let item = localStorage.getItem(CONTENT_KEY);
if(item) {
setInitContents(item);
}
}, []);
<Editor
ref={editorRef}
initialValue={initContents}
height="400px"
...
/>
useEffect에는 setMarkdown을 이용해야 한다.
editorRef.current.getInstance().setMarkdown(item);
먼저 handleSave에 localStorage 저장 코드를 추가하자.
const CONTENT_KEY = "CONTENT_KEY";
const handleSave = () => {
let markDownContent = editorRef.current.getInstance().getMarkdown();
//let htmlContent = editorRef.current.getInstance().getHTML();
localStorage.setItem(CONTENT_KEY, markDownContent);
};
useEffect에서 setMarkdown 메서드를 이용해 localStorage에 저장해둔 내용을 설정한다.
useEffect(() => {
let item = localStorage.getItem(CONTENT_KEY);
if(item) {
editorRef.current.getInstance().setMarkdown(item);
}
}, []);
Editor의 initalValue는 더 이상 필요가 없다.
<Editor
ref={editorRef}
// initialValue={initContents}
정상적으로 편집한 내용이 잘 저장되는지 확인해 보자.
전체 코드는 다음과 같다.
import React, { useEffect, useRef, useState } from "react";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
// Toast UI Editor
import "@toast-ui/editor/dist/toastui-editor.css";
import { Editor } from "@toast-ui/react-editor";
// Dark Theme 적용
// import '@toast-ui/editor/dist/toastui-editor.css';
// import '@toast-ui/editor/dist/theme/toastui-editor-dark.css';
// Color Syntax Plugin
import "tui-color-picker/dist/tui-color-picker.css";
import "@toast-ui/editor-plugin-color-syntax/dist/toastui-editor-plugin-color-syntax.css";
import colorSyntax from "@toast-ui/editor-plugin-color-syntax";
// Table Merged Cell Plugin
import "@toast-ui/editor-plugin-table-merged-cell/dist/toastui-editor-plugin-table-merged-cell.css";
import tableMergedCell from "@toast-ui/editor-plugin-table-merged-cell";
const colorSyntaxOptions = {
preset: [
"#333333", "#666666", "#FFFFFF", "#EE2323", "#F89009", "#009A87", "#006DD7", "#8A3DB6",
"#781B33", "#5733B1", "#953B34", "#FFC1C8", "#FFC9AF", "#9FEEC3", "#99CEFA", "#C1BEF9",
],
};
const CONTENT_KEY = "CONTENT_KEY";
const App = () => {
const editorRef = useRef(null);
const handleSave = () => {
let markDownContent = editorRef.current.getInstance().getMarkdown();
//let htmlContent = editorRef.current.getInstance().getHTML();
localStorage.setItem(CONTENT_KEY, markDownContent);
};
useEffect(() => {
let item = localStorage.getItem(CONTENT_KEY);
if(item) {
editorRef.current.getInstance().setMarkdown(item);
}
}, []);
return (
<div>
<Box sx={{ m: 2 }}>
<h1>Toast UI Editor</h1>
<Button
variant="outlined"
color="primary"
sx={{ m: 1 }}
onClick={handleSave}
>
저장하기
</Button>
<Editor
ref={editorRef}
// initialValue={initContents}
height="400px"
placeholder="Please Enter Text."
previewStyle="tab" // or vertical
initialEditType="wysiwyg" // or markdown
// hideModeSwitch={true} // 하단 숨기기
toolbarItems={[
// 툴바 옵션 설정
["heading", "bold", "italic", "strike"],
["hr", "quote"],
["ul", "ol", "task", "indent", "outdent"],
["table", /* "image", */ "link"],
["code", "codeblock"],
]}
//theme="dark"
//useCommandShortcut={false} // 키보드 입력 컨트롤 방지 ex ctrl z 등
usageStatistics={false} // 통계 수집 거부
plugins={[[colorSyntax, colorSyntaxOptions], tableMergedCell]}
/>
</Box>
</div>
);
};
export default App;
댓글