ํ๋ก๊ทธ๋๋ฐ ์ธ๊ณ๋ฅผ ํ๊ตฌํฉ์๋ค.
CKEditor ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์ฉํ์ฌ ๊ฒ์ํ ๊ตฌ์ฑํ๊ธฐ
CKEditor ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ์ฌ ๊ฒ์ํ์ ํจ๊ณผ์ ์ผ๋ก ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
HTML, CSS๋ฅผ ์ ๋ถ ํ์ฉํด์ ๊ตฌํํ๋ ค๋ฉด ์ด๋ ค์ด ๊ฒ์ํ ์์๋ฅผ ๊ฐํธํ๊ฒ ์ถ๋ ฅํ ์ ์์ต๋๋ค.
์๋๋ ๊ตฌํ ์์์ ๋๋ค.
CKEditor ๋ผ์ด๋ธ๋ฌ๋ฆฌ
1. CKEditor ๋?
CKEditor๋ ์น ๊ธฐ๋ฐ์ ํ ์คํธ ์๋ํฐ๋ก, HTML๋ก ์์ฑ๋ ๋ฌธ์๋ฅผ ํธ์งํ ์ ์์ต๋๋ค.
์ด๋ ์น์ฌ์ดํธ์ ์ฌ์ฉ์๊ฐ ๋ธ๋ผ์ฐ์ ์์ ์ง์ ์ฝํ ์ธ ๋ฅผ ํธ์งํ ์ ์๊ฒ ํด์ค๋๋ค.
์ด ์๋ํฐ๋ JavaScript๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ค์ํ ์น ๋ธ๋ผ์ฐ์ ์ ํธํ๋ฉ๋๋ค.
CKEditor ์ฌ์ดํธ URL
https://ckeditor.com/ckeditor-5/
CKEditor ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฃผ์ ๊ธฐ๋ฅ
CKEditor๋ ํ ์คํธ ์คํ์ผ๋ง, ๊ธ๋จธ๋ฆฌ ๊ธฐํธ์ ๋ฒํธ ๋งค๊ธฐ๊ธฐ, ํ ์ฝ์ , ๋งํฌ์ ์ด๋ฏธ์ง ์ฝ์ ๋ฑ๊ณผ ๊ฐ์ ํ๋ถํ ํ ์คํธ ํธ์ง ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
๋ํ, HTML ์ฝ๋๋ฅผ ์ง์ ํธ์งํ๋ '์์ค' ๋ชจ๋๋ ์์ต๋๋ค.
์ด ์ธ์๋ CKEditor๋ ํ๋ฌ๊ทธ์ธ ์ํคํ ์ฒ๋ฅผ ํตํด ์ฌ์ฉ์๊ฐ ์ํ๋ ๊ธฐ๋ฅ์ ์ถ๊ฐ๋ก ๊ฐ๋ฐํ๊ณ ์ ์ฉํ ์ ์์ต๋๋ค.
CKEditor ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ ๋ฐฉ๋ฒ
์ ๋ react ํ๋ก์ ํธ์์ ์ฌ์ฉํ์์ต๋๋ค.
1. ์ค์น
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น๋ฅผ ์งํํฉ๋๋ค.
npm install --save @ckeditor/ckeditor5-react @ckeditor/ckeditor5-build-classic
documentation ์์ ๋ค๋ฅธ ์๋ํฐ์ ์ข ๋ฅ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
https://ckeditor.com/docs/ckeditor5/latest/builds/guides/overview.html
2. ์ฝ๋ ์์ฑ
์๋ ์ฝ๋๋ฅผ ์ ์ ํ ์์น์ ์ ๋ ฅํฉ๋๋ค.
import React, { Component } from 'react';
import { CKEditor } from '@ckeditor/ckeditor5-react';
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
<CKEditor
editor={ClassicEditor}
data="<p>Hello from CKEditor 5!</p>"
onReady={editor => {
// You can store the "editor" and use when it is needed.
console.log('Editor is ready to use!', editor);
}}
onChange={(event, editor) => {
const data = editor.getData();
console.log({ event, editor, data });
setMountainContent({
...mountainContent,
content: data
})
console.log(mountainContent);
}}
onBlur={(event, editor) => {
console.log('Blur.', editor);
}}
onFocus={(event, editor) => {
console.log('Focus.', editor);
}}
/>
</div>
์์ธํ ์ค๋ช ์ ์๋ ๊ณต์๋ฌธ์ ์ฐธ๊ณ ๋ฐ๋๋๋ค.
https://ckeditor.com/docs/ckeditor5/latest/installation/integrations/react.html
3. ์ต์ ๋์ด CSS ์ ์ฉ
์ ํ๋ก์ ํธ์ ์ฌ์ฉํ ์ฝ๋์ ๋๋ค.
Board.jsx
import Nav from '../nav/Nav';
import style from './Board.module.css';
import { CKEditor } from '@ckeditor/ckeditor5-react';
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import { useState } from 'react';
function Board() {
const [mountainContent, setMountainContent] = useState({
title: '',
content: ''
})
const [viewContent, setViewContent] = useState([]);
const getValue = e => {
const { name, value } = e.target;
setMountainContent(prevContent => ({
...prevContent,
[name]: value
}));
};
return (
<>
<div className={style.homepage_nav}>
<Nav />
</div>
<div className={style.board}>
<h1>๋ฑ์ฐ ํ๊ธฐ</h1>
<div className={style.mountain_container}>
{viewContent.map(element =>
<div>
<h2>{element.title}</h2>
<div>{element.content}</div>
</div>)}
</div>
<div className={style.form_wrapper}>
<input className={style.title_input}
type='text'
placeholder='์ ๋ชฉ'
onChange={getValue}
name='title' />
<CKEditor
editor={ClassicEditor}
data="<p>Hello from CKEditor 5!</p>"
onReady={editor => {
// You can store the "editor" and use when it is needed.
console.log('Editor is ready to use!', editor);
}}
onChange={(event, editor) => {
const data = editor.getData();
console.log({ event, editor, data });
setMountainContent({
...mountainContent,
content: data
})
console.log(mountainContent);
}}
onBlur={(event, editor) => {
console.log('Blur.', editor);
}}
onFocus={(event, editor) => {
console.log('Focus.', editor);
}}
/>
</div>
<button className={style.submit_button}
onClick={() => {
setViewContent(prevContent => prevContent.concat(mountainContent));
}}
>์
๋ ฅ</button>
</div>
</>
)
}
export default Board;
Board.module.css
.board {
text-align: center;
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 7rem;
}
.mountain_container {
margin: 0 auto;
width: 80%;
border: 1px solid #333;
padding: 10px 0 30px 0;
border-radius: 5px;
margin-bottom: 50px;
}
.form_wrapper {
width: 60%;
margin: 0 auto;
}
.title_input {
width: 500px;
height: 40px;
margin: 10px;
}
.text_area {
width: 80%;
min-height: 500px;
}
.submit_button {
width: 200px;
height: 50px;
font-size: 20px;
padding: 20px;
border: none;
background: indianred;
border-radius: 5px;
margin-top: 40px;
vertical-align: middle;
}
์ ์ฝ๋๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ๋ฉด ์๋์ ๊ฐ์ ํํ๊ฐ ์ถ๋ ฅ๋ฉ๋๋ค.
๊ฒ์ํ์ ๊ธฐ๋ณธ ์์ญ์ด ๋๋ฌด ์์ต๋๋ค.
Board.css ๋ฑ ๋ฐฉ์์ ์ฌ์ฉํ ๊ฒฝ์ฐ๋ผ๋ฉด css ํ์ผ์ ์๋ ์ฝ๋๋ฅผ ์ ๋ ฅํ๋ฉด ์ต์ ๋์ด๊ฐ ์ค์ ๋ฉ๋๋ค.
.ck-editor__editable {
min-height: 30rem;
}
๊ทธ๋ฌ๋ ์ ๋ module.css ํ์์ ์ฌ์ฉํ์๊ธฐ ๋๋ฌธ์ CSS๊ฐ ์ ์ฉ๋์ง ์๋ ๋ฌธ์ ๊ฐ ์๊ฒผ์ต๋๋ค.
๊ทธ๋์ board.module.css ํ์ผ์ด ์๋ index.css์ ๊ฐ์ ๊ธ๋ก๋ฒ ์คํ์ผ์ํธ ํ์ผ์ ์ ์ฝ๋๋ฅผ ์ ๋ ฅํ์ฌ ์ต์ ๋์ด๋ฅผ ์ค์ ํ๋ ๋ฐฉ์์ผ๋ก ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์์ต๋๋ค.
index.css
/* ๊ธ๋ก๋ฒ ์คํ์ผ์ํธ ํ์ผ์ ์ถ๊ฐ */
.ck-editor__editable {
min-height: 30rem;
}
๊ตฌํ ํ๋ฉด