브라우저에서는 TeX이 돌지 않습니다. 그래서 LaTeX 수식을 웹 페이지에 올리는 도구인 MathJax와 KaTeX는 둘 다 TeX의 수식 조판을 JavaScript로 다시 구현한 것이며, 같은 문제를 두고 정반대의 선택을 했습니다. 같은 수식 500개를 조판시키니 KaTeX는 45밀리초, MathJax는 264밀리초가 걸렸습니다. 그러나 속도만 보고 고르면 엉뚱한 데서 발목을 잡힙니다. 예컨대 둘 다 기본 설정에서는 $...$를 수식으로 인식하지 않습니다. 이 페이지에서는 두 설계의 차이, 실제로 뜨는 오류 문구, 그리고 페이지에 붙여 넣은 TeX이 끝까지 남는지를 살펴봅니다.
KaTeX와 MathJax의 차이: 어느 쪽을 올릴까
수식이 많고 속도가 중요하면 KaTeX, 써 놓은 LaTeX을 되도록 그대로 통과시키고 싶으면 MathJax입니다. 속도 차이는 누가 더 열심히 최적화했느냐의 문제가 아니라 API의 생김새 자체입니다. KaTeX의 katex.renderToString(...)은 문자열을 동기적으로 돌려줍니다. 호출한 자리에서 HTML을 손에 넣으므로, 나중에 조판 결과가 끼어들어 레이아웃이 튀는 일이 없습니다. 반면 MathJax는 브라우저에서 MathJax.typesetPromise()를 부르도록 설계되어, 이름 그대로 Promise를 돌려줍니다. 독자가 순간적으로 날것의 \frac{1}{2}를 보게 되는 것은 이 비동기성의 부작용입니다.
<!-- MathJax 3: configure BEFORE the script tag loads -->
<script>
window.MathJax = {
tex: { inlineMath: [["$", "$"], ["\\(", "\\)"]] }
};
</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<!-- KaTeX: stylesheet, engine, then the auto-render pass -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body);"></script>$...$가 수식이 되지 않는 이유: KaTeX 소스에 있는 한 줄의 주석
두 라이브러리 모두 기본 설정에서는 $...$를 인라인 수식으로 다루지 않습니다. 설정을 빠뜨린 것이 아니라 의도적인 기본값입니다. KaTeX auto-render의 소스를 열어 보면 $ 설정 줄이 주석 처리된 채 그대로 놓여 있고, 바로 위에 이유가 적혀 있습니다. “LaTeX uses $…$, but it ruins the display of normal $ in text”. 통화 기호 $가 본문에 두 번 나오면 그 사이가 수식이 되어 버리기 때문입니다. MathJax의 기본값도 같아서 inlineMath는 \(...\)뿐이고, displayMath가 $$...$$와 \[...\]입니다. “수식이 소스 그대로 나온다”는 문의의 상당 부분은 바로 이 한 가지로 설명됩니다.
// KaTeX auto-render, default delimiters (dist/contrib/auto-render.js):
// { left: "$$", right: "$$", display: true }
// { left: "\\(", right: "\\)", display: false }
// { left: "\\[", right: "\\]", display: true }
// plus \begin{equation} \begin{align} \begin{alignat} \begin{gather} \begin{CD}
//
// and this line is deliberately commented out in the source:
// // LaTeX uses $...$, but it ruins the display of normal `$` in text:
// // {left: "$", right: "$", display: false},
// turn it on yourself only if the page has no currency amounts:
renderMathInElement(document.body, {
delimiters: [
{ left: "$$", right: "$$", display: true },
{ left: "$", right: "$", display: false },
{ left: "\\(", right: "\\)", display: false },
{ left: "\\[", right: "\\]", display: true }
],
ignoredTags: ["script", "noscript", "style", "textarea", "pre", "code"]
});KaTeX이 하지 못하는 일과 실제로 뜨는 오류 문구
KaTeX이 지원하는 것은 LaTeX 수식의 부분집합이며, 벗어난 것은 예외로 던져집니다. 문구는 일정해서 KaTeX parse error: Undefined control sequence: \eqref at position 1: 같은 모양입니다. 가장 아픈 구멍은 수식 번호의 상호 참조입니다. \label도 \eqref도 정의되어 있지 않아, 번호를 붙이고 본문에서 참조하는 문서는 이 시점에서 KaTeX만으로는 성립하지 않습니다. 화학식 \ce{H2O}도 맨 KaTeX에서는 통하지 않아 mhchem 확장을 따로 불러와야 합니다. 또 하나 자주 밟는 것이 KaTeX parse error: {align} can be used only in display mode. 로, 인라인 구분 기호 안에 align 환경을 쓰면 나옵니다.
다만 “KaTeX은 매크로를 못 쓴다”는 것은 오해입니다. \newcommand, \def, \gdef는 모두 통하고, macros 옵션에 빈 객체를 넘겨 두면 \gdef로 만든 정의가 호출을 넘어 남습니다. 사이트 전체에서 쓰는 자작 매크로는 이 방식으로 한 번만 흘려 넣는 것이 정석입니다. 거꾸로 MathJax는 TeX 패키지에 해당하는 구현을 서른 개쯤 내장하고 있습니다. ams, amscd, mathtools, mhchem, cancel, braket, bussproofs, empheq, colortbl 등이며, 이 목록이 곧 “호환성이 넓다”의 실체입니다. 그리고 수식 하나 때문에 페이지가 무너지지 않길 바란다면 throwOnError: false를 기억해 두십시오. KaTeX은 예외를 던지는 대신 실패한 부분을 빨강(#cc0000)으로 그대로 보여 주고 넘어갑니다.
| 작성한 것 | KaTeX | MathJax |
|---|---|---|
\frac \int \underbrace \text | 통과 | 통과 |
\newcommand \def \gdef | 가능(macros로 지속) | 가능(macros 설정) |
\label \eqref | 불가. Undefined control sequence | 가능(tags 설정으로 번호 매김) |
\ce{H2O} | mhchem 확장을 따로 추가해야 합니다 | 내장 |
align (inline) | {align} can be used only in display mode. | 통과 |
MathML은 지금 쓸 수 있는가: 2023년에 메워진 구멍
주요 브라우저는 모두 MathML을 표시할 수 있습니다. 가장 오래 비어 있던 자리는 Chrome이었는데, 넣었다가 뺀 뒤 버전 109에서 정식으로 복귀했습니다(Edge도 같은 109부터). Firefox는 버전 2 시절부터, Safari는 10부터 지원합니다. 그렇지만 실무에서 MathML을 직접 쓰는 경우는 아직 많지 않습니다. 오히려 중요한 것은 두 라이브러리가 모두 MathML을 출력에 포함한다는 점입니다. KaTeX은 눈에 보이는 HTML 옆에 <math> 요소를 반드시 붙이고, MathJax의 표준 번들 tex-mml-chtml.js는 보조 기술용 MathML을 더하는 확장을 처음부터 불러옵니다. 스크린 리더가 수식을 읽을 수 있는 것은 이 숨은 층 덕분입니다.
페이지에 붙인 TeX은 남는가: 어느 쪽에서 복사해 되돌릴 수 있나
KaTeX이면 남고, MathJax는 기본 설정에서 남지 않습니다. KaTeX이 내보내는 MathML 안에는 반드시 <annotation encoding="application/x-tex">가 있고, 그 안에 원래의 TeX이 그대로 들어 있습니다. x^2+1을 넘기면 출력 어딘가에 x^2+1이라는 문자열이 그대로 남습니다. 게다가 배포물에는 copy-tex라는 확장이 함께 들어 있는데, 그 소스 주석에 “Replace .katex elements with their TeX source”라고 적혀 있습니다. 이를 불러 두면 수식을 선택해 복사할 때 클립보드에 담기는 것이 글자 모양이 아니라 $x^2+1$이 됩니다. MathJax의 일반적인 HTML 출력에는 원래의 TeX이 들어가지 않습니다. 대신 오른쪽 클릭 메뉴에 “Show Math As”가 있어 원래 기술을 꺼내도록 되어 있습니다.
<!-- what KaTeX puts in the DOM for x^2+1 -->
<span class="katex"><span class="katex-mathml"><math
xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow>
<msup><mi>x</mi><mn>2</mn></msup><mo>+</mo><mn>1</mn>
</mrow><annotation encoding="application/x-tex">x^2+1</annotation>
</semantics></math></span><span class="katex-html" aria-hidden="true">...</span></span>
<!-- load this and Ctrl-C on a formula yields $x^2+1$, not glyphs -->
<script defer src="https://cdn.jsdelivr.net/npm/katex/dist/contrib/copy-tex.min.js"></script>자작 매크로를 브라우저까지 보내기
여기가 문서 전체를 HTML로 바꾸는 도구들과의 경계선입니다. MathJax도 KaTeX도 받는 것은 수식 조각뿐이며, 여러분의 프리앰블을 읽지 않습니다. 그래서 .tex 첫머리에 \newcommand{\R}{\mathbb{R}}를 적어 두어도 브라우저 쪽에는 아무것도 전해지지 않고, \R은 정의되지 않은 채 빨갛게 나옵니다. 매크로는 설정 객체로 따로 넘겨야 합니다. 이 함정은 변환 도구를 쓸 때도 마찬가지여서, make4ht의 mathjax 모드는 수식을 LaTeX 그대로 HTML에 남기므로 자작 매크로가 역시 전개되지 않습니다. 변환 도구 쪽 사정은 “LaTeX → HTML”에 맡기지만, 대처는 같습니다. 매크로 정의를 한곳에 모아 TeX과 JavaScript 양쪽에서 읽게 하는 것입니다.
// KaTeX: one shared object, and \gdef survives from call to call
const macros = {};
katex.renderToString("\\gdef\\R{\\mathbb{R}}", { macros });
katex.renderToString("f\\colon \\R \\to \\R", { macros }); // \R resolves
// or declare them up front, the same way for the auto-render pass:
renderMathInElement(document.body, {
macros: { "\\R": "\\mathbb{R}", "\\eps": "\\varepsilon" },
throwOnError: false // print the bad source in red, do not break the page
});
// MathJax 3: the equivalent lives in the config object
window.MathJax = {
tex: {
macros: { R: "\\mathbb{R}", eps: "\\varepsilon" },
tags: "ams" // this is what enables \label and \eqref
}
};- 수식이 소스 그대로 나온다 → 구분 기호를 의심하십시오.
$...$는 KaTeX에서도 MathJax에서도 기본 비활성입니다. - 문서가
\eqref를 쓴다 → MathJax를 고르고tags: "ams"를 설정합니다. KaTeX에는 번호 매김 장치가 아예 없습니다. - 수식이 수백 개인 목록 페이지 → KaTeX. 동기 렌더링이라 레이아웃이 나중에 튀지 않습니다.
- 자작 매크로가 있다 → 반드시
macros로 넘기십시오. 프리앰블은 브라우저에 닿지 않습니다. - 수식 하나로 페이지가 무너지면 곤란하다 → KaTeX의
throwOnError: false. 빨간 글씨로 표시하고 넘어갑니다. - 문서 전체를 웹에 올리고 싶다 → 그것은 수식 렌더러의 일이 아닙니다(“LaTeX → HTML” 참조).