React에서 라이브러리를 불러오는 2가지 방식, mathlive 라이브러리 사용하여 equation editor 구현하기

안지수·2025년 10월 9일

equation editor를 만들며, mathlive라는 라이브러리 사용 방법을 알아보면서, 라이브러리 사용 방식에 2가지가 있다는 사실을 알았다.
그것을 간단하게 정리해보려고 한다.

또, 이어서, mathlive 라이브러리 사용 방법을 알아보려고 한다.

🟠 라이브러리 호출 방식 2가지

🔹CDN 방식

: Content Delivery Network 에 있는 파일 (즉, 웹 주소)을 브라우저가 직접 불러옴
-> index.html 안에 script 태그로 넣어줌

  • npm 설치 안해도 바로 쓸 수 있음
  • 오프라인에서는 작동안함
  • 버전 관리 어려움

🔹npm 방식

: npm install로 프로젝트에 직접 설치 (node_modules)

  • 버전 고정 가능
  • 오프라인에서도 작동
  • 유지보수, 배포, 협업에 적합

--> 즉, 직접 배포할 프로젝트에는 당연히 npm 방식으로 라이브러리 불러오는 게 맞음!!!!!

🟠 mathlive 라이브러리 사용

🔹 프로젝트 시작하기

  1. npm install mathlive
  2. import "mathlive";
  3. 코드 추가
<math-field
	// onInput={(e) => setValue((e.target as any).value)}
    ref={mathfieldRef}
    virtual-keyboard-mode="onfocus"
>
	log
</math-field>

-> 그런데, Property 'math-field' does not exist on type 'JSX.IntrinsicElements' 라는 오류 발생

WHY?
: math-field 는 커스톰 웹 컴포넌트라서, typescript에서는 인식하지 못함. 따라서 해당 코드도 인식할 수 있게 아래 코드 글로벌에 추가

import 'react';

declare module 'react' {
  namespace JSX {
    interface IntrinsicElements {
      'math-field': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & {
        value?: string;
        readonly?: boolean;
        disabled?: boolean;
        onInput?: (event: any) => void;
        virtualKeyboardMode?: string;
      };
    }
  }
}

-> typescript에서 math-field 태그 인식할 수 있게 추가

  1. 아래와 같은 문제 발생
ResizeObserver loop completed with undelivered notifications.
    at handleError (webpack-internal:///./node_modules/webpack-dev-server/client/overlay.js:589:58)
    at eval (webpack-internal:///./node_modules/webpack-dev-server/client/overlay.js:613:7)

: 개발 서버에서 브라우저에서 DOM 크기 변화 감시하는 ResizeObserver가 과도하게 처리되어서 뜨는 오류이다. 프로덕션에서는 나타나지 않으므로 해결하지 않아도 되는 문제인데, overlay가 되는 것이 문제였으므로 (빨간 화면)
-> webpack.config.js 를 수정해서 해결하였다.

    devServer: {
        historyApiFallback: true,
        client: {
            overlay: {
                runtimeErrors: (error) => {
                if(error?.message === "ResizeObserver loop completed with undelivered notifications.")
                {
                    console.error(error)
                    return false;
                }
                return true;
                },
            },
        },
    },

-> devServer 즉, 로컬 개발할 때의 동작들을 제어하는 부분에 설정을 추가해준 것. 그 중에서도 client, 브라우저에서 보여주는 부분에 대한 설정 추가
-> 해당 메시지가 뜨면, overlay는 안되지만, 콘솔에는 남게끔 수정해두었다.

🔹 Customization

1. Custom Layout

keyboard.layouts = {
	rows: [
            ["1","2","3","4","5","x","y",""],
            ["6","7","8","9","0","+","-","\\sqrt{#0}"],
            ["\\%","-",".","\\frac{#0}{#0}","{#0}\\frac{#0}{#0}","\\cdot","\\div","\\sqrt[#0]{#0}"],
            ["","{#0}^{#0}","({#0})","\\le","\\lt","=","\\ge","\\gt"],
            ["\\pi","","","","","","","\\lvert{#@}\\rvert"],
         ]
};

2. Remove menu, keyboard show up always (not disappear even when the focus is out)

        const style = document.createElement("style");
        style.innerHTML = `
            .digitalSHSATModule__eeContainer { position: relative; }
            math-field::part(menu-toggle),
            math-field::part(virtual-keyboard-toggle) { display: none !important; }
            .digitalSHSATModule__eeContainer .ML__virtual-keyboard {
                position: absolute !important;
                top: calc(100% + 8px) !important;
                left: 0 !important;
                width: 620px !important;
                z-index: 999 !important;
            }
        `;
        document.head.appendChild(style);

-> style을 추가해준다. display: none이 되도록!
math-field::part(menu-toggle),
math-field::part(virtual-keyboard-toggle) { display: none !important; }

3. location in the question

        const container = document.querySelector(".digitalSHSATModule__eeContainer");
        if (container) keyboard.container = container;
        setTimeout(() => {
            const vk = container?.querySelector(".ML__keyboard") as HTMLElement;
            if (vk) {
                vk.style.height = "260px";
                vk.style.pointerEvents = "auto";
                vk.style.opacity = "1";
                vk.style.visibility = "visible";
            }
        }, 50);

--> 기준이 되는 컨테이너를 바꿔주었다. 그 안에 담기도록 위치를 조절해주었다.

4. Style customization

                const preventHide = (e: FocusEvent) => {
                    setTimeout(() => {
                        if (mf && document.activeElement !== mf) { // refocus
                            mf.focus();
                        }
                        keyboard.show();
                        if (vk) {
                            vk.style.display = "block";
                            vk.style.visibility = "visible";
                            vk.style.opacity = "1";
                            vk.style.pointerEvents = "auto";
                        }
                    }, 10); // delay after hide keyboard
                };
                mf.addEventListener("blur", preventHide, true);
                mf.addEventListener("focusout", preventHide, true);

-> focus가 사라지면, 키보드가 사라지는 것이다. 이거를 해결하는데, 가장 오랜 시간이 걸렸다. ㅋㅋ 그래서, 포커스 아웃이 되면, refocus 되도록 만들었다.

profile
지수의 취준, 개발일기

0개의 댓글