1. 라이브러리
npm install react-quill
2. 소스코드
이미지 핸들러를 만들어 이미지 등록할 경우 Firebase Store에 추가하고 해당 URL을 받아와 저장했다.
2023.03.01 - [React] - [NextJS] Firebase Storage 파일 업로드/다운로드 방법
import dynamic from "next/dynamic";
import "react-quill/dist/quill.snow.css";
import { useState, useMemo, useRef, useEffect } from "react";
import styled from "styled-components";
import { useForm } from "react-hook-form";
import moment from "moment";
import { storage } from "../common/firebase";
import {
ref,
getDownloadURL,
uploadBytes,
deleteObject,
} from "firebase/storage";
import { communityWrite } from "../api/backEndApi";
import Loader from "./Loader";
import { getCookie } from "../common/utills";
const ReactQuill = dynamic(
async () => {
const { default: RQ } = await import("react-quill");
return function comp({ forwardedRef, ...props }: any) {
return <RQ ref={forwardedRef} {...props} />;
};
},
{ ssr: false, loading: () => <Loader /> }
);
const Title = styled.input`
margin: 10px;
width: 80%;
height: 2rem;
border-radius: 10px;
font-size: 20px;
`;
const Save = styled.button`
cursor: pointer;
padding: 8px;
border: 1px solid;
border-radius: 5px;
background-color: ${(props) => props.theme.pointColor};
font-weight: bold;
color: white;
font-family: "Noto Sans KR";
margin-top: 100px;
float: right;
&:hover {
background-color: #00c3ff;
}
`;
export default function TextEditor() {
const [title, setTitle] = useState("");
const [dom, setDom] = useState("");
const { register, handleSubmit } = useForm();
const [img, setImg] = useState([] as any);
const quillRef = useRef<any>();
const imageHandler = () => {
const input = document.createElement("input");
input.setAttribute("type", "file");
input.setAttribute("accept", "image/*");
input.click();
input.onchange = async (event: any) => {
const file: File = event?.target?.files[0];
const path =
"images/community/" +
new Date().getFullYear() +
"년/" +
(new Date().getMonth() + 1) +
"월/";
const fileNm =
moment().format("YYYYhmmss") + `_${getCookie("id")}_` + file.name;
const storageRef = ref(storage, path + fileNm);
uploadBytes(storageRef, file).then((snapshot) => {
getDownloadURL(snapshot.ref).then((downUrl) => {
quillRef.current?.editor?.insertEmbed(
quillRef.current.getEditor().getSelection().index,
"image",
downUrl
);
setImg((prev: []) => [...prev, downUrl]);
});
});
};
};
const quillImage = useMemo(() => {
const result = Array.from(
dom.replace(/amp;/g, "").matchAll(/<img[^>]+src=["']([^'">]+)['"]/gi)
);
return result.map((item) => item.pop() || "");
}, [dom]);
useEffect(() => {
const dellFile = img?.filter((item: any) => !quillImage.includes(item));
if (dellFile.length) {
dellFile.forEach((item: any) => {
const desertRef = ref(storage, item);
deleteObject(desertRef).then(() => {
const chageFile = img?.filter((item: any) =>
quillImage.includes(item)
);
setImg(chageFile);
});
});
}
}, [img, quillImage]);
const formats = [
"header",
"font",
"size",
"bold",
"italic",
"underline",
"align",
"strike",
"script",
"blockquote",
"background",
"list",
"bullet",
"indent",
"link",
"image",
"color",
"code-block",
];
const modules = useMemo(
() => ({
toolbar: {
container: [
["bold", "italic", "underline", "strike"],
["blockquote", "code-block"],
[{ header: 1 }, { header: 2 }],
[{ list: "ordered" }, { list: "bullet" }],
[{ script: "sub" }, { script: "super" }],
[{ indent: "-1" }, { indent: "+1" }],
[{ direction: "rtl" }],
[{ size: ["small", false, "large", "huge"] }],
[{ header: [1, 2, 3, 4, 5, 6, false] }],
[{ color: [] }, { background: [] }],
[{ font: [] }],
[{ align: [] }],
["clean"],
["image", "video"],
],
handlers: {
image: imageHandler,
},
},
}),
[]
);
const onValid = () => {
const requestObj = {
id: moment().format("YYYYMMDDHHmmss"),
title: title,
content: quillRef.current?.editor.getText(),
img: JSON.stringify(img),
dom: dom,
};
communityWrite(requestObj);
};
return (
<form onSubmit={handleSubmit(onValid)}>
<span>제목</span>
<Title
{...register("TITLE", { required: true })}
onChange={(e) => setTitle(e.currentTarget.value)}
placeholder="Title..."
/>
<ReactQuill
forwardedRef={quillRef}
modules={modules}
formats={formats}
theme="snow"
style={{ height: "90%" }}
onChange={setDom}
/>
<Save>완료</Save>
</form>
);
}
반응형
'React' 카테고리의 다른 글
[React Native] RN CLI Mac 개발 환경 구성 (0) | 2024.04.22 |
---|---|
[카카오맵 API / 카카오 주소 API] 검색된 주소를 카카오 맵에 표시하는 방법 (0) | 2023.04.18 |
[카카오맵 API] React 클릭 위치로 마커(Marker) 변경 및 주소 가져오는 방법 (0) | 2023.04.17 |
[카카오맵 API] React TypeScript에서 Geolocation를 사용한 현재 위치 표시 방법 (0) | 2023.04.11 |
[카카오맵 API] React TypeScript에서 카카오맵 불러오기 (0) | 2023.04.10 |