주말 모두를 사용해가며 에디터 팝업 띄우는 것은 성공했다. 예제가 나와있었는데, 이를 내 프로젝트에 적용하는 것이 쉽지 않았다. 일단 코드를 이해하는데 시간을 좀 사용해야 했고, 타입 에러 어떻게 하면 잡을 수 있을지에 대해서도 생각하게 됬다.
문제는 기존에 설정한 webpack에서 에디터의 한 기능이 제대로 동작하지 않는데, 같이 수업을 듣는 분께서 해결 방법에 대해 말씀해주셨다. 내 코드에서는 근데 알려주신 방법으로 해결이 되지 않았는데 다른 분들에게 도움이 되실 것 같다. 아무래도 내가 webpack 설정에 대해 잘 몰라서 에러가 발생하는 것으로 파악되는데, 시간이 되면 webpack에 대해 조금 더 공부해야겠다.
2주차 (06.08 ~ 06.14)
에디터 라이브러리
Slate.js 라이브러리를 사용해서 텍스트 에디터 기능을 추가했습니다.
Slate.js
는 완전한 커스터마이징을 제공하고 있고, 문서화 및 예제도 잘 나와있어서, 노션에 적합한 라이브러리라 판단했고, 이를 사용하면 마크다운 에디터 뿐 아니라 다양한 기능을 추가할 수 있을 것이라고 예상됩니다.
Slate.js 사용법
Slate.js
의 기본적인 컨셉은 Block 과 Leaf 의 조작입니다.
- Block: 한 줄 또는 한 문단이라고 생각하시면 됩니다. 엔터를 누르지 않고(또는 shift + 엔터) 글을 쓸 때, 여러 줄로 나뉘어 지는 것은 기본적으로 한 Block 이고, 엔터를 입력해서 Block을 끝낼 수 있습니다.
- Leaf: Leaf 는 한 Block 내에 존재할 수도 있고, 여러 Block에 걸쳐서 존재할 수도 있지만, 기본적으로는 한 Block 내에서 특정 부분을 Leaf 라고 합니다.위의 문장에서 전체는 한 Block 이라고 말할 수 있고, 굴게 표시된 리프 부분은 Block 내의 Leaf 라고 할 수 있습니다. 물론 굴게 처리되지 않은 부분도 모두 Leaf 입니다.
이것은 블록일까요? 리프일까요?
위의 기본 컨셉을 가지고, 일단 Block과 Leaf를 어떻게 구성할 것인지에 대해 사용자가 정해주어야 합니다.
- Block.tsx저는 아무 처리도 하지 않은 디폴트 블록은
<p>
태그 내에 보여주도록 설정했습니다. 추가로code
블록과bullet
블록,title
블록을 만들었습니다.
const Block = ({ attributes, children, element }: RenderElementProps) => {
switch (element.type) {
case 'code':
return <pre {...attributes}>{children};
case 'bullet':
return (<li {...attributes}>{children});
case 'h1':
return <h1 {...attributes}>{children};
case 'h2':
return <h2 {...attributes}>{children};
case 'h3':
return <h3 {...attributes}>{children};
default:
return <p {...attributes}>{children};
}
};
- Leaf.tsx리프의 경우 기본적으로
<span>
태그 내에 보여주도록 했고, 타입이 bold, italic, underlined 일 경우 이에 맞는 효과를 적용하도록 했습니다.
const Leaf = ({ attributes, children, leaf }: RenderLeafProps) => {
if (leaf.bold) {
children = <strong>{children}</strong>;
}
if (leaf.italic) {
children = <em>{children}</em>;
}
if (leaf.underlined) {
children = <u>{children}</u>;
}
return <span {...attributes}>{children}</span>; };
에디터 컴포넌트는 아래와 같이 생성했습니다.
const MyEditor: React.FC = () => {
const editor = useMemo(() => withHistory(withReact(createEditor())), []);
const [value, setValue] = useState<Descendant[]>(initialValue);
// Block 단위의 변화가 있을 때 기존에 만든 구문으로 변환시켜줌
const renderElement = useCallback((props: RenderElementProps) => {
return <Block {...props} />;
}, []);
// Leaf 내에서 변화가 있을 때 기존에 만든 구문으로 변환시켜줌
const renderLeaf = useCallback((props: RenderLeafProps) => {
return <Leaf {...props} />;
}, []);
// 키다운 이벤트
const onKeyDownHandler = (event: React.KeyboardEvent<HTMLDivElement>) => {
// ...
};
return (
<Slate
editor={editor}
value={value}
onChange={(newValue) => setValue(newValue)}
>
<Toolbar />
<StyledEditable
renderElement={renderElement}
renderLeaf={renderLeaf}
onKeyDown={onKeyDownHandler}
/>
</Slate>
);
};
Slate.js 사용 이슈
webpack 설정을 혼자 해서 진행했을 때, Generate가 제대로 작동하지 않아서, 에디터 Block 또는 Leaf의 타입 체크를 하지 못하는 상황이 발생했습니다. CRA로 시작한 프로젝트에서는 정상 동작한 것으로 보아 webpack 또는 babel 설정에서 다른 점이 있을 것이라 추측하고 있습니다.