벨로그에서 글을 써보거나, gitHub Readme.md등의 글을 써본 경험이 있는 분들이라면 마크다운 에디터를 사용한 경험이 있을 것이다.
마크다운 에디터또한 분명 좋지만, 마크다운 문법에 없는 강조기능이나 하이라이팅 기능을 사용하고싶은 경우가 있다. 이런경우 md파일이 아닌 mdx 파일을 활용하면 말한 기능을 사용할 수 있다.
위와같은 텍스트 에디터를 만들 것이다.
마크다운 에디터를 기반으로 내가 커스텀한 기능들을 넣을 수 있다.
글을 읽기전에,
https://github.com/Ionaru/easy-markdown-editor
두 라이브러리의 공식문서를 아주아주 자세히 읽어보는것을 추천한다. 두 라이브러리 문서들을 정독하면서 만들어보았다..
https://github.com/axe312ger/mdx-live-editor
두 라이브러리를 합치는데는 위 라이브러리를 많이 참고했다. 해당 라이브러리는 최신 CRA버전에서는 사용할 수 없다. mdx v1버전으로 만들었기 때문이다. 그래도 참고는 많이 되었다.
easymde와 mdx 라이브러리를 활용하여 구현할 것이다.
전체적인 프로세스를 간략하게 설명하자면
우선 사용자의 입력을 텍스트로 받아옴
=> 텍스트를 MDX로 생각하고 JSX로 반환하는 코드를 생성
=> 해당 코드를 실행하여 JSX요소를 받아온다.
=> 이를 리액트 요소로 만든 후, renderToStaticMarkup 으로 랜더링을 한다.
먼저 폴더구조를 한번 보고 자세히 들어가자
editor
ㄴ editor.ts => easymde를통해 에디터를 염
ㄴ guideLine.ts => 에디터에 기본값으로 넣어줄 값
ㄴ mdxComponent.ts => 커스텀 컴포넌트 정보
ㄴ MdxEditor.tsx => 다른 4개 파일을 종합하여 에디터를 컴포넌트형태로 반환
ㄴ toolbar.ts => 툴바 (에디터 상단) 커스텀
먼저 editor.ts 부분을 보자
/* @jsxRuntime automatic @jsxImportSource react */
import React, { createElement } from 'react';
import EasyMDE from 'easymde';
import { renderToStaticMarkup } from 'react-dom/server';
import { evaluateSync } from '@mdx-js/mdx';
import * as runtime from 'react/jsx-runtime';
//string으로 된 mdx => 컴포넌트로 변환
export const generate = (body: string) => {
const mdx = evaluateSync(body, {
...(runtime as any),
}).default;
return renderToStaticMarkup(createElement(mdx));
};
interface EasyEditerType {
toolbar: any;
easymde: any;
mdxComponents: string;
setContent: React.Dispatch<React.SetStateAction<string>>;
}
/**
*
* @param toolbar toolbar 커스텀
* @param easymde easymde
* @param mdxComponents 커스텀할 컴포넌트 목록
* @param setContent 에디터 값을 useState로 관리할 수 있게
* @returns
*/
export function EasyEditer({
toolbar = [],
easymde: easymdeConfig = {},
mdxComponents = '',
setContent,
}: EasyEditerType) {
const config = {
...{
sideBySideFullscreen: false,
autoDownloadFontAwesome: true,
forceSync: true,
autofocus: true,
indentWithTabs: false,
spellChecker: false,
},
toolbar,
...easymdeConfig,
};
const easymde = new EasyMDE({
...config,
previewRender: (plainText: any) => {
setContent(plainText);
return generate(mdxComponents + plainText);
},
});
EasyMDE.toggleSideBySide(easymde);
return easymde;
}
easymde를 mdx 에디터로 커스텀한 부분이다. 인자로는 툴바옵션,커스텀할 component, easymde, 그리고 에디터의 값을 써야할 경우 쓸수있게 useState의 set 함수를 넣을 수 있게 설정해 두었다.
또한 easymde의 경우 맨 처음에는 preview기능을 꺼두고 시작하는데, 이를 켜놓고 시작하도록 설정해두었다.
커스텀에 가장 중요한 부분은
//string으로 된 mdx => 컴포넌트로 변환
export const generate = (body: string) => {
const mdx = evaluateSync(body, {
...(runtime as any),
}).default;
return renderToStaticMarkup(createElement(mdx));
};
위 부분이다. generate 함수를 만들어 단순 텍스트로 구성된 요소를 리액트 컴포넌트로 만들어 preview단에서 렌더링할 수 있게 도와준다.
guideLine.ts
const guideLine = {
testguide: `<Highlight color="#25c2a0">Docusaurus green</Highlight> and <Highlight color="#1877F2">Facebook blue</Highlight> are my favorite colors.
I can write **Markdown** alongside my _JSX_!`,
};
export default guideLine;
에디터에 기본적으로 값을 넣을 수 있게 설정한 부분이다. 해당 텍스트가 default값으로 설정되서 나오게 된다.
mdxComponent.ts
//설정할 mdx문법
const mdxComponents = `
export const planet = 'World'
export const Highlight = ({children, color}) => (
<span
style={{
backgroundColor: color,
borderRadius: '2px',
color: '#fff',
padding: '0.2rem',
}}>
{children}
</span>
);
`;
export default mdxComponents;
mdx문서에 컴포넌트를 적용할 것들을 넣는 부분이다. 해당부분은 아직 미완성된 상태로 나중에 함수자체를 넣어서 합칠 수 있도록 구현할 생각이다.
toolbar.ts
const toolbarOption = [
'bold',
'italic',
'heading',
'|',
'quote',
'unordered-list',
'ordered-list',
'|',
'link',
'image',
'|',
'preview',
'side-by-side',
'|',
'undo',
'redo',
'guide',
];
export default toolbarOption;
에디터의 도구를 설정할 수 있는 부분이다. easymde 공식문서를 따라 커스텀을 할 수 있다. 제공해주는 기능외에 커스텀해서 만드는것도 가능하다.
MdxEditor.tsx
위에서 설명한 4개의 파일을 합쳐 에디터를 하나의 컴포넌트로 반환해주는 파일이다.
import React, { useEffect, useRef, useState } from 'react';
import styled from '@emotion/styled';
import { EasyEditer } from 'editor/editor';
import 'styles/editor.css';
import 'easymde/dist/easymde.min.css';
import mdxComponents from 'editor/mdxComponent';
import toolbarOption from 'editor/toolbar';
const Textarea = styled.textarea`
min-width: 100%;
min-height: 100%;
`;
interface MDXEditorType {
guideLine: string;
setContent: React.Dispatch<React.SetStateAction<string>>;
}
/**
*
* @param guideLine 에디터에 가이드라인을 제시
* @param setContent 에디터안의 값을 useState로 관리할 수있게
* @returns
*/
export default function MDXEditor({ guideLine, setContent }: MDXEditorType) {
const editorRef = useRef(null);
const [, setEditor] = useState<any>(null);
useEffect(() => {
setEditor(
EasyEditer({
easymde: { element: editorRef.current },
mdxComponents,
toolbar: toolbarOption,
setContent,
}),
);
}, []);
return (
<div className="markdown-body">
<Textarea ref={editorRef} id="editor" defaultValue={guideLine} />
</div>
);
}
작은 프로젝트를 진행해보면서 md파일과 mdx 파일, 그리고 블로그등에서 쓰이는 텍스트 에디터에 대한 공부가 된것같다.
'프로젝트 > 소규모프로젝트들' 카테고리의 다른 글
라즈베리파이로 웹 서버 만들기 (2) - 라파 DB 서버로 사용하기 (1) | 2023.01.24 |
---|---|
라즈베리파이로 웹서버 만들기 (1) - 밖에서 라파 접속하기 (3) | 2023.01.24 |
JS_리액트_TodoList (0) | 2021.12.27 |
MFC를 이용한 디지털신호처리 프로그램 (0) | 2021.12.15 |
JS_독서관리 시스템 (2) | 2021.12.10 |