정규식 오탐 버그, 문구를 바꿔 우회했다가 재발한 이유 (부정 전방탐색으로 고친 실측 기록)

·

법률 콘텐츠를 검수하는 린터에 정규식 하나를 넣었다가, 정상적인 문장까지 막는 오탐이 났다. 문구를 바꿔 우회했는데 며칠 뒤 다른 글에서 똑같이 재발했고, 그제서야 근본 원인이 정규식 자체에 있다는 게 실측으로 드러났다.

잡으려던 것 — “100% 승인” 같은 결과 보장 표현

변호사가 아닌 사람이 법률 정보를 쓸 때, 변호사 광고 규정과 표시광고법상 위험한 표현이 있다. “100% 승인”, “무조건 면책” 같은 결과를 보장하는 문구다. 그래서 이런 정규식을 넣었다.

[/100\s*%|무조건|반드시\s*(면책|인가|승인)/, "결과 보장 표현"]

100\s*%는 “100%”·“100 %” 둘 다 잡는다. 이 채널에서 다루는 법률 콘텐츠 초안을 검수할 때, 이 패턴에 걸리면 발행 전에 치명 오류로 막힌다.

오탐 — “중위소득 100% 이하”도 똑같이 걸린다

이 채널은 법률 해설뿐 아니라 정부지원 주택 정책도 다룬다. 든든전세주택 같은 제도를 설명하려면 “중위소득 100% 이하”처럼 소득 기준선을 인용해야 하는데, 이것도 100\s*%에 그대로 걸렸다. “100% 승인”과 “100% 이하”는 완전히 다른 문장인데 정규식은 숫자와 기호만 보고 똑같이 판단한 것이다.

처음엔 문구를 바꿔 우회했다

처음 이 오탐을 만났을 때는 정규식을 고치는 대신 초안의 문구를 바꿔 통과시켰다. 당장 그 글은 문제없이 발행됐다.

그런데 며칠 뒤 청년월세지원 제도를 다루는 다른 글에서 똑같은 패턴이 다시 걸렸다. 소득 기준을 인용하는 표현 자체가 이 채널의 콘텐츠 성격상 계속 나올 수밖에 없었던 것이다. 이 재발을 실제로 확인하고 나서야, 문서 단위로 문구를 바꾸는 건 증상만 치우는 것이고 원인은 정규식에 그대로 남아 있다는 게 분명해졌다.

근본 수정 — 부정 전방탐색으로 예외 처리

두 번째로 걸렸을 때는 정규식 자체를 고쳤다.

// "100%"는 결과 보장("100% 승인")과 통계 기준선("중위소득 100% 이하") 둘 다에 쓰인다.
// 이 채널은 정부지원 소득 기준(중위소득 100%/150% 등)을 계속 다루므로, 뒤에
// 이하·이상·미만·초과·수준이 붙는 기준선 표현은 제외하고 나머지만 잡는다.
[/100\s*%(?!\s*(이하|이상|미만|초과|수준))|무조건|반드시\s*(면책|인가|승인)/, "결과 보장 표현"]

(?!\s*(이하|이상|미만|초과|수준))이 부정 전방탐색이다. “100%” 바로 뒤에(공백 허용) 저 다섯 단어 중 하나가 오면 매치하지 않는다. 그 결과 “중위소득 100% 이하”는 통과하고, “100% 승인”은 그대로 걸린다. 같은 숫자 하나를 지우거나 단어를 블랙리스트에 추가하는 방식이 아니라, 뒤에 오는 문맥으로 의미를 구분하는 방식이다.

왜 부정 전방탐색이 맞는 해법인가

이 버그의 핵심은 “100%“라는 토큰 자체가 나쁜 게 아니라는 점이다. 나쁜 건 “100%” 뒤에 결과를 단정하는 말이 오는 조합이다. 이걸 잡으려고 시도할 수 있는 방법은 여러 가지다.

  • 문서마다 표현을 바꾼다 — 이번에 실제로 해봤고, 재발로 실패가 확인됐다.
  • “100%“를 아예 규칙에서 뺀다 — 그러면 진짜 “100% 승인” 같은 표현을 놓친다.
  • 뒤따르는 단어로 예외 처리한다 — 부정 전방탐색이 이 역할을 한다.

세 번째 방법만 두 요구사항(정상 표현은 통과, 위험 표현은 차단)을 동시에 만족시킨다. 정규식으로 텍스트를 분류하는 규칙을 짤 때, 토큰 하나만 보고 판단하면 이런 오탐이 반드시 생긴다. 판단 기준을 토큰 앞뒤 문맥으로 넓히는 게 첫 번째로 시도할 방법이다.

정리

같은 정규식 오탐을 두 번 만났다. 첫 번째는 문서 문구를 바꿔 우회했고, 두 번째는 다른 문서에서 똑같이 재발하는 걸 실제로 확인한 뒤에야 정규식 자체를 고쳤다. 우회가 왜 답이 아닌지는 재발을 겪어봐야 확실히 알 수 있었다 — 재발하기 전까지는 “이번 한 번의 특이 케이스”로 보이기 쉽다.

자주 묻는 질문

왜 처음부터 부정 전방탐색으로 안 짰나요?

처음 정규식을 짤 때는 '100%'가 결과 보장 표현으로만 쓰일 거라고 가정했습니다. 실제로 이 채널이 정부지원 소득 기준(중위소득 100%·150% 등) 콘텐츠를 계속 다루게 되면서 같은 숫자 패턴이 정반대 문맥에서도 나온다는 게 실제로 확인됐고, 그제서야 예외 처리가 필요하다는 걸 알았습니다.

글 문구를 바꿔서 우회하는 게 왜 문제인가요?

그 문서 하나는 통과하지만, 같은 표현을 쓰는 다음 문서에서 똑같이 걸립니다. 실제로 이 사례에서도 한 글에서 문구를 바꿔 우회했다가, 며칠 뒤 다른 글에서 똑같은 오탐이 재발했습니다. 증상을 문서 단위로 고치는 건 원인을 규칙 자체에 남겨두는 것과 같습니다.

부정 전방탐색이 정확히 뭔가요?

정규식 문법 중 하나로, <code>(?!패턴)</code>은 '그 위치 뒤에 패턴이 오지 않는 경우에만 매치'라는 뜻입니다. 이번 경우 <code>100\s*%(?!\s*(이하|이상|미만|초과|수준))</code>은 '100%' 뒤에 저 단어들이 바로 오지 않을 때만 매치하므로, '100% 이하'는 걸러지고 '100% 승인'은 그대로 잡힙니다.

이 버그는 어떻게 발견했나요?

네이버 블로그용 법률 콘텐츠 린터(<code>naver-lint.mjs</code>)를 실제로 운영하면서 발견했습니다. 정부지원 주택 정책을 다루는 글에서 정규식이 치명 오류로 발행을 막았고, 원인을 확인해보니 소득 기준 표현이 결과 보장 표현과 같은 패턴으로 잡히고 있었습니다.

#정규식#regex#린터#부정전방탐색#버그