Nevertheless

[React 18 , TS] Toast-ui 사용하기 본문

프로젝트

[React 18 , TS] Toast-ui 사용하기

hxx_1 2024. 12. 10. 16:52

게시물 작성 페이지 : Editor.tsx

 

TS 에서 props 인터페이스 정의 

interface TuiEditorProps {
  onChange?: (value: string) => void;
  value?: string;
}
  • onChange: 에디터 내용이 변경될 때 호출되는 콜백 함수, 문자열 파라미터를 받고 반환값이 없는(void) 함수 타입, 물음표(?) 는 이 prop 이 선택적임을 나타낸다.
  • value: 에디터에 표시될 초기 텍스트 값, 문자열 타입이며 선택적 prop 이다.
const TuiEditor = forwardRef<Editor, TuiEditorProps>((props, ref) => {
  const { onChange } = props;
  ... 
  )
});

컴포넌트를 forwardRef 로 감싸서 상위 컴포넌트에서 감싸서 상위 컴포넌트에서 ref 를 전달 받을 수 있게 한다. 

forwardRef<참조할_컴포넌트_타입, props_타입>

  • Editor: ref가 가리킬 수 있는 대상의 타입
  • TuiEditorProps: 컴포넌트가 받을 수 있는 props의 타입
  • props: 일반적인 속성들 (onChange, value 등)
  • ref: 에디터 인스턴스를 직접 조작할 수 있게 해주는 참조값
const { onChange } = props; //props 에서 onChange 함수를 구조분해 할당으로 추출 

  useEffect(() => {
    if ((ref as any)?.current) { // ref.current 가 존재하는지 확인
      const instance = (ref as any).current.getInstance(); // toast ui editor 의 인스턴스 가져오기
      
      //에디터의 내용이 변경될 때마다 실행될 이벤트 리스너 등록
      instance.on('change', () => {
        if (onChange) { //onChange prop이 존재하는 경우에만 실행
          onChange(instance.getHTML()); // 현재 에디터의 HTML 내용을 가져와서 OnChange 함수에 전달
        }
      });
    }
  }, [onChange, ref]); //onChange 나 ref 가 변경될 때마다 useEffect 재실행
  • ref as any를 사용하는 이유는 TypeScript의 엄격한 타입 검사를 우회하기 위함
  • getInstance()는 Toast UI Editor의 메서드로, 단순히 에디터에 작성된 내용만을 의미하는 것이 아니라, 에디터의 핵심 기능들을 조작할 수 있는 인스턴스를 반환하는 메서드이다.
  • instance.on('change', ...)는 에디터 내용이 변경될 때마다 콜백 함수를 실행하도록 이벤트 리스너를 설정
  • instance.getHTML()은 현재 에디터에 작성된 내용을 HTML 형식으로 반환
 return (
    <Box display="flex" justifyContent="center" width="100%">
      <Box width="75%">
        <Editor
          //Editor 컴포넌트에 대한 참조를 생성,컴포넌트 외부에서 Editor 의 메서드들을 직접 호출할 수 있음 
          ref={ref}
          height="500px" // 높이 
          placeholder="Please Enter Text."
          previewStyle="tab" // 미리 보기 방식
          initialEditType="wysiwyg" // 초기 편집 타입
          toolbarItems={[ // 에디터 툴바에 표시될 도구들을 그룹별로 설정
            ['heading', 'bold', 'italic', 'strike'],
            ['hr', 'quote'],
            ['ul', 'ol', 'task', 'indent', 'outdent'],
            ['table', 'image', 'link'],
            ['code', 'codeblock'],
          ]}
          initialValue={props.value || " "}  // 초기 내용 설정, 값이 없을 경우 공백
          usageStatistics={false} // 사용 통계 수집 비활성화
          plugins={[[colorSyntax, colorSyntaxOptions],fontSize]} // 색상 구문과 글꼴 크기 플러그인 활성화 
        />
      </Box>
    </Box>
  )

 


💡전체 코드

// Editor.tsx
import '@toast-ui/editor/dist/toastui-editor.css'
import { Editor } from '@toast-ui/react-editor'
import React, { forwardRef, useEffect } from 'react'
import 'tui-color-picker/dist/tui-color-picker.css'
import '@toast-ui/editor-plugin-color-syntax/dist/toastui-editor-plugin-color-syntax.css'
import colorSyntax from '@toast-ui/editor-plugin-color-syntax'
import fontSize from "tui-editor-plugin-font-size";
import "tui-editor-plugin-font-size/dist/tui-editor-plugin-font-size.css";
import { Box } from '@mui/material'

interface TuiEditorProps {
  onChange?: (value: string) => void;
  value?: string;
}

const colorSyntaxOptions = {
  preset: [
    '#333333', '#666666', '#FFFFFF', '#EE2323',
    '#F89009', '#009A87', '#006DD7', '#8A3DB6',
    '#781B33', '#5733B1', '#953B34', '#FFC1C8',
    '#FFC9AF', '#9FEEC3', '#99CEFA', '#C1BEF9',
  ],
}

const TuiEditor = forwardRef<Editor, TuiEditorProps>((props, ref) => {
  const { onChange } = props;

  useEffect(() => {
    if ((ref as any)?.current) {
      const instance = (ref as any).current.getInstance();
      instance.on('change', () => {
        if (onChange) {
          onChange(instance.getHTML());
        }
      });
    }
  }, [onChange, ref]); 

  return (
    <Box display="flex" justifyContent="center" width="100%">
      <Box width="75%">
        <Editor
          ref={ref}
          height="500px"
          placeholder="Please Enter Text."
          previewStyle="tab"
          initialEditType="wysiwyg"
          toolbarItems={[
            ['heading', 'bold', 'italic', 'strike'],
            ['hr', 'quote'],
            ['ul', 'ol', 'task', 'indent', 'outdent'],
            ['table', 'image', 'link'],
            ['code', 'codeblock'],
          ]}
          initialValue={props.value || " "}  // Add value prop support
          usageStatistics={false}
          plugins={[[colorSyntax, colorSyntaxOptions],fontSize]}
        />
      </Box>
    </Box>
  )
});

export default TuiEditor;