예전에 일기를 쓰다가 다시 일기를 써야겠다는 생각이 들었다.
그런데 나도 개발자니까 나만의 일기장을 만들면 어떨까란 생각이 들었다.
웹으로도 가능하고, 앱으로도 가능하다면 더 좋을 것 같다는 생각이 들어서 우선 웹은 react로 제작하고, 앱은 react native를 이용해서 만들겠다는 생각을 했다. 만들게 되면 서버는 나만을 위한 것이기도 하고 웹과 앱 동시에 사용을 위한 것이기도 해서 firebase를 이용해야겠다고 계획을 세웠다.
시도해보자.
처음엔 textarea 태그를 이용해서 제작을 하려고 했다.
그러나 우리가 흔히 웹사이트에서 글쓰기를 하면 글자크기, 두께 기울기 등이 있다. 난 나의 일기장에도 그런 것을 이용해서 더욱 표현할 수 있다면 좋겠다는 생각을 했고, textarea는 이것을 표현 할 수가 없는 단점이 있다.
그래서 div태그에 contenteditable을 주어서 입력이 가능하도록 진행했다.
그리고 useRef를 이용해서 이후 만들게 될 버튼을 클릭했을 때에 포커싱이 자동으로 가능하게 제작해주었다.
<div
ref={editorRef}
contentEditable={true}
/>
이러니 작성들은 잘 되는 것을 확인할 수 있었다.
이제 버튼을 만들어야 한다.
처음에 버튼을 만들때 일부러 span태그를 이용해서 만들었는데, 버튼을 연속으로 2번 클릭해야 작동하는 문제를 비롯해 제대로 작동이 되지 않았다. 혹시나 하는 마음에 button태그로 변경해주니 님 무슨일?? 하는 것처럼 그냥 아주 잘 제대로 작동이 되어버렸다.... 당황스럽다.
도중에 button을 써야한다면 button을 쓰는 이유가 있구나 함을 배웠다.
뭐 어찌저찌 하다보니 아래와 같은 코드가 탄생했다.
import React, { useRef, useState } from 'react';
import './NotePage.css';
export default function NotePage() {
const editorRef = useRef(null);
const btnClick = (style1, style2 = null, style3 = null) => {
document.execCommand(style1, style2, style3);
};
return (
<div>
<div className='text-toolbar'>
<button className='text-btn' style={{ fontWeight: 600 }} onClick={() => {btnClick('bold')}}>B</button>
<button className='text-btn' style={{ fontStyle: 'italic' }} onClick={() => {btnClick('italic')}}>I</button>
<button className='text-btn' style={{ textDecoration: 'underline' }} onClick={() => {btnClick('underline')}}>U</button>
<button className='text-btn' style={{ textDecoration: 'line-through' }} onClick={() => {btnClick('strikeThrough')}}>S</button>
<button className='text-btn' onClick={() => {btnClick('fontSize', false, '1')}}>12px</button>
<button className='text-btn' onClick={() => {btnClick('fontSize', false, '3')}}>16px</button>
<button className='text-btn' onClick={() => {btnClick('fontSize', false, '5')}}>20px</button>
<button className='text-btn' onClick={() => {btnClick('justifyLeft')}}>좌측정렬</button>
<button className='text-btn' onClick={() => {btnClick('justifyCenter')}}>가운데정렬</button>
<button className='text-btn' onClick={() => {btnClick('justifyRight')}}>우측정렬</button>
<input className='text-btn' type="color" onChange={(e) => {btnClick('foreColor', false, e.target.value)}} />
</div>
<div
ref={editorRef}
contentEditable={true}
className='editor'
/>
</div>
);
}
이렇게 하니 내가 원한 바로 작동은 하지만 문제가 생겼다.
contentEditable을 이용하니 focusing 될 때 input과 다르게 caret, 즉 키보드 커서가 맨 마지막이 아닌 맨 앞에 위치하게 되었다.
그래서 조정을 위해 여럿 검색을 통해 진행을 해서 아래처럼 조정을 해주었다.
import React, { useRef, useState } from 'react';
import './NotePage.css';
export default function NotePage() {
const editorRef = useRef(null);
const btnClick = (style1, style2 = null, style3 = null) => {
document.execCommand(style1, style2, style3);
focusContentEditableTextToEnd(editorRef.current)
};
const focusContentEditableTextToEnd = (element) => {
if (element.innerText.length === 0) {
element.focus();
return;
}
const selection = window.getSelection();
const newRange = document.createRange();
newRange.selectNodeContents(element);
newRange.collapse(false);
selection?.removeAllRanges();
selection?.addRange(newRange);
};
return (
<div>
<div className='text-toolbar'>
<button className='text-btn' style={{ fontWeight: 600 }} onClick={() => {btnClick('bold')}}>B</button>
<button className='text-btn' style={{ fontStyle: 'italic' }} onClick={() => {btnClick('italic')}}>I</button>
<button className='text-btn' style={{ textDecoration: 'underline' }} onClick={() => {btnClick('underline')}}>U</button>
<button className='text-btn' style={{ textDecoration: 'line-through' }} onClick={() => {btnClick('strikeThrough')}}>S</button>
<button className='text-btn' onClick={() => {btnClick('fontSize', false, '1')}}>12px</button>
<button className='text-btn' onClick={() => {btnClick('fontSize', false, '3')}}>16px</button>
<button className='text-btn' onClick={() => {btnClick('fontSize', false, '5')}}>20px</button>
<button className='text-btn' onClick={() => {btnClick('justifyLeft')}}>좌측정렬</button>
<button className='text-btn' onClick={() => {btnClick('justifyCenter')}}>가운데정렬</button>
<button className='text-btn' onClick={() => {btnClick('justifyRight')}}>우측정렬</button>
<input className='text-btn' type="color" onChange={(e) => {btnClick('foreColor', false, e.target.value)}} />
</div>
<div
ref={editorRef}
contentEditable={true}
className='editor'
/>
</div>
);
}
이렇게 하니 이번엔 문제가 내가 커서를 찍어놨어도 무조건 끝으로 넘어간다는 것이다.흑흑흑 그리고 기존에는 기존 caret을 기준으로 굵게, 기울기 등의 효과를 넣으면 바로바로 적용이 되었는데, 이 방식을 사용하니 무조건 드래그 하고 적용을 해야한다는 문제가 생겼다...
아직 나의 수준에서는 당장 모든 것을 마음에 들게 할 수 없었기에 이전 것을 더 공부해서 다시 보완하는 방향으로 결정했다 흑흑
안되면 라이브러리 사용해야지......

css는 이렇게 사용했다.
.text-toolbar{
width: 80%;
height: 50px;
border: 1px solid black;
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.text-btn{
height: 35px;
border: 0;
border-radius: 5px;
box-sizing: border-box;
background: #e2e2e2;
margin: 0px 5px;
padding: 5px 10px;
cursor: pointer;
}
.text-btn:active{
opacity: 0.8;
}
.editor {
height: 500px;
width: 80%;
border: 1px solid #000;
box-sizing: border-box;
padding: 10px;
overflow-y:scroll;
outline:none
}
어.렵.다.😢
그래도 좋은 경험이었다. 하루 동안 계속 만지면서, contentEditable이 있다는 것도 처음알았고, caret과 관련한 정보도 습득했다.
좀 더 시간을 사용하고 공부해서 나도 네이버나 velog 등에 있는 글쓰기 처럼 만들어야지.
개발은 역시 너무나 어렵다. 할 때마다 드는 생각이 얼마나 똑똑하면 일상에서 아무생각없이 사용하는게 별 문제 없이 잘 사용되지? 이다.
오늘과 같은 경험이 쌓이다 보면 나도 그런 사람이 되어있겠지.
나보다 똑똑한 사람들이 만든 좋은 라이브러리 많다. 그것을 이용해보자.