그런데 상단 툴바를 잘 살펴보니 무슨 이유에서인지 폰트 사이즈를 조정하는 기능이 없다!
유료 버전에만 포함되어 있는건가 싶었는데, 설마 이런 기본적인 기능을 유료로 포함했을 것 같지는 않았고 이미지 삽입도 무료 기능에 포함되어 있는 마당에 폰트 사이즈 조정이 유료일리는 없었다.
공식 문서의 유료 버전 기능에서도 폰트 사이즈에 대한 내용이 없어서 1시간 가량 고민을 했었는데..
const editorInit = {
max_width: 800,
max_height: 800,
menubar: false,
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
fontsize_formats: "8pt 9pt 10pt 11pt 12pt 14pt 18pt 24pt 30pt 36pt 48pt 60pt 72pt 96pt",
plugins: [ "image", "code", "table", "link", "media", "codesample", "lists", "autoresize", "codesample" ],
toolbar:
'undo redo | fontsize | bold italic backcolor | ' +
'alignleft aligncenter alignright alignjustify | ' +
'image | bullist numlist | codesample removeformat ',
};
TinyMCE 에디터는 사용하고 싶은 기능에 따라 plugin이 나뉘어져 있고, plugins 속성에 사용할 plugin을 추가한 다음 그에 대응되는 toolbar 키워드를 설정 파일에 입력. 웹 에디터 상에서 기능을 사용하는 방식이다.
조사한 자료에 맞춰 plugins와 toolbar를 적용했음에도 기능이 작동하지 않아 골머리를 썩혔는데, 알고보니 6 버전 이후 에디터의 문법과 사용되는 키워드들의 명칭이 대거 변경된 것이 문제였다. 자료에서는 5버전 이하 내용들이 섞여있었고, 이 내용은 6버전에서는 사용할 수 없어 기능이 동작하지 않았던 것.
6버전에 맞는 자료를 찾아 제대로 된 키워드를 사용했더니, 이제 기능이 정상적으로 동작하기 시작했다.
Draft.js와는 달리 TinyMCE 에디터는 데이터 변환 작업 없이 사용자가 작성한 글 데이터를 그대로 DB에 저장할 수 있었다. 블로그에서는 ContextAPI와 useReducer을 사용하고 있으므로 dispatch 함수를 이용하여 state를 관리해주고 있다.
const addDocument = async (doc) => {
dispatch({ type: 'isPending' });
try {
const docRef = await addDoc(colRef, {
title: doc.titleData,
text: doc.postData,
file: fileName,
writer: user.displayName,
type: doc.selectTypeData,
createdTime,
});
if (doc.fileData) {
const imagesRef = ref(storageRef, fileName);
await uploadBytes(imagesRef, doc.fileData);
}
dispatch({ type: 'addDoc', payload: docRef });
alert('글 작성이 완료되었습니다.');
navigate('/questions', { replace: true });
}
catch (error) {
alert(error.message);
navigate('/questions', { replace: true });
};
};
기본적인 구조는 에디터에서 작성한 글 데이터가 state의 형태로 저장된 뒤. 파이어스토어의 addDoc 함수를 통해 DB에 데이터를 저장하는 것이다.
저장을 완료하면 사용자에게 글 작성이 완료되었음을 알리고, 게시판 목록으로 화면을 전환한다.
다음 순서는 글 수정 기능이다. 수정 기능 구현에는 DB에 저장된 글 데이터를 가져오는 기능이 필요하다. 이는 글 조회 기능 구현과도 연관이 있다.
const getDocument = async (docid) => {
dispatch({ type: 'isPending' });
try {
const docRef = doc(colRef, docid);
const docSnap = await getDoc(docRef);
dispatch({ type: 'getDoc', payload: docSnap.data() });
}
catch (error) {
dispatch({ type: 'error', payload: error.message });
alert(error.message);
}
};
글 데이터를 가져온 다음, 에디터 상에서 데이터를 출력시키고 사용자가 값을 수정한 뒤 다시 저장하도록 한다. 게시판 종류에 따라서 FireStore의 collection를 다르게 지정해야 한다. 따라서 게시판 종류에 따라 collection Ref를 다르게 가져오고, 조건문을 활용하였다.
const updateDocument = async (props) => {
const docRef = doc(appFireStore, transaction, props.id);
dispatch({ type: 'isPending' });
let fileName = 'No file';
if (props.fileData) {
fileName = props.fileData.fileName;
}
if (props.type === 'qs') {
try {
await setDoc(docRef, {
title: props.titleData,
text: props.postData,
fileName: fileName,
type: props.selectTypeData,
createdTime,
}, { merge: true });
if (props.fileData) {
const imagesRef = ref(storageRef, fileName);
await uploadBytes(imagesRef, props.fileData);
}
dispatch({ type: 'updateDoc', payload: docRef });
alert('글 수정이 완료되었습니다.');
navigate('/questions', { replace: true });
}
catch (error) {
alert(error.message);
dispatch({ type: 'error', payload: error.message });
navigate('/questions', { replace: true });
};
}
(..생략..)
};
그리고 하나의 에디터 컴포넌트에서 글 작성 및 수정을 모두 할 수 있어야 한다. 에디터 컴포넌트로 진입하는 경로에 따라 작성 페이지와 수정 페이지가 동작하도록 해야한다.
useEffect(() => {
if (type === 'qs' && id !== 'write') {
setTitleData(response.document?.title);
setPostData(response.document?.text);
}
else if (type === 'dr' && id !== 'write') {
setTitleData(response.document?.title);
setPostData(response.document?.text);
}
else if (type === 'sr' && id !== 'write') {
setTitleData(response.document?.title);
setPostData(response.document?.text);
setSelectTypeData(response.document?.type);
};
}, [response]);
이를 위해서 컴포넌트 진입 시 게시판의 종류를 'type' 인자로 받아오고, 글을 작성할 때에는 id값으로 'write'를 수정할 때에는 FireStore가 자동으로 생성한 글 데이터의 id 값을 가져오도록 하여 id값이 'write'가 아닐 경우에는 글 데이터를 조회하여 화면에 출력시키도록 하였다.
<button type='submit' className={styles.recordeditorwritebtu}>
{isUpdate ? <>글 수정</> : <>글 작성</>}
</button>
그리고 isUpdate 변수와 삼항 연산자를 이용하여 어떤 버튼이 출력될 지 조건 연산을 적용해주고.
const handleOnSubmit = (event) => {
event.preventDefault();
if (!isUpdate) {
addDocument({ type, titleData, postData, fileData, selectTypeData });
}
else {
updateDocument({ type, id, titleData, postData, fileData, selectTypeData });
}
};
isUpdate 변수을 이용하여 submit 버튼을 클릭했을 때 글 작성 기능이 동작할지, 수정 기능이 동작할 지를 결정해주면 된다.
그리고.. 글 수정 기능 또한 정상적으로 동작한다!