Jekyll 블로그 MathJax 3.0 문법 렌더링

· by 박승재

MathJax는 브라우저에서 수학 수식(LaTeX, MathML 등)을 렌더링하는 자바스크립트 엔진으로 2019년 9월 5일 v3.0.0 업데이트를 통해 큰 성능 향상이 있었다. 그래서 더 빨라진 MathJax 3을 웹페이지 수학 수식 엔진으로 적용해보려 한다.

적용

<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>

코드 한 줄이면 적용 가능하다.

참고: The MathJax Components

웹페이지 다운로드 효율성을 위해 최소한의 컴포넌트만 사용하고 싶다면 아래와 같이 startup 컴포넌트를 사용하면 된다.

<script>
  window.MathJax = {
    loader: {
      load: ['input/tex-base', 'output/svg']
    },
  };
</script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/startup.js"></script>

이제 브라우저로 코드가 렌더링 되는 것을 확인할 수 있다.

호환성 해결

하지만 Jekyll에서는 MathJax 코드를 적용해도 수식이 렌더링 되지 않을 것이다.

그 이유는 Jekyll의 마크다운 엔진 kramdown에서는 여전히 MathJax v2 문법을 기준으로 수식을 생성하기 때문이다. 문제는 여기서 생긴다.

MathJax API 변경사항 중 MathJax v2 사용하는 <script type="math/tex"> 표현식을 v3에서는 삭제했다. 따라서 Jekyll 블로그에 MathJax v3 코드를 그대로 적용하면 수식이 제대로 보이지 않는다.

이제 MathJax의 호환성 문제를 해결해보도록 하자.

window.MathJax = {
  options: {
    renderActions: {
      find: [10, function (doc) {
        for (const node of document.querySelectorAll('script[type^="math/tex"]')) {
          const display = !!node.type.match(/; *mode=display/);
          const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
          const text = document.createTextNode('');
          node.parentNode.replaceChild(text, node);
          math.start = { node: text, delim: '', n: 0 };
          math.end = { node: text, delim: '', n: 0 };
          doc.math.push(math);
        }
      }, ''],
    },
  },
};

위와 같이 <script type="math/tex">을 찾아서 직접 렌더링하면 된다.

find의 첫 번째 요소 10MathItem.STATE.FINDMATH의 상숫값이다.

참고: Changes in the MathJax API

아래는 최종적으로 완성된 코드이다.

<script>
  window.MathJax = {
    loader: {
      load: ['input/tex-base', 'output/svg']
    },
    options: {
      renderActions: {
        findScript: [10, function (doc) {
          for (const node of document.querySelectorAll('script[type^="math/tex"]')) {
            const display = !!node.type.match(/; *mode=display/);
            const math = new doc.options.MathItem(node.textContent, doc.inputJax[0], display);
            const text = document.createTextNode('');
            node.parentNode.replaceChild(text, node);
            math.start = { node: text, delim: '', n: 0 };
            math.end = { node: text, delim: '', n: 0 };
            doc.math.push(math);
          }
        }, '']
      }
    },
  };
</script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/startup.js"></script>

서비스 워커 등록

서비스 워커(Service Worker)로 MathJax가 사용하는 컴포넌트와 리소스를 캐싱하면 더 빠르게 웹페이지를 불러올 수 있다.

서비스 워커의 기본 세팅 방법은 Service Worker와 Workbox: 웹페이지를 빠르게 만드는 새로운 방법을 참고하자.

필자는 Workbox를 이용해 서비스 워커를 구축했다.

Workbox에서는 아래와 같이 jsDelivr CDN 주소를 등록하면 우선적으로 캐싱된 리소스를 불러와 사용할 수 있다.

// Cache jsDelivr CDN Assets
registerRoute(
  /^https:\/\/cdn\.jsdelivr\.net/,
  new CacheFirst({
    cacheName: 'jsdelivr',
    plugins: [
      new CacheableResponsePlugin({
        statuses: [0, 200],
      }),
      new ExpirationPlugin({
        maxAgeSeconds: 60 * 60 * 24 * 14, // 14 Days
      }),
    ],
  }),
);

리소스가 캐싱되는지 확인하기 위해 Chrome으로 웹페이지에 접속해보자.

screenshot

개발자 모드(F12) > Network에서 MathJax 리소스를 서비스 워커에서 가져옴을 확인할 수 있다.