'7,016만 3천원'을 정규식이 못 읽는다 — 한글 수사가 섞인 금액 파싱

·

문서에 금액이 실제로 적혀 있는지 검사하는 린트 규칙을 만들었다. 표 이미지에만 숫자가 있고 본문에는 없는 문서를 걸러내려는 목적이었다. 규칙을 만들고 첫 문서를 돌렸는데 치명 판정이 났다. 금액은 분명히 본문에 있었다.

패턴과 입력

패턴은 이렇게 생겼다.

const moneyValRe =
  "\\d[\\d,]*\\s*(?:억\\s*(?:\\d[\\d,]*\\s*)?)?(?:만\\s*(?:\\d[\\d,]*\\s*)?)?원";
const contextMoneyRe = new RegExp(`(?:임대보증금|보증금)[^0-9]{0,10}${moneyValRe}`);

의도는 “보증금”이라는 단어 근처에 금액이 나오면 통과다. 억과 만 단위를 옵션으로 넣었으니 한국식 금액은 다 잡힐 거라고 생각했다.

실제로 문서에 쓴 문장은 이거였다.

임대보증금 7,016만 3천원에 월 임대료 44만 2,570원

직접 돌려서 확인한 결과다.

const re = /(?:임대보증금|보증금)[^0-9]{0,10}\d[\d,]*\s*(?:억\s*(?:\d[\d,]*\s*)?)?(?:만\s*(?:\d[\d,]*\s*)?)?/;

re.test("임대보증금 7,016만 3천원");    // false
re.test("임대보증금 7,016만 3,000원");  // true
re.test("임대보증금 7,016만원");         // true
re.test("보증금 1억 3,316만 3,000원");  // true
re.test("보증금 1억 3,316만 3천원");     // false

왜 안 걸리나

패턴을 조각으로 보면 금방 보인다.

조각읽는 것
\d[\d,]*앞자리 숫자 — 7,016
(?:억\s*(?:\d[\d,]*\s*)?)?억 단위와 그 뒤 숫자
(?:만\s*(?:\d[\d,]*\s*)?)?만 단위와 그 뒤 숫자

뒤에 올 수 있는 것을 \d[\d,]*로만 열어 뒀다. 그래서 3,000원은 통과하고 3천원은 막힌다. 이 패턴 어디에도 없기 때문이다.

한국어 금액 표기는 큰 단위 뒤에 작은 수사가 다시 붙는다. 만·억으로 4자리씩 끊은 다음, 그 안쪽을 또 천·백·십으로 쓸 수 있다. 7,016만 3천원은 아라비아 숫자와 한글 수사가 한 문자열에서 두 번 번갈아 나온다. 숫자만 처리하는 패턴은 이 교대를 못 따라간다.

같은 금액을 쓰는 방법이 최소 이만큼 된다.

70,163,000원
7,016만 3,000원
7,016만 3천원
칠천십육만 삼천원
7016.3만원

정규식을 키우지 않은 이유

(?:천\s*)?을 넣으면 당장은 통과한다. 그런데 문제가 하나 남는다.

이 린트의 목적은 표 이미지 안에만 있는 숫자가 본문 텍스트에도 있는지 확인하는 것이다. 검색엔진과 요약 모델은 이미지 속 숫자를 읽지 못하니, 대표 금액은 문장으로도 써야 한다는 규칙이다. 그러려면 본문의 금액이 표의 금액과 같은 값인지 확인해야 한다.

정규식은 매치 여부만 답한다. 7,016만 3천원이 원문 표의 70,163,000과 같은 값인지는 판정하지 못한다. 한글 수사를 허용하는 순간 표기 변형이 늘어나고, 대조는 결국 사람 눈으로 넘어간다.

매치와 계산은 다른 일이다. 값을 비교하려면 파서가 필요하고, 파서를 넣으면 이번엔 파서가 틀릴 수 있다.

대신 표기를 고정했다

그래서 패턴은 그대로 두고 작성 규칙을 하나로 정했다. 큰 단위 뒤 자리는 아라비아 숫자에 천 단위 쉼표로만 쓴다.

✅ 7,016만 3,000원
✅ 1억 3,316만 3,000원
✅ 70,163,000원
❌ 7,016만 3천원

이렇게 하면 본문 문자열에서 쉼표와 단위를 제거한 값이 표의 숫자와 그대로 비교된다. 대조가 문자열 연산으로 끝난다.

그리고 이 규칙을 문서가 아니라 린트 실패 메시지에 넣었다.

fatal.push(
  "공고형인데 임대조건 금액이 도표 이미지에만 있고 본문 텍스트엔 없다 — " +
  "이미지 속 숫자는 검색엔진·AI브리핑이 읽지 못한다. " +
  "표에 있는 실제 금액 1~2개를 본문 문장으로 그대로 써라" +
  "(예: '보증금 1,138만원에 월 14만 8,300원')"
);

메시지에 올바른 표기 예시가 들어 있어서, 걸렸을 때 무엇을 고쳐야 하는지 따로 찾을 필요가 없다. 이번에도 이 메시지를 보고 표기를 바꿔서 바로 통과했다.

정리

  • 한국어 금액은 만·억 뒤에 천·백·십이 다시 붙는다. 숫자만 훑는 패턴은 이 교대를 놓친다.
  • 패턴을 늘려 매치시킬 수는 있지만, 표기 변형이 늘면 값 대조가 불가능해진다.
  • 검사가 목적이라면 파서를 넣는 대신 표기를 하나로 고정하는 편이 싸다.
  • 그 규칙은 문서가 아니라 실패 메시지에 넣어야 다음에 잊지 않는다.

자주 묻는 질문

왜 '3천원'을 패턴에 추가하지 않았나요?

추가할 수는 있습니다. 문제는 그다음입니다. 매치가 되면 린트는 통과시키지만, 그 글에 적힌 '7,016만 3천원'이 원문 표의 70,163,000원과 같은 값인지는 정규식이 판정할 수 없습니다. 한글 수사를 허용하는 순간 표기 변형이 늘어나고, 원문 대조는 사람이 눈으로 해야 합니다. 표기를 하나로 고정하면 문자열 비교만으로 대조가 됩니다.

한국어 금액 파싱 라이브러리를 쓰면 되지 않나요?

값을 계산해야 한다면 그게 맞습니다. 다만 제 목적은 계산이 아니라 '이 문서에 금액이 문장으로 적혀 있는가'를 검사하는 것이었습니다. 검사 목적에는 파서가 과합니다. 그리고 파서를 넣어도 표기 변형을 허용한다는 사실 자체는 그대로라, 원문 대조 문제는 해결되지 않습니다.

왜 문서가 아니라 도구에 규칙을 넣었나요?

문서에 적은 작성 규칙은 다음번에 반드시 잊히기 때문입니다. 저는 같은 실수를 문서에 규칙이 있는 상태에서 반복한 적이 있습니다. 도구가 실패로 막으면 잊을 수가 없습니다. 실패 메시지에 올바른 표기 예시를 같이 넣어 두면 고치는 데 몇 초면 됩니다.

이 문제는 한국어에만 있나요?

숫자와 단위 수사가 섞여 표기되는 언어라면 비슷한 문제가 생깁니다. 영어도 '1.5 million'과 '1,500,000'이 같은 값이지만 다른 문자열입니다. 다만 한국어는 만·억이 4자리마다 끊기는 데다 그 뒤에 천·백이 다시 붙을 수 있어서, 한 금액을 쓰는 방법이 영어보다 더 많이 나옵니다.

#정규식#자바스크립트#한국어#파싱#린트