인프런에서 이걸로 바꿨대!!!!
사실 이러면⬆︎ 안 된다는 것쯤은 알지만... Rich Text Editor라는 이름 자체를 처음 들어봐서 무엇을 왜 써야하는지 명확한 이유를 찾는 나만의 기준이 없었기에. 그래도 최소한의 방어선이 있었는데, 아래와 같았다.
인프런에서 사용하고, 유튜브나 블로그 글도 꽤 많고, 다양한 기능이 있고, LogRocket에서도 추천하는~ 뭐 여튼 이런 이유로 채택하게 되었다.
🔥 본문을 서버에 전송할 수 있게 string으로 바꾸고, title과 content로 분리하는 작업
// request body type
title: string;
content: string;
// 본문을 title과 block으로 나누기
const DocumentWithTitle = Document.extend({
content: 'title block+',
});
// title 노드 생성
const Title = Node.create<TitleProps>({
name: 'title',
addOptions() {
return {
level: 1,
HTMLAttributes: {},
};
},
content: 'text*',
marks: '',
group: 'block',
defining: true,
renderHTML({ HTMLAttributes }) {
const level = this.options.level;
return [`h${level}`, mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
},
});
// h1 선택 (title)
const titleNode = doc.querySelector('h1');
// 제목 없으면 에러
if (!titleNode || !titleNode.textContent) {
alert('제목을 입력해주세요');
return;
}
// 본문에서 제목 제거
titleNode.remove();
// 실제 content
const updatedHTML = doc.body.innerHTML;
🔥 발행 시 이미지를 firebase에 업로드 하면서 그 URL을 이미지 src로 대체하는 작업
// text를 html로 변환
const doc = parser.parseFromString(articleHTML, 'text/html');
// 모든 img 태그 선택
const images = doc.querySelectorAll('img');
// 이미지를 firebase에 업로드 후 src를 그 URL로 변환
const uploadImageAndChangeURL = Array.from(images).map(async (image) => {
const blobUrl = image.src;
const blob = await createBlob(blobUrl);
const downloadUrl = await uploadImageToFirebase(blob);
image.src = downloadUrl;
});
// 모든 img 작업이 끝날 때까지 대기
await Promise.all(uploadImageAndChangeURL);
🔥 메뉴바 코드 간결하게 수정
🔥 에디터 디자인
🔥 게시글 작성, 수정 에디터 나누기
mode
Props를 받게 설정해서 모드에 따라 버튼과 그 버튼의 로직이 바뀌게 제작➕ 문서 예시 코드가 친절하다.
➕ 다양한 익스텐션이 있다. (함께 쓰기, 이미지 등)
➕ 커스텀하기 좋다.
➖ 학습양이 꽤 방대하다.
➖ 타입스크립트랑 쓰기가 힘들다.
➖ 코드를 분리하기가 어렵다. (이건 내 리액트 실력 문제일 가능성이 크긴 하다)
➖ css module로 사용하는 방법이 없다(?)
➖ 아직 이런저런 버그가 많다.