Monaco Editor 라이브러리 적용기

김지원·2024년 2월 6일
1

Frontend

목록 보기
24/28

최근 사내에서 개발자들을 위한 간단한 코드 에디터 UI를 개발해야 일이 생겼다.

Task: Create UI to see/edit the configuration snapshot

개발 배경은 다음과 같다.
Customize가 가능한 대시보드를 만드는 프로젝트를 진행 중에 있는데, Customize를 위해 사용되는 configurations 를 설정하는 도구를 1. 엑셀 => 2. RDS 로 최근에 migrate했다.
최종 목표는 해당 대시보드 내에서 drag&drop 과 각 차트나 테이블 등 컴포넌트별로 edit할 수 있도록 UI를 업그레이드 할 예정이지만
개발자들의 편의를 위해, 개발자들을 위한 UI를 빠르게 개발하기로 결정하였다.

configurations 에 해당하는 raw json 을 보고 수정할 수 있는 UI를 개발하는 것이 목표다.

처음으로 User가 아닌 개발자를 위한 개발을 해보았다!

👩🏻‍💻 Next.js 에 Monaco Editor 적용하기

📌 사용한 라이브러리: Monaco Editor
📌 사용한 webpack: @monaco-editor/react
About Monaco Editor for React - use the monaco-editor in any React application without needing to use webpack (or rollup/parcel/etc) configuration files / plugins

설치하기

yarn add monaco-editor monaco-editor-webpack-plugin @monaco-editor/react

📃 MonacoEditor Component 만들기

import Editor from '@monaco-editor/react';
import { LANGUAGE_TYPE } from './constants';
import { ErrorType, LanguageType, MarkerType } from './type';

interface MonacoEditorProps {
  value: string;
  onChange: (newValue: string, event: any) => void;
  onValidation: (newErrors: ErrorType[]) => void;
  language?: LanguageType; // Default is json
}

const MonacoEditor: React.FC<MonacoEditorProps> = ({
  value,
  onChange,
  onValidation,
  language = LANGUAGE_TYPE.JSON,
}) => {
  function handleEditorValidation(markers: MarkerType[]) {
    const markerErrors = Array.from(
      new Set(
        markers.map((marker) => ({
          startLineNumber: marker.startLineNumber,
          endLineNumber: marker.endLineNumber,
        }))
      )
    );
    onValidation(markerErrors);
  }

  return (
    <Editor height="80vh" language={language} value={value} onChange={onChange} onValidate={handleEditorValidation} />
  );
};

export default MonacoEditor;

🔵 props
1. value: Required
- MonacoEditor 에 보여줄 내용, input value라고 생각하면 됨
2. onChange: Required
3. onValidation: Required
- json format에 맞지 않으면 submit button을 disable 해야하기 때문에 필요함
4. language: Optional, default is json

📃 MonacoEditor Component 를 사용하는 component 예시

const SettingModal = () => {
  const [code, setCode] = useState('');
  const [errors, setErrors] = useState<ErrorType[]>([]);

  const handleCodeChange = (newCode) => {
    setCode(newCode);
  };

  const handleValidation = (newErrors) => {
    setErrors(newErrors);
  };

  return (
      <MonacoEditor
        value={code}
        onChange={handleCodeChange}
        onValidation={handleValidation}
        language={LANGUAGE_TYPE.JSON}
      />
  );
};

export default SettingModal;

참고

DiffEditor 을 사용하고 싶었으나, onChange property가 없는 관계로 Editor을 사용하였다.

아래와 같이 여기에 의하면 manually 하게 onChange 동작 방식의 function을 정의하여 사용할 수 있다.

You could use something like this,

  function handleEditorDidMount(editor, monaco) {
    console.log('handleEditorDidMount')
    // here is another way to get monaco instance
    // you can also store it in `useRef` for further usage
    monacoRef.current = monaco;
    const ed = editor.getModel().modified;
    ed.onDidChangeContent((event) => {
      onChange(ed.getValue());
    });
  }

Note that this can cause memory leak since too many listeners can be added with onDidChangeContent

하지만 memory leak 을 일으킬 수 있다고 하여 일단 1차적으로는 Editor를 사용하기로 하였다.

profile
Make your lives Extraordinary!

0개의 댓글