사내 문서를 RAG에 연결할 때 가장 먼저 확인할 것은 모델의 성능이 아니라 원문이 어디까지 이동하는가다. 문서에 주민등록번호, 전화번호, 이메일, 계좌번호가 포함돼 있다면 벡터 데이터베이스에 저장되는 순간과 검색 결과가 외부 LLM으로 전달되는 순간 모두 노출 지점이 된다.

GeekNews에 소개된 ko-pii는 이 구간에서 한국어 개인정보를 로컬로 탐지하고 가명화하는 Python 라이브러리다. 외부 API나 ML 모델 없이 규칙, 사전, 형식과 체크섬을 사용한다는 점은 폐쇄망이나 규제 산업에서 매력적이다. 다만 새 도구의 소개 문구만 보고 운영에 넣는 것은 위험하다. 실제 배포 버전을 설치해 보고, 무엇을 잘 찾고 무엇을 놓칠 수 있는지 먼저 구분했다.

실제 배포 버전으로 실행해봤다

2026년 8월 18일 기준 GitHub 기본 브랜치의 pyproject.toml과 변경 이력에는 1.15.4가 적혀 있지만, PyPI와 GitHub 최신 릴리스는 1.15.2였다. 따라서 ko-pii==1.15.4 설치는 실패했고, 실제 배포된 1.15.2를 별도 환경에서 실행했다.

uv run --isolated --with 'ko-pii==1.15.2' python -c \
"from ko_pii import Anonymizer, ProcessingMode; \
result = Anonymizer(mode=ProcessingMode.STRICT, strategy='tokenize').process(\
'신청인 홍길동, 연락처 010-1234-5678, 이메일 [email protected]'); \
print(result.text)"

확인한 출력은 다음과 같았다.

신청인 <PERSON_1>, 연락처 <PHONE_1>, 이메일 <EMAIL_1>

이 짧은 예제에서는 사람 이름, 전화번호, 이메일을 모두 구분했다. 설치 후 바로 실행되는 코어 패키지에 별도 런타임 의존성이 없다는 설명도 확인할 수 있었다. 하지만 예제 하나의 성공은 운영 정확도를 뜻하지 않는다. 버전 표기와 실제 배포본이 어긋난 상태도 도입 검토표에 반드시 남겨야 한다.

ko-pii가 잘 맞는 정보와 어려운 정보는 다르다

도구는 주민등록번호, 외국인등록번호, 사업자등록번호, 카드번호처럼 형식과 체크섬이 있는 식별자와 전화번호, 이메일, 주소, 인명 등을 포함한 33개 범주를 제공한다. 이 가운데 구조가 분명한 번호는 같은 규칙으로 반복 검증하기 쉽다.

반면 사람 이름과 비정형 주소는 문맥 의존성이 크다. 홍길동은 이름일 가능성이 높지만 제품명, 약품명, 기관명처럼 사람 이름과 비슷한 단어가 섞인 도메인에서는 오탐이 늘 수 있다. 서울특별시 강남구 테헤란로 152 같은 정형 주소와 강남 쪽에 산다 같은 표현도 난도가 다르다.

프로젝트가 공개한 측정값은 대화체 KDPII에서 F1 0.66, 행정·서식형 생성 평가셋에서 F1 0.79다. 이는 프로젝트 측 측정이며 데이터 분포와 라벨 정책에 따라 달라진다. README도 고객 문서 30~100건을 따로 라벨링해 확인하라고 안내한다. 전체 F1 하나보다 주민번호·전화번호처럼 절대 놓치면 안 되는 항목의 재현율과 PERSON·ADDRESS의 오탐률을 분리해서 보는 편이 안전하다.

마스킹과 가명화, 익명화는 같은 말이 아니다

ko-pii는 여러 치환 전략을 제공한다.

  • tokenize: <PERSON_1>처럼 바꾸고 Vault에 원문 대응표를 보관한다.
  • redact: [성명]처럼 범주명으로 바꾸며 원문 복원을 전제로 하지 않는다.
  • partial: 전화번호나 주민번호의 일부를 남기고 가린다.
  • hashed: 같은 값이 같은 토큰이 되도록 해시 기반 식별자를 만든다.

tokenize는 문서 안에서 같은 사람을 같은 토큰으로 유지할 수 있어 RAG 문맥 보존에 유리하다. 대신 Vault가 유출되면 원문을 복원할 수 있으므로 Vault를 벡터 데이터베이스나 애플리케이션 서버와 같은 위치에 두면 안 된다. 암호화, 접근 권한, 복원 감사 기록, 키 회전과 백업 정책이 함께 있어야 한다.

partial 역시 화면에서 읽기 편한 마스킹일 뿐 재식별 위험이 사라졌다는 뜻은 아니다. 여러 준식별자가 결합되면 개인을 다시 추정할 수 있다. 도구의 출력이 법적 익명성이나 개인정보보호법 준수를 자동으로 보장한다고 해석해서는 안 된다.

RAG에서는 인제스트와 검색 양쪽을 막아야 한다

문서를 처음 저장할 때 한 번 가명화하고 끝내면 충분하지 않다. 운영 중 새 문서가 추가되거나 기존 인덱스에 원문이 남아 있을 수 있고, 검색 결과를 조합하는 과정에서 다른 데이터가 섞일 수도 있다.

권장 흐름은 다음과 같다.

  1. 원문 저장 구역을 외부 LLM과 분리한다.
  2. 인제스트 전에 로컬 탐지와 가명화를 수행한다.
  3. 가명화된 텍스트만 벡터 데이터베이스에 넣는다.
  4. 검색 결과가 LLM으로 전달되기 직전에 다시 탐지한다.
  5. 차단된 항목과 복원 요청을 감사 로그로 남긴다.
  6. 원문과 Vault에는 별도의 최소 권한을 적용한다.

두 번째 탐지는 첫 번째 단계의 미탐뿐 아니라 이후 파이프라인에서 합쳐진 정보도 잡기 위한 방어선이다. 다만 같은 규칙을 두 번 실행한다고 새로운 종류의 미탐이 해결되는 것은 아니다. 실제 문서 표본으로 계속 오탐과 미탐을 검토해야 한다.

운영 전에는 라벨별 승격 기준을 만든다

바로 전체 문서를 변환하기보다 작은 비공개 파일럿이 적절하다.

  • 실제 문서 30~100건에서 개인정보 위치를 사람이 먼저 표시한다.
  • 주민번호, 계좌번호, 전화번호, PERSON, ADDRESS를 분리해 정밀도와 재현율을 계산한다.
  • HWP, PDF, DOCX처럼 실제로 사용하는 형식에서 표·머리말·줄바꿈이 보존되는지 확인한다.
  • 오탐 때문에 정상 문서 처리가 멈추는 경우와 미탐으로 원문이 나가는 경우의 비용을 따로 정한다.
  • 배포 버전을 고정하고 GitHub 기본 브랜치의 예제와 섞어 사용하지 않는다.
  • Vault 없이 복구할 수 있는 백업과 Vault를 포함해야 하는 백업을 구분한다.
  • 외부 전송 직전에는 별도의 차단 규칙과 사람 검토 경로를 둔다.

특히 현재처럼 저장소 버전과 패키지 저장소 버전이 다를 때는 pip install ko-pii만 문서에 적지 말고 검증한 버전을 명시해야 한다. 소스에서 직접 설치하면 배포되지 않은 변경까지 포함될 수 있으므로 운영 재현성이 더 낮아진다.

결론: 유용한 전처리 후보지만 마지막 안전장치는 아니다

ko-pii는 한국형 번호 체계와 문서 형식을 로컬에서 처리하려는 문제를 구체적으로 다룬다. 실제 PyPI 배포본 1.15.2로 이름, 전화번호, 이메일이 가명화되는 것도 확인했다. 외부 모델 호출 없이 시작할 수 있다는 점은 분명한 장점이다.

그러나 이름과 주소는 도메인에 따라 오탐과 미탐이 달라지고, 공개 벤치마크는 고객 환경의 보증서가 아니다. 버전 배포 상태도 아직 꼼꼼히 확인해야 한다. 따라서 이 도구는 ‘설치하면 개인정보 문제가 끝나는 제품’보다 실제 문서로 승격 여부를 판단할 수 있는 로컬 전처리 후보로 보는 것이 정확하다.