어떤 AI가 뱉어도, 깨지지 않게.

MODU는 LLM이 쏟아낸 마크다운·LaTeX·표·코드가 뒤엉킨 출력을 깨뜨리지 않고 받아, 정규화하고 조판하는 엔진입니다. 경계부터 가려낸 다음 조판합니다.

붙여넣은 원문
이 노트북은 $999에 팔렸다. `price = $x * 2` 는 코드다. 그러나 $y = x^2$ 는 수식이다.
MODU

이 노트북은 $999에 팔렸다. price = $x * 2 는 코드다. 그러나 y=x2\displaystyle y = x^2 는 수식이다.

다른 조판기는 사람이 정성껏 쓴 소스를 가정합니다. MODU는 LLM이 그러지 않았다고 가정합니다.

TeX도, KaTeX도, 브라우저도, PreTeXt도 모두 사람이 작정하고 쓴 소스를 조판하려고 태어났습니다. 모델의 출력은 그렇지 않습니다. 반쯤 닫히고, 문법이 뒤엉키고, 구분자는 있어도 그만 없어도 그만인 출력 — MODU는 그걸 나중에 땜질한 게 아니라 처음부터 받아내려고 만든 엔진입니다.

이런 것들의 출력을 받아내려고 만들었습니다

  • ChatGPT
  • Claude
  • Gemini
  • Copilot
  • Llama
  • HWP(X)

엔진이 실제로 하는 일

수식이 들어와도 안 깨지는 표

$-\ln|\cos x|$ 안의 | 는 절댓값이지 열 구분이 아닙니다. | 로 셀부터 쪼개는 것 — 다른 데서 표 안 수식이 깨지는 이유가 그겁니다.

붙여넣은 원문
| 함수 | 도함수 | 적분 |
|:--:|:--:|:--:|
| $\tan x$ | $\sec^2 x$ | $-\ln|\cos x|$ |
| $f(x)=|x|$ | $\mathrm{sgn}(x)$ | $\frac{1}{2}x|x|$ |
MODU
함수도함수적분
tanx\displaystyle \tan xsec2x\displaystyle \sec^2 xlncosx\displaystyle -\ln|\cos x|
f(x)=x\displaystyle f(x)=|x|sgn(x)\displaystyle \mathrm{sgn}(x)12xx\displaystyle \frac{1}{2}x|x|

렌더가 아니라 중재

마크다운과 LaTeX는 같은 문자를 두고 싸웁니다 — | _ * $ \. 그 문자가 누구 것인지 판정하는 게 일의 전부이고, 렌더보다 먼저 일어납니다. 제목·볼드·목록·수식이 전부 같은 엉킨 문자열 하나에서 나옵니다.

붙여넣은 원문
# 이차방정식의 근

**근의 공식**:

$$x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}$$

1. 판별식 $D = b^2 - 4ac$ 계산
2. $D > 0$ 이면 서로 다른 두 실근

- 첫째 항목
- 둘째 *항목*
MODU

이차방정식의 근

근의 공식:

x=b±b24ac2ax = \frac{-b \pm \sqrt{b^2-4ac}}{2a}
  1. 판별식 D=b24ac\displaystyle D = b^2 - 4ac 계산
  2. D>0\displaystyle D > 0 이면 서로 다른 두 실근
  • 첫째 항목
  • 둘째 항목

구분자 없는 수식

모델은 $ 기호 하나 없이 LaTeX를 문장 속에 그대로 흘려 넣습니다. MODU는 언어의 경계를 단서 삼아 그걸 찾아내 감싸고, 끝내 수식으로 조판합니다.

붙여넣은 원문
두 점을 지나는 직선은 \ell(x) = 3x + 3 이고, 판별식은 \frac{-b}{2a} 이다.
MODU

두 점을 지나는 직선은 (x)=3x+3\displaystyle \ell(x) = 3x + 3 이고, 판별식은 b2a\displaystyle \frac{-b}{2a} 이다.

모델이 그때그때 쓴 구분자

$…$, \(…\), \[…\] — 모델마다 버릇이 다르고, 한 답변 안에서 버릇을 바꾸기도 합니다. 전부 한 형식으로 정규화합니다.

붙여넣은 원문
인라인 \(a^2+b^2=c^2\) 이고 블록은:

\[E = mc^2\]
MODU

인라인 a2+b2=c2\displaystyle a^2+b^2=c^2 이고 블록은:

E=mc2E = mc^2

수식이 아닌 $

$999는 가격, `price = $x` 는 코드, $y = x^2$ 는 수식입니다. 같은 문자 하나에 문맥이 정하는 세 가지 답.

붙여넣은 원문
이 노트북은 $999에 팔렸다. `price = $x * 2` 는 코드다. 그러나 $y = x^2$ 는 수식이다.
MODU

이 노트북은 $999에 팔렸다. price = $x * 2 는 코드다. 그러나 y=x2\displaystyle y = x^2 는 수식이다.

덜 끝난 입력도 괜찮습니다

스트리밍 출력은 반쯤 쓰이다 만 채로 도착합니다 — 닫히지 않은 $$, 닫히지 않은 **. MODU는 문서를 통째로 버리는 대신, 이미 닫힌 건 조판하고 나머지는 글자 그대로 남깁니다.

붙여넣은 원문
확률은 다음과 같습니다:

$$P(A) = \frac{1}{2}$$

그리고 다음은 $$P(B) = \frac{1{
MODU

확률은 다음과 같습니다:

P(A)=12P(A) = \frac{1}{2}

그리고 다음은 $$P(B) = \frac{1{

규모

5만 자, 한 번에, 에러 0.

제목도 볼드도 없이 — 한국어·영어 산문에 수식 수백 개, 문단에 일부러 딱 붙인 블록 수식까지. 이 문서 전체를 방금 당신의 브라우저에서 MODU가 조판했습니다. 내려보세요.

사용법

문자열을 넣으면, HTML이 나온다.

프레임워크도, 스토어도, DOM도 필요 없습니다. 의존성은 katex와 jszip 둘뿐이라 서버에서 빌드 타임에 돌리든 브라우저에서 실시간으로 돌리든 상관없습니다 — 위 예제들은 전부 이 페이지가 빌드될 때 MODU가 직접 조판한 것입니다.

아직 npm 미배포 — 엔진을 먼저 강화하는 중입니다.

modu
import { renderContentToHtml } from "modu";

// 저 셀 안의 | 는 절댓값인가, 열 구분인가?
// 엔진이 렌더보다 먼저 판정한다.
const html = renderContentToHtml(
  "| $f(x)=|x|$ | $\\frac{1}{2}x|x|$ |\n|---|---|\n| a | b |"
);

놀이터

깨질 만한 걸 붙여넣어 보세요.

ChatGPT·Claude·Gemini의 진짜 출력이든, 아래 적대적 케이스든. 타이핑하는 대로 브라우저에서 바로 렌더됩니다.