수식 아래첨자 표시 수정: 유니코드를 LaTeX로 변환

이 글의 목차 8개 항목
  • 분야: FE
  • 기술 스택: React · TypeScript

문제와 원인

skctMathMarkdown의 수식 후보 문자 집합과 판별 조건에 유니코드 아래첨자가 빠져 aₙ₋₁을 수식으로 고르지 못했다. 표기 변환도 _n과 _(...)만 처리해 유니코드 아래첨자가 원문 그대로 표시됐다.

수정 전 코드

proseMath

function proseMath(text: string) {
  return text.replace(/[|√A-Za-z0-9α-ωΑ-Ω][A-Za-z0-9α-ωΑ-Ω_\^⁰¹²³⁴⁵⁶⁷⁸⁹=+−×÷*\/≥≤≠<>|(). \t{}-]*/gu, raw => {
    const expression = raw.trimEnd().replace(/[.,]+$/u, "");
    if (!/(?:_[A-Za-z\d(]|\^[A-Za-z\d-]|[⁰¹²³⁴⁵⁶⁷⁸⁹√≥≤≠]|[\w)]\s*[=+×÷−]\s*[\w(|]|\d\s*\/\s*\d)/u.test(expression)
      || /^\d{4}\/\d{1,2}\/\d{1,2}$/u.test(expression)) return raw;
    return `$${notation(expression)}$${raw.slice(expression.length)}`;
  });
}

notation

.replace(/_\(([^()]+)\)/gu, "_{$1}")
.replace(/_([A-Za-zα-ωΑ-Ω]|\d+)/gu, "_{$1}")

수정 후 코드

proseMath

수식 후보 추출·판별에 아래첨자 숫자와 n·k·i·j, 아래첨자 +·−를 추가했다. 영문·그리스 문자 뒤의 아래첨자를 한 구간으로 읽고 대응값을 합쳐 aₙ₋₁을 a_{n-1}로 변환한다.

function proseMath(text: string) {
  return text.replace(/[|√A-Za-z0-9α-ωΑ-Ω][A-Za-z0-9α-ωΑ-Ω_\^⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉₊₋ₙₖᵢⱼ=+−×÷*\/≥≤≠<>|(). \t{}-]*/gu, raw => {
    const expression = raw.trimEnd().replace(/[.,]+$/u, "");
    if (!/(?:_[A-Za-z\d(]|\^[A-Za-z\d-]|[⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉ₙₖᵢⱼ√≥≤≠]|[\w)]\s*[=+×÷−]\s*[\w(|]|\d\s*\/\s*\d)/u.test(expression)
      || /^\d{4}\/\d{1,2}\/\d{1,2}$/u.test(expression)) return raw;
    return `$${notation(expression)}$${raw.slice(expression.length)}`;
  });
}

notation

const subscripts: Record<string, string> = {
  "₀": "0", "₁": "1", "₂": "2", "₃": "3", "₄": "4",
  "₅": "5", "₆": "6", "₇": "7", "₈": "8", "₉": "9",
  "₊": "+", "₋": "-", "ₙ": "n", "ₖ": "k", "ᵢ": "i", "ⱼ": "j",
};

return expression
  .replace(
    /([A-Za-zα-ωΑ-Ω])([₀₁₂₃₄₅₆₇₈₉₊₋ₙₖᵢⱼ]+)/gu,
    (_, base: string, subscript: string) =>
      `${base}_{${[...subscript].map(char => subscripts[char]).join("")}}`,
  )
  .replace(/_\(([^()]+)\)/gu, "_{$1}")
  .replace(/_([A-Za-zα-ωΑ-Ω]|\d+)/gu, "_{$1}");

저장된 문제 원문은 유지하고 표시용 Markdown을 변환한다. 인라인 코드·코드 블록·기존 수식 구분자 내부와 날짜를 보호하는 기존 처리도 유지했다.

검증 결과

원본 전후 skctMathMarkdown에 같은 6개 문자열을 넣어 반환된 Markdown을 비교했다. 아래첨자 3개는 0/3개(0%)에서 3/3개(100%)로, 전체 기대 문자열 일치는 3/6개에서 6/6개로 바뀌었다.

입력수정 전수정 후·기대 Markdown
aₙaₙ$a_{n}$
aₙ₋₁aₙ₋₁$a_{n-1}$
xₖ₊₁xₖ₊₁$x_{k+1}$
a_n$a_{n}$$a_{n}$
인라인 코드 `aₙ``aₙ``aₙ`
날짜 2026/9/302026/9/302026/9/30

기존 표기·코드·날짜 3/3개 조건은 전후 같은 결과를 유지했다.

같은 문자열 6개를 원본 전후 함수에 다시 전달해 로컬 Node에서 각 1회 실행했다. 아래첨자 3개가 LaTeX로 변환됐고 기존 표기·인라인 코드·날짜 입력은 그대로 남았다.

아래첨자 3개 변환과 기존 표기 3개 보존원본 보기 (새 창)

아래첨자 3개 변환과 기존 표기 3개 보존

100%원본 보기 (새 창)

확대한 이미지는 상하좌우로 이동할 수 있습니다. 키보드는 이미지 영역에 초점을 두고 방향키를 사용하세요. Esc로 닫습니다.