근거 문서에 없는 말을 원고에서 찾는다.
장문 문서를 새로 쓸 때 비슷한 앞 문서를 틀로 삼아 재활용하면, 앞 문서의 기관 이름과 기능 이름과 안 쓰기로 한 기술이 검토 없이 딸려 온다. 사람이 읽어서 잡으려면 문서 전체를 여러 번 훑어야 하고, 금지어 목록을 손으로 적어두는 방식은 목록에 없는 말을 통과시킨다.
이 도구는 목록 대신 근거 문서를 본다. 원고에서 후보 토큰을 뽑아 근거 문서에 한 번도 안 나오는 것만 남긴다.
목록을 손으로 적는 일을 없애려던 것인데 다 못 지켰다. 지킨 만큼만 적어둔다.
알려진 잔재 33종을 평범한 문장에 심어 재현율을 쟀다. 처음엔 17/33이었다. 소문자로 시작하는 토큰(mTLS), 한글에 영문이 붙은 토큰(소장품DB), 영문에 한글이 붙은 토큰(A-아르코), 슬래시로 잘린 한 글자 조각(G/W)까지 패턴을 넓히자 21/33이 됐고 오탐은 늘지 않았다.
남은 12종은 전부 순한글이다. 순회전시·소장품등록·도록발간처럼 영문 약어도 기관 접미사도 없는 명사구라 형태로는 구분할 방법이 없다. 조사 경계로 명사구를 통째로 잘라 전수로 뽑아봤더니 문서 두 개에서 후보가 2,634종 나왔다. 사람이 읽을 양이 아니라 그 방법은 접었다.
그래서 순한글은 목록으로 받는다. 자동 쪽은 목록에 없던 새 잔재를 잡고, 목록 쪽은 형태로 구분 안 되는 순한글을 잡는다. 둘이 서로 다른 것을 잡는다.
| 갈래 | 방식 | 몫 |
|---|---|---|
| 영문·혼합 토큰 · 기관명 | 자동 추출 + 근거 대조 | 21/33 |
| 순한글 명사구 | 목록 대조 | 12/33 |
파이썬 3.9 이상이면 되고 외부 패키지는 없다. 시험은 65개, python -m unittest discover tests로 돈다.
| 파이썬 | 3.9 이상 |
| 외부 패키지 | 없음 |
| 시험 | 65개 |
| 읽는 형식 | .hwpx와 마크다운 |
python samples/make_samples.py --out samples/generated
python -m src.cli \
--source samples/generated/spec.hwpx \
--source samples/generated/scope.hwpx \
--terms tests/fixtures/residue_terms.txt \
samples/generated/manuscript.md첫 줄이 합성 표본을 만든다. 지어낸 도서관 사업의 근거 문서 두 개와, 앞서 했다는 설정의 미술관 사업 어휘가 섞여 들어간 원고 하나다. 둘째 줄이 그 원고를 검사해 7건을 낸다 — 자동 5, 목록 2.
--source는 여러 번 줄 수 있고 .md와 .hwpx를 섞어도 된다. --terms는 순한글 목록 파일이고 생략하면 자동 추출만 돈다. --out 없이 돌리면 리포트가 화면에 그대로 찍힌다. 발화가 있으면 종료코드 1을 돌려준다.
근거에 없다는 것만으로 발화시키면 대부분이 오탐이다. 앞 문서의 이름을 새 이름으로 바꾸라고 적어둔 편집 메모, 안 쓰기로 정리한 기술, 근거 문서와 표기만 다른 낱말, 문서 작업 자체의 어휘 — 넷 다 잔재가 아니라 글을 쓰는 과정에서 남는 부산물이다.
| 필터 | 무엇을 보나 |
|---|---|
| 구역 판정 | 인용블록(>)·표(|)·체크박스·머리말·프론트매터 안의 언급은 편집 메모로 본다. 코드펜스 안은 산문이 아니라 코드로 본다 |
| 부정 서술 | 미적용·제외·없다·아니다 같은 말이 같은 줄에 있으면 넘어간다 |
| 표기 변형 | 대소문자를 맞춰 대조한다 |
| 작업 메타 낱말 | RFP·PPT·PDF·P1~P3·TODO 같은 고정 목록을 제외한다 |
구역 판정 하나가 제일 크게 듣는다. 실제 문서에서 이 필터만으로 후보가 걸린 줄이 39줄에서 6줄로 줄었다. 남은 6줄은 열어보니 전부 「우리 쪽 구성에 대응 항목이 없으니 다른 절로 흡수한다」 같은 작업 지시문이었다. 편집 동사와 화살표가 공통점이었지만 근거가 6줄뿐이라 여기서 규칙을 뽑지 않았다. 6줄에서 뽑은 규칙은 그 6줄에만 맞을 뿐이다.
구역 판정에 코드펜스라는 개념이 없었다. of() 가 인용·표·체크박스·머리말만 보고 나머지를 전부 「본문」이라 불렀으니, 펜스 안의 명령어와 변수 이름이 잔재 후보로 쏟아졌다.
내 공개 저장소의 마크다운 20편에 대고 재봤다.
| 본문 → 코드로 옮겨간 줄 | 231 |
| 그 줄들이 내던 후보 | 588 |
| 고친 뒤 남는 후보 | 1,315 |
| 줄어드는 비율 | 30.9% |
잡히던 것들이다 — python · unittest · linkaudit · bash · discover. ```bash 의 bash 까지 후보였다.
펜스는 줄 모양이 아니라 문서 위치다. 그래서 of() 가 아니라 classify() 가 본다 — 프론트매터를 다루던 자리와 같다. of() 의 계약은 안 바꿨다.
닫히지 않은 펜스는 문서 끝까지 코드로 본다. 본문으로 되돌리면 코드가 후보로 쏟아지고, 잃는 쪽이 낫다.
블록인용 안의 펜스도 센다. CommonMark 에서 블록인용은 컨테이너라 인용 안에서 연 펜스는 인용 안에서 닫힌다. 인용 깊이가 같은 울타리끼리만 짝을 짓는다 — 같은 함정을 wikilink-audit 이 같은 날 먼저 밟았고, 거기서는 그 결함이 볼트 문서에 「코드 블록을 콜아웃 안에 넣지 말 것」이라는 회피 규칙까지 적게 만들었다.
다섯째 필터가 있었다. 원고가 「해오름(HAEOREUM)」처럼 괄호로 병기하고 근거 문서가 앞말을 알면 뒷말도 산 것으로 봐주는 규칙이다.
이 규칙을 지키는 골든셋 시험을 네 번 고쳤다. 처음 시험은 병기 예시가 하나도 없어서 규칙을 통째로 지워도 통과했다. 병기 예시를 넣었더니 이번엔 규칙의 관문만 빼도 통과했는데, 시험이 쓰는 근거 문장에 앞말이 이미 있어서 관문 유무가 결과를 안 바꿨기 때문이다. 관문 시험을 넣었더니 이번엔 규칙을 넓히는 쪽이 안 잡혔다. 좁혀서 무력화하는 쪽만 붙들고 있었다.
네 번째에야 규칙을 제대로 붙들었고, 그다음 전체 검토에서 그 규칙 자체가 틀렸다는 것이 나왔다. 근거 대조가 부분문자열이라 「사업」·「보안」·「관리」 같은 흔한 말은 긴 문서 어딘가에 반드시 있고, 그래서 잔재를 「사업(MSA)」로 감싸기만 하면 관문이 통과하고 MSA가 조용해진다. 골든셋이 발화를 보증한 21종 중 다섯이 이 모양으로 사라졌다.
없애고 재봤다.
| 실제 오탐 | 감싼 잔재 5종 발화 | |
|---|---|---|
| 규칙 있음 | 0건 | 0종 |
| 규칙 없음 | 0건 | 5종 |
막아주는 오탐이 0건이었다. 병기를 실제로 쓴 한 곳은 표 안에 있어서 구역 필터가 이미 잡고 있었으니, 이미 풀린 문제를 위해 만들어놓고 조용한 누락 다섯 개를 대가로 내던 셈이다. 규칙째 들어냈다.
시험을 네 번 고치는 동안 매번 전부 초록불이었고, 그 네 번이 물은 것은 시험이 규칙을 제대로 붙드는가였지 그 규칙이 옳은가가 아니었다. 시험이 지키는 것과 지켜야 하는 것은 다른 문제다.
「근거 문서에 없다」는 잔재의 필요조건이지 충분조건이 아니다. 업계 표준 용어, 회사 실적, 방법론 이름처럼 근거 문서에 안 나와도 정당한 서술일 수 있다. 이 도구가 내놓는 건 읽어볼 자리지 판정이 아니다.
동형이의 약어는 못 가른다. LMS가 Long Message Service인지 학습관리시스템인지는 문맥을 봐야 아는데 여기는 문맥을 안 본다.
순한글 목록은 사람이 채워야 한다. 그 목록에 없는 순한글 잔재는 지나간다.
지금은 마크다운과 .hwpx만 읽는다. .pptx와 .pdf는 안 본다.
.hwpx 에는 코드펜스가 없으므로 위 펜스 판정은 마크다운에만 듣는다. 한글 문서에서 코드를 어떻게 표시하는지는 안 봤다.
이 저장소에 실제 사업 문서는 없다. samples/가 만드는 문서와 시험이 쓰는 문장은 전부 지어낸 것이다. 기관 이름·사업 이름·요구사항 번호도 지어냈다.
MIT(LICENSE).