KCMF의 관리자 페이지에서 공지사항을 등록하는 페이지를 작업중이었다. 폼 작업 진행중 에디터도 구현해야했고, MUI 라이브러리에서는 따로 에디터 기능이 제공되지 않았다. 그래서 찾아보던 도중 ReactQuill 라는 툴을 알게되어 적용해보기로 했다.
yarn add react-quill
import ReactQuill from "react-quill";
import 'react-quill/dist/quill.snow.css';
const ReactEditor = () => {
return (
<div>
<ReactQuill theme="snow"/>
</div>
)
}
export default ReactEditor;
나는 현재 react-hook-form 을 사용하고 있어 Controller 안에 적용해주었다.
여기에 Controller 의 field 객체값으로 onChange 와 value 속성을 추가해주었다.
<Controller
control={control}
name="content"
defaultValue=""
render={({ field }) => (
<ReactQuill
theme="snow"
value={field.value}
onChange={field.onChange}
/>
)}
/>
초반에는 위의 모습처럼 툴바에 기본적인 기능만 표시되는데, 그 외 이미지나 표, 글자 스타일 적용 등 고급 기능들을 적용하고 싶을땐 커스텀해서 추가해줄 수 있다. modules 에 여러 다른 옵션들을 설정해 ReactQuill 의 module 속성으로 적용해주자.
const modules: {} = useMemo(
() => ({
toolbar: {
container: [
[{ font: [] }],
[{ header: [1, 2, 3, 4, 5, false] }],
['bold', 'italic', 'underline', 'strike'],
[{ color: [] }, { background: [] }],
[{ align: [] }],
[{ list: 'ordered' }, { list: 'bullet' }],
['link', 'image', 'video'],
['insertTable'],
['clean'],
],
},
}),
[],
);
<ReactQuill
theme="snow"
value={field.value}
onChange={field.onChange}
modules={modules}
/>
에디터의 value 값은 html 모습의 string 형태로 내려오는데, 이를 출력하려면 dangerouselySetInnerHTML
를 사용해주어야 한다. 실시간으로 값을 확인하기 위해 react-hook-form 의 watch 를 사용하여 값을 확인했다.
<div dangerouslySetInnerHTML={{ __html: watch('content') }} />
여기서 주의해야할 점은, dangerouselySetInnerHTML
이 HTML 태그를 그대로 렌더링할 수 있게 해주는 대신 무분별하게 사용할 경우 보안에 취약하다는 점이다. script가 포함되어 있으면 XSS (Cross-Site Scripting) 공격에 취약한데, 외부 입력 데이터를 사용할 때는 반드시 검증 및 정제를 시켜주어야 한다.
그럼 어떻게 이를 해결할 수 있을까?
이는 DOMPurify 라이브러리를 사용해 안전하게 HTML 을 처리해줄 수 있다.
설치
yarn add dompurify
적용
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(watch('content')) }} />
DOMPurify는 기본적으로 HTML에서 위험한 태그와 속성을 제거하여 안전한 HTML을 생성해준다.
sanitize
메소드는 입력된 HTML을 정제하고 안전한 HTML 문자열을 반환해준다. 이를 통해 XSS 공격으로부터 보호할 수 있다.
여기서 추가적인 설정을 통해 더 많은 정제 옵션을 사용할 수 있는데, 특정 태그나 속성을 허용하는 등 별도의 설정할 수 있다고 한다.
에디터의 기본 height 값이 너무 낮아 ReactQuill 태그에 직접적으로 style 객체를 통해 height 300px 값을 주었는데 적용되지 않았다.
검사기로 아래 요소들의 class 이름을 찾았고, 글이 작성되는 요소에 따로 height 를 주고자 styled-component 를 사용해 스타일을 적용시켰다.
import styled from 'styled-components';
const StyledQuill = styled(ReactQuill)`
border-radius: 8px;
overflow: hidden !important;
border: 1px solid #ccc;
.ql-container {
border: none;
}
.ql-toolbar {
border: none;
border-bottom: 1px solid #ccc;
}
.ql-editor {
height: 300px;
overflow-y: auto;
}
`;
<StyledQuill
theme="snow"
value={field.value}
onChange={field.onChange}
modules={modules}
style={styles.editor}
/>
참고
https://rlawo32.tistory.com/entry/React-웹-에디터-React-Quill-사용하기-1
https://velog.io/@hskwon517/React-Quill-에디터-사용하기
https://velog.io/@rlaclgns321/웹-에디터-React-quill