한국어 PII 검출 + 가역 가명화 라이브러리 (ML 없이 룰 기반)
Project description
ko-pii
한국어 문서의 개인정보를 검출하고 가역적으로 가명화하는 Python 라이브러리. 외부 ML 의존성 없이 룰 + 사전 + 체크섬만으로 동작. 공공 문서에서 특히 강하며, 어떤 ML 파이프라인의 전처리 레이어로도 활용 가능.
from k_pii import Anonymizer, ProcessingMode
result = Anonymizer(mode=ProcessingMode.STRICT, strategy="tokenize").process(
"신청인 홍길동 (880101-1234568) 연락처 010-1234-5678"
)
print(result.text)
# 신청인 <PERSON_1> (<RRN_1>) 연락처 <PHONE_1>
print(result.vault.reveal("<RRN_1>")) # 880101-1234568 (권한자만 복원)
print(result.combined_risk.combined_risk) # RiskLevel.CRITICAL
가명화 전후 비교
원본:
신청인 홍길동 (880101-1234568) 연락처 010-1234-5678
주소: 서울특별시 강남구 테헤란로 152
tokenize (토큰 치환 + Vault 복원 가능):
신청인 <PERSON_1> (<RRN_1>) 연락처 <PHONE_1>
주소: <ADDRESS_1>
partial (일부만 가림 — 실무 양식):
신청인 홍** (880101-1******) 연락처 010-****-5678
주소: 서울특별시 강남구 ***
redact (카테고리명 치환):
신청인 [성명] ([주민등록번호]) 연락처 [전화번호]
주소: [주소]
처음이시면:
mode=ProcessingMode.STRICT+strategy="tokenize"추천. 가장 안전한 기본 설정 (MEDIUM 위험도 이상 차단 + Vault 복원 가능).
이런 것도 됩니다
- 조사 붙어있어도 잡힘 — "홍길동이" "홍길동에게" "홍길동의" → 조사 자동 분리 후 PERSON 검출
- 한자 병기 — "홍길동(洪吉童)" → 한글+한자 모두 인식
- 로마자 이름 — "Hong Gildong" → 한글로 정규화 후 매칭
- HWP/HWPX/DOCX/PDF 직접 입력 —
ko-pii report.hwp --strategy tokenize(표·머리말·꼬리말·메타데이터 모두 처리) - CSV/XLSX 헤더 자동 인식 — "성명/주민번호/연락처" 헤더 → 자동으로 PERSON/RRN/PHONE 매핑
- 공문서 날짜 자동 거부 — "시행일자: 2026-05-21" "감사기간: 3월~4월" → 생일 아님 (비-생일 키워드 30+)
- 가명 표기 자동 거부 — "박씨" "김모씨" "○○○ 시민" → 이미 가명화됨 (PII 아님)
- 결합 위험도 자동 평가 — 이름만으로는 PII 아닐 수 있지만, 이름 + 주민번호 + 주소 가 같이 나오면 → CRITICAL (「개인정보 비식별 조치 가이드라인」 의 준식별자 결합 검증)
- 감사 로그 — 누가·언제·어떤 토큰을 복원했는지 JSONL 추적 (개인정보보호법 제29조)
목차
- 주요 특징
- 가명화 전후 비교 + 이런 것도 됩니다
- 설치
- 사용 시나리오
- 평가 결과
- 사용법
- 32 PII 카테고리
- 검출 정책 — 어떤 접두어·anchor 가 작동하는가
- 처리 모드 + 치환 전략
- 부가 기능
- 데모 / 시각 자료
- FAQ
- 개발 + 문서
- 라이선스 + 법령
주요 특징
- 한국 특화 — 한국어 PII 32 카테고리 (RRN · FRN · 여권 · 사업자 · 카드 · 계좌 · 전화 · 이메일 · 주소 · 차량 · 인명 · 직책 등). 공공 문서에서 특히 강함
- 결정적 검출 — 룰 + 사전 + 체크섬. 주민등록번호·카드·사업자번호 등은 체크섬 검증으로 F1 ≈ 1.000
- 외부 의존성 없음 — Python 표준 라이브러리만 사용. 오프라인/폐쇄망 동작, GPU 불필요
- 전처리 레이어 —
DetectionResult(label/start/end/text/confidence) 표준 객체 출력. ML 파이프라인 앞단에 끼워넣기 편함 - 가역 가명화 + Vault — 토큰 ↔ 원본 매핑을 별도 저장소에 분리, 복원 가능
- 법적 근거 자동 부착 — 각 검출에 개인정보보호법 조항 자동 부착 (감사 추적)
- 다양한 입력 — TXT · CSV · XLSX · HWP · HWPX · DOCX · PDF (
[file]extras)
도메인별 활용 가이드
| 도메인 | 권장 설정 | 비고 |
|---|---|---|
| 공공 문서 (공문서·민원·인사) | STRICT + tokenize |
기본값. 가장 잘 맞는 도메인 |
| LLM 학습 데이터 전처리 | PARANOID + tokenize 또는 redact |
누수 차단 우선 |
| 의약품·바이오 | STRICT + exclude={"AGE","HEIGHT","WEIGHT"} |
"체중 1kg당" 같은 용법 오탐 방지 |
| 금융·보험 | STRICT + tokenize |
RRN·카드·계좌 결정적 검출 |
| 일반 사무 (사내 문서) | BALANCED + partial |
읽기 편한 부분 마스킹 |
# 의약품 도메인 — PERSON FP 방지 + 신체속성 오탐 방지
anon = Anonymizer(
mode=ProcessingMode.STRICT,
strategy="tokenize",
exclude={"AGE", "HEIGHT", "WEIGHT"}, # "체중 1kg당" 오탐 방지
)
# PERSON FP 가 많다면 — 도메인 사전 주입
# src/k_pii/dictionaries/common_words.py 에 의약품 성분명·제조사명 추가
# 예: "이부프로펜", "한미약품", "메트포르민" → PERSON 에서 자동 제외
설치
pip install ko-pii
extras 옵션 (필요 시):
pip install "ko-pii[file]" # HWP/HWPX/DOCX/PDF
pip install "ko-pii[security]" # Vault AES-256-GCM
Python 3.10 이상. 코어는 표준 라이브러리만 사용. PyPI publish 는 추후 예정.
사용 시나리오
시나리오 1 — 결재 공문 일괄 가명화 (외부 공개·LLM 전송 전)
from pathlib import Path
from k_pii import Anonymizer, ProcessingMode
anon = Anonymizer(mode=ProcessingMode.PARANOID, strategy="tokenize")
for path in Path("./공문서/").glob("*.hwp"):
result = anon.process(path.read_text(encoding="utf-8"))
Path(f"./가명화/{path.name}").write_text(result.text, encoding="utf-8")
# vault.json 분리 보관 (권한 있는 사용자만 복원 가능)
result.vault.save(f"./vault/{path.stem}.json")
- PARANOID 모드 — LOW 위험도 이상 모두 차단 (LLM/외부 전송 안전)
- 가명화 결과는 외부에, Vault 는 사내 저장소에 분리 보관
- HWP/HWPX 파서:
pip install "ko-pii[file]"
시나리오 2 — 민원 응대 시스템에서 사전 PII 검증
from k_pii import Anonymizer, ProcessingMode, RiskLevel
anon = Anonymizer(mode=ProcessingMode.AUDIT) # 차단 X, 검출만 보고
result = anon.process(incoming_petition_text)
# 결합 위험도가 CRITICAL 이면 담당자에게 알림
if result.combined_risk.combined_risk >= RiskLevel.CRITICAL:
notify_admin(
identifiers=result.combined_risk.identifiers, # {"RRN", "PHONE"}
quasi=result.combined_risk.quasi_identifiers, # {"PERSON", "ADDRESS"}
)
# 응대 직원에게는 가명화 버전 제공
masked = Anonymizer(mode=ProcessingMode.STRICT, strategy="partial").process(
incoming_petition_text
).text
- AUDIT 모드 — 차단 없이 검출만 보고 (감사·통계용)
- 결합 위험도 자동 평가 — 「개인정보 비식별 조치 가이드라인」 의 준식별자 결합 검증
- 응대 직원에게는
partial전략으로 일부만 마스킹 (880101-1******)
시나리오 3 — Python 로그에 PII 자동 가명화 (개발자용)
# 코드 어디서든 logger.info("...") 호출 시 자동 가명화
import logging
from k_pii import Anonymizer, ProcessingMode
_anon = Anonymizer(mode=ProcessingMode.STRICT, strategy="redact")
class PIIFilter(logging.Filter):
def filter(self, record):
record.msg = _anon.process(str(record.msg)).text
return True
logging.getLogger().addFilter(PIIFilter())
logging.info("신청인 홍길동 (880101-1234568) 처리 완료")
# → "신청인 [성명] ([주민등록번호]) 처리 완료"
평가 결과
정확도 (4 벤치마크 + 공정 비교)
| 평가 도메인 | 문서 수 | ko-pii | openai/PF | Presidio (default) | Presidio +KR |
|---|---|---|---|---|---|
| 행정문서 + PII 주입 (메인) | 200 | 0.885 | 0.539 | 0.134 | 0.455 |
| Opus 외부 시각 벤치마크 ★ | 187 | 0.770 | (pending) | (pending) | (pending) |
| KDPII (한국어 일상 대화 참고) | 4,891 | 0.656 | 0.382 | 0.302 | 0.367 |
| KLUE-NER PS (신문기사 풀네임 참고) | 5,000 | 0.419 | 0.155 | 0.000 | 0.000 |
| Naver x 창원대 NER PS (뉴스 기사 참고) | 90,000 | 0.308 | — | — | — |
메인 도메인 (행정문서) F1 = 0.885. 외부 인간 라벨 데이터 (KDPII / KLUE-NER / Naver 창원대) 에서도 측정 완료. 상세 분석: docs/EVALUATION_REPORT.md.
운영 전 권장: 사용하시는 도메인의 실제 문서 30~100건을 직접 라벨링해서 검증하세요. 도메인마다 성능 차이가 있습니다.
알려진 한계
- PERSON 오탐 (FP) — 룰 기반 PERSON 검출의 가장 큰 약점. 도메인 어휘 (의약품 성분명 등) 가 사람 이름으로 잡힐 수 있음. →
common_words.py에 도메인 사전 주입 또는exclude={"PERSON"}으로 끄기 - ADDRESS 비정형 — "강남 쪽에 살아" 같은 비정형 주소는 약함 (anchor 필요). 정형 주소 ("서울특별시 강남구 테헤란로 152") 는 OK
- 결정적 PII (RRN·PHONE·EMAIL·카드·사업자) 는 체크섬/형식 검증이라 오탐 거의 없음
상세 평가: docs/EVALUATION_REPORT.md.
사용법
CLI
# 기본
ko-pii input.txt --mode STRICT --strategy tokenize \
--vault vault.json -o output.txt --report report.html
# 배치 (디렉토리 일괄, 병렬)
ko-pii ./incoming/ --batch --workers 4 --output-dir ./anonymized/
# Vault 암호화 + 감사 로그
KPII_VAULT_PASSWORD=secret ko-pii doc.hwp \
--vault vault.kvault --audit-log audit.jsonl
Python API
from k_pii import Anonymizer, ProcessingMode
anon = Anonymizer(mode=ProcessingMode.STRICT, strategy="tokenize")
result = anon.process(text)
print(result.text) # 가명화된 텍스트
print(result.vault.reveal("<RRN_1>")) # 원본 복원 (권한자만)
print(result.summary["by_label"]) # {"RRN": 1, "PHONE": 1, "PERSON": 1}
결합 위험도 + k-익명성
# 검출 결과의 결합 위험도 자동 평가
print(result.combined_risk.combined_risk) # RiskLevel.CRITICAL
print(result.combined_risk.identifiers) # {"RRN"}
print(result.combined_risk.quasi_identifiers) # {"PERSON", "ADDRESS", "DT_BIRTH"}
# k-익명성 평가 (집단 데이터)
from k_pii.analytics import k_anonymity
report = k_anonymity(records, quasi_identifiers=["age", "city", "job"], k=5)
print(report.satisfies_k) # True/False
print(report.generalization_suggestions) # ["age: 30-39", ...]
CSV/XLSX 표 자동 처리
from k_pii.tabular import anonymize_records
import csv
rows = list(csv.DictReader(open("employees.csv")))
# 헤더 "성명/주민번호/연락처/주소" → 자동으로 PERSON/RRN/PHONE/ADDRESS 매핑
result = anonymize_records(rows, strategy="tokenize")
print(result.rows[0])
검토 큐 워크플로우 (오탐 학습)
confidence 낮은 검출 → 검토 큐에 저장 → 사용자가 FP/OK/FN 마킹 → 누적 마킹에서 사전 추천 패치 자동 생성 (자동 반영 X, 사람 검토 후 반영).
result = anon.process(text)
# 1. confidence 낮아 REVIEW 분류된 검출 (모드별 자동 분류)
for record in result.review_items():
d = record.detection
print(d.text, d.confidence, d.evidence)
# 2. 별도 JSONL 큐에 저장 → 사용자가 verdict 마킹
from k_pii.review.queue import ReviewQueue
q = ReviewQueue("review.jsonl")
q.enqueue_review_records(result.review_items(), document=text)
# 3. 누적 마킹 → 패치 파일 생성 (common_words 후보 / 이름 후보)
from k_pii.review.feedback import apply_feedback
apply_feedback(
queue_path="review.jsonl",
output_dir="feedback_patches/",
min_repeat=2, # 같은 토큰이 2회 이상 FP → 후보 (사전 오염 방지)
)
# → feedback_patches/common_words_additions.txt (PERSON FP 후보)
# → feedback_patches/names_to_add.txt (FN 표시 이름)
# → feedback_patches/summary.json
개별 검출기 호출
from k_pii.patterns.rrn import detect
for r in detect("신청인 880101-1234568"):
print(r.label, r.text, r.confidence, r.legal_basis)
# RRN 880101-1234568 1.0 개인정보보호법 제24조의2
32 PII 카테고리
결정적 검증 (체크섬·화이트리스트)
| 카테고리 | 검증 | 위험도 |
|---|---|---|
| RRN (주민등록번호) | 13자리 + 날짜 + 한국 체크섬 | CRITICAL |
| FRN (외국인등록번호) | gender 5-8 + 체크섬 | CRITICAL |
| 사업자등록번호 | 국세청 가중합 체크섬 | LOW |
| 법인등록번호 | 법인 체크섬 (RRN 우선) | MEDIUM |
| 운전면허번호 | 지방청 코드 11-28 화이트리스트 | HIGH |
| 여권번호 | prefix (M/S/PP/PD 등) + 8자리 | CRITICAL |
| 신용카드 | BIN 화이트리스트 + Luhn | CRITICAL |
| 필지고유번호 (PNU) | 19자리 + 시·도 코드 | LOW |
키워드 anchor (키워드 + 형식 모두 필요)
| 카테고리 | 키워드 |
|---|---|
| 건강보험증 | 건강보험 / 의료보험 / 보험증 |
| 처방번호 | 처방번호 / Rx / 교부번호 |
| 의약품 코드 | 약품코드 / KD코드 + 한국 GS1 |
| 팩스번호 | 팩스 / FAX |
| 계좌번호 | 계좌 / 은행명 60+ (3-way anchor) |
| 사번 | 사번 / 공무원번호 / 직원번호 / 임용번호 |
| 민원번호 | 민원 / 청구 / 정보공개 / 행정심판 |
| 사건번호 | 사건유형 (가합/고합/구합/헌가 등) |
형식 검증
| 카테고리 | 검증 |
|---|---|
| 전화번호 | 모바일 010-019 / 서울 02 / 지방 031-064 / VoIP 070 / 대표 15xx-18xx / +82 국제 |
| 이메일 | RFC 5322 |
| IP | IPv4 옥텟 + IPv6 RFC 4291 |
| URL | http(s) / ftp |
| 우편번호 | 시·도 첫자리 매핑 |
| 차량번호 | 신형 NN[가-힣]NNNN + 용도 한글 화이트리스트 |
| 공문서번호 | 부처명 + 형식 |
사전·휴리스틱
| 카테고리 | 사전 규모 |
|---|---|
| 인명 (PERSON) | 성씨 286 + 직책 인접 + 17 거부 룰 |
| 주소 (ADDRESS) | 광역 17 + 기초 226 + 빈출 동 150 + 국가 70 |
| 학력 (EDUCATION) | 대학 ~330 + 약칭 |
| 전공 (MAJOR) | 학과 ~400 (KEDI 분류) |
| 직책 (POSITION) | titles 250+ (정부·경찰·소방·군·검사·법관·민간) |
인적 속성 (준식별자 — 결합 시 식별 위험)
| 카테고리 | 검증 | 위험도 |
|---|---|---|
| 생년월일 | 날짜 + 키워드/풀네임/년생 marker | HIGH |
| 나이 | "32세 / 32살 / 환갑 / 12개월 아기 / 30대" | INFO |
| 신장 | "175cm / 1.75m" 50–250 | INFO |
| 체중 | "70kg / 70킬로" 1–300 | INFO |
준식별자 (Quasi-Identifier) 단독으로는 식별 불가지만 다른 정보와 결합 시 재식별 위험.
analytics/combined_risk가 자동 평가.
검출 정책 — 어떤 접두어·anchor 가 작동하는가
각 PII 검출은 단순 정규식 매칭이 아닌 multi-gate: 접두어 라벨 / 키워드 anchor / 문맥 사전 / 형식 검증 의 조합. 어떤 접두어가 어떤 카테고리를 트리거하는지 정리.
PERSON 필드 라벨 (50+ 항목)
라벨 직후 1~4글자 한글 → 강한 PERSON 후보. 소스: src/k_pii/dictionaries/field_labels.py.
| 도메인 | 라벨 |
|---|---|
| 기본 | 성명 이름 성함 이 름 |
| 민원·행정 | 신청인 신청자 민원인 청구인 보호자 대리인 당사자 |
| 결재 | 기안자 결재자 검토자 보고자 수신자 발신자 참조 |
| 회의 | 참석자 위임자 수임자 출장자 |
| 사법 | 원고 피고 고소인 피고소인 증인 감정인 |
| 경찰·소방 | 피의자 피해자 용의자 참고인 신고자 신고인 수사관 조사관 출동대장 출동자 구조자 |
| 인사 | 평가자 피평가자 면담자 피면담자 추천인 피추천인 |
| 의료 | 환자 |
라벨 prefix variant 7종 모두 인식: 성명: 홍길동 / 성명 : 홍길동 / [성명] 홍길동 / (성명) 홍길동 / <성명> 홍길동 / 성명 ── 홍길동 / 성명 홍길동 (2칸).
DT_BIRTH — 생일 anchor + 비-생일 거부 키워드
잡는 키워드 (25자 윈도우): 생년월일 생일 출생일 출생 탄생 태어난 DOB birthday + OO년생 marker + 풀네임 인접
거부 키워드 (15자 윈도우 — 공문서 일반 일자가 생일로 오탐 방지): 선고일자 심사일자 처리일자 회의일자 시행일자 공포일자 배포일자 발령일자 접수일자 회신일자 통보일자 처분일자 발급일자 유효기간 평가기간 계약기간 감사기간 출동일자 종결일자 작성일자 발생일시 발효일 결재일자 회계연도 기준일 외 30+
→ "시행일자: 2026년 5월 20일" 자동 거부, "생년월일: 1988년 1월 1일" 만 검출.
ADDRESS — 도로명·지번 + 대화체 anchor
- 도로명·지번: 광역·시·군·구 토큰 인접 시 anchor 없이도 검출
- 대화체 단독 (시·군·구·동 만): 25자 윈도우 내 anchor 필수 —
주소자택거주본적사세요사신다사셨살던살아살고산다이사이사하명함
ACCOUNT — 3-way anchor
셋 중 하나만 통과해도 검출:
- "계좌" 키워드 직전 —
계좌계좌번호계좌번 - 은행명 앞 —
국민신한우리하나농협기업MGBNK카뱅토스뱅크등 60+ - 은행명 뒤 —
110-4872-153649 신한
PERSON 거부 룰 (17+, FP 방지)
- 단성 + 직급/지역/학교/은행:
김부장김포시이화여대이수신→ 거부 - 한국어 어말 형태소 16종:
~은데~는데~라서~면서~까지~라고끝 → 거부 - 부처·기관명:
보건복지부행정안전부→ 거부 - 이미 가명화된 표기:
박씨김모씨김 모정 군이 양○○○ 시민→ 거부 - 명사 suffix:
~사건~수갈채~성스럽게→ 거부
상세: src/k_pii/patterns/person.py::_COMMON_KOREAN_ENDINGS + _NOUN_SUFFIXES_FORBIDDEN.
카테고리별 검출 예시
[PERSON]
✓ 성명: 김도윤 (필드 라벨)
✓ [성명] 박지훈 (대괄호 variant)
✓ <피해자> 이수정 (꺾쇠 variant)
✓ 박지훈 과장님께 (직책 인접)
✓ 기획재정부 김도윤 장관 (3중 매크로)
✓ 홍길동(洪吉童) (한자 병기)
✗ 김부장이 협조 안 함 (단성+호칭 = 거부)
✗ 보건복지부는 검토 후 (부처명)
✗ 서울에서 발표 (지역명)
[DT_BIRTH]
✓ 생년월일: 1988년 1월 1일 (생일 키워드)
✓ 1988년생 김민수 (년생 marker + 풀네임)
✗ 시행일자: 2026년 5월 20일 (비-생일 거부)
✗ 감사기간 2026년 3월 ~ 4월 (비-생일 거부)
[ADDRESS]
✓ 서울특별시 강남구 테헤란로 152 (도로명)
✓ 주소: 분당구 정자동 (라벨)
✓ 살던 곳이 마포구 합정동 (대화체 anchor)
✗ 강남구 영등포구 등 25개 자치구 (anchor 없음)
[EDUCATION]
✓ 서울대학교 졸업 (정식명)
✓ 고대 출신 (약칭 + anchor 필수)
✗ 양재초 (약칭 단독 = 거부)
[ACCOUNT]
✓ 계좌 110-4872-153649 (키워드 anchor)
✓ 국민은행 110-4872-153649 (은행명 앞)
✓ 110-4872-153649 신한 (은행명 뒤)
✗ 110-4872-153649 (anchor 없음 = 거부)
[결정적 검증]
✓ 880101-1234568 (RRN — 날짜 + 체크섬)
✓ 880101-1999999 (RRN 후-2020 — 체크섬 실패도 confidence 0.7 emit)
✓ 850315-5345676 (FRN — gender 5)
✓ 120-81-47521 (사업자 — 국세청 체크섬)
✓ 191211-0006637 (법인 — 한국전력)
✓ 4242-4242-4242-4242 (카드 — Visa BIN + Luhn)
✓ M12345678 (여권 — prefix M)
✗ 881301-1000004 (RRN — 13월 무효)
✗ 1234-1234-1234-1234 (카드 — BIN 첫자리 1 거부)
✗ A12345678 (여권 — A prefix 거부)
✗ 020-1234-5678 (PHONE — 020 미할당)
전체 정책: docs/annotation_policy.md · docs/pattern_analysis.md.
처리 모드 + 치환 전략
처리 모드 (차단 기준)
| 모드 | 차단 기준 | 용도 |
|---|---|---|
PARANOID |
LOW 이상 모두 차단 | 외부 공개·LLM 전송 전 |
STRICT |
MEDIUM 이상 차단 | 실무 표준 (기본값) |
BALANCED |
HIGH 이상 차단 | 내부 협업 |
PERMISSIVE |
CRITICAL 만 차단 | 분석가 작업 |
AUDIT |
차단 없음, 검출만 보고 | 감사·통계 |
치환 전략
| 전략 | 880101-1234568 → |
가역 | 설명 |
|---|---|---|---|
tokenize |
<RRN_1> |
✓ | 토큰 치환, Vault 에 원본 보관 |
redact |
[주민등록번호] |
✗ | 카테고리명으로 치환 |
asterisk |
************** |
✗ | 별표 마스킹 |
partial |
880101-1****** |
✗ | 일부만 가림 (실무 표준) |
hashed |
<RRN:abc123> |
✗ | 해시 (같은 값 → 같은 토큰) |
fpe |
771202-2345671 |
✗ | 형식 유지 암호화 (FPE) — 원본과 동일 형식 |
부가 기능
| 기능 | 설명 | 설치 |
|---|---|---|
| HWP/HWPX/DOCX/PDF 파서 | 한컴오피스·MS Word·PDF 자동 파싱 (본문 + 표 + 머리말 + 메타데이터) | [file] |
| Vault 암호화 | 토큰↔원본 매핑 별도 저장소. AES-256-GCM (정부·금융권 표준) + PBKDF2 480k 반복 | [security] |
| 감사 로그 (JSONL) | 모든 reveal() 호출 기록 — 누가·언제·어떤 토큰 복원 (개인정보보호법 제29조) |
코어 |
| 배치 처리 | 디렉토리 일괄 + 병렬 워커 | 코어 |
| 검토 큐 | confidence 낮은 검출 → 사람 검토 → 누적 오탐 어휘 자동 학습 | 코어 |
| HTML 리포트 | 정탐 초록 / 오탐 빨강 / 미탐 노랑 인터랙티브 | 코어 |
| 한자/로마자 변형 | 洪吉童 → 홍길동, Hong Gildong → 홍길동 자동 매칭 |
코어 |
데모 / 시각 자료
브라우저에서 열어보면 됩니다. (GitHub 에서는 raw 보기, clone 후에는 직접 열기)
| 데모 | 내용 | 파일 |
|---|---|---|
| HTML 검토 리포트 | 한 문서 검출 결과 — 정탐 초록·오탐 빨강·미탐 노랑 색상 코딩 + 카테고리별 막대 + 사용자 마킹 버튼 | docs/synthetic_sample.html |
| Before/After 가명화 샘플 | 6 시나리오 원본 ↔ 가명화 비교 + Vault 복원표 | docs/sample_redaction.html |
| KDPII 100 문서 시각 비교 | 일상 대화체에 ko-pii 검출 결과 — 정탐/오탐/미탐 색상 + 라벨 비교 | docs/kdpii_visual_compare.html |
| 종합 쇼케이스 | 전체 기능 데모 한 페이지 (검출 + 가명화 + 위험도 + 검토) | docs/showcase.html |
| 대형 데모 (15 시나리오) | 공문서·민원·인사·의료·SNS 등 15 도메인 447 검출 케이스 | docs/user_megademo.html |
리포트는 --report report.html 옵션으로 직접 생성 가능:
ko-pii input.txt --mode STRICT --strategy tokenize --report report.html
FAQ
Q1. ML 없이 룰만으로 정말 잘 되나요? 한국 공공 도메인 (행정문서 + PII 주입 200 문서) F1 0.885. 한국 핵심 PII (주민번호·여권·카드·사업자 등) 는 체크섬 검증으로 F1 ≈ 1.000 — 이건 ML 로 대체 불가능한 영역. PERSON 같은 맥락 의존적 PII 는 ML 이 더 나을 수 있지만, 공공 문서 (anchor 강함) 에서는 F1 0.795 로 충분히 실용적.
Q2. 오탐(FP) 가 너무 많이 나오면 어떻게 해야 하나요? 4가지 완화 메커니즘 (오탐 정책 섹션 참조):
- 모드 변경 —
STRICT→BALANCED또는PERMISSIVE로 차단 기준 낮춤 - 검토 큐 + 자동 학습 — 반복 FP 를
apply_feedback()로common_words사전에 추가 (사람 검토 후) - 카테고리 제외 —
Anonymizer(exclude={"PERSON"})로 특정 카테고리 끄기 - 거부 룰 직접 추가 —
src/k_pii/dictionaries/common_words.py에 어휘 추가 실측: 200 docs 메인 벤치마크에서 ADDRESS FP 3, ACCOUNT FP 0, DT_BIRTH FP 4 (PERSON 만 183).
Q3. Vault 파일을 잃어버리면 복원 못 하나요? 네. Vault = 토큰 → 원본 매핑이라 분실 시 복원 불가 (보안 설계상 의도). 대안:
[security]extras + 비밀번호 (KPII_VAULT_PASSWORD=...) 로 암호화 보관- 클라우드 KMS / 키 관리 시스템에 백업
- 가명화 전 원본 별도 백업 (감사 추적용)
복원 자체가 필요 없으면
strategy="redact"([주민등록번호]식으로 카테고리명 치환) 또는"hashed"(같은 값 → 같은 토큰) 사용.
Q4. HWP/HWPX 의 표·머리말·꼬리말 다 처리되나요?
네. [file] extras 설치 시 한컴오피스 파서가 본문 + 표 + 머리말 + 꼬리말 + 메타데이터 (작성자/수정자) 까지 모두 추출. 다만 출력은 텍스트만 — 원본 HWP 파일에 직접 박스 그리는 기능은 없음 (텍스트 추출 → 가명화 후 별도 파일로 저장).
Q5. 외국인 이름 / 한자 이름도 잡히나요?
- 한자 병기:
홍길동(洪吉童)→ 한글+한자 모두 인식 - 외국인 한글 표기:
스즈카,왕메이,응우옌 티 홍등 — 풀네임이면 검출 (성씨 사전 X 이지만 anchor + 길이로 후보 추출) - 로마자 이름:
Hong Gildong→ 한글로 정규화 후 매칭 정확도는 한국식 풀네임보다 낮음 (KDPII 외국 이름 카테고리 별도 평가 없음).
Q6. 가명화한 데이터로 통계 분석 가능한가요?
tokenize전략 — 같은 값은 같은 토큰 → 그룹화 가능 (예: 같은 RRN = 같은 사람)hashed전략 — 결정적 해시 → 그룹화 가능 (Vault 없이도)fpe전략 — 형식 유지 → DB 컬럼 타입 그대로 분석k_anonymity()함수로 집단 분석 적정성 사전 검증
Q7. 클라우드 환경 (AWS/Azure/GCP) 에서 쓸 수 있나요? 네. 코어는 표준 라이브러리만 사용해 어떤 Python 3.10+ 환경에서도 동작. 폐쇄망/온프레미스 한국 공공 환경도 OK (외부 API 호출 없음). Docker 이미지는 추후 제공 예정.
Q8. 다른 PII 검출 도구 (Presidio / openai/privacy-filter) 와 어떻게 다른가요?
- Presidio (Microsoft) — 영어 위주. 한국 특화 PII (RRN/FRN/여권 등) 부재
- openai/privacy-filter — 다국어 일반 PII. 한국 핵심 PII 14 카테고리 라벨 없음
- ko-pii — 한국 특화, 32 카테고리, 체크섬 검증, 법적 근거 자동 부착
개발 + 문서
개발 환경
git clone https://github.com/modak000/k-pii
cd k-pii
python -m venv .venv
source .venv/bin/activate # Linux/macOS
# .venv\Scripts\activate # Windows
pip install -e ".[dev]"
pytest # 699 passed
python -m k_pii.eval.benchmark -n 60 # 합성 회귀 감지
python -m k_pii.eval.kdpii kdpii.jsonl # KDPII 평가
문서 인덱스
| 문서 | 내용 |
|---|---|
EVALUATION_REPORT.md |
통합 평가 보고서 (정탐/오탐/미탐 + Tier + FP 정책 + 4 벤치마크 + 부트스트랩) |
eval_samples.md |
평가 데이터 5종 실제 샘플 한눈에 |
domain_fit_report.md |
도메인 적합도 (KDPII vs 공공 문서 실측) |
legal_mapping.md |
카테고리별 법조항 매핑 |
annotation_policy.md |
검출 정책 (왜 잡고 / 왜 거부) |
risk_levels.md |
위험도 + 모드별 차단 기준 |
pattern_analysis.md |
패턴별 정탐/오탐 분석 |
coverage.md |
카테고리별 검증 매트릭스 |
integration_openai_privacy_filter.md |
OpenAI 비교 평가 상세 |
integration_presidio.md |
Presidio 비교 평가 상세 |
integration_mcp.md |
MCP 서버 (Claude Desktop 연동) |
sample_redaction.md · .html |
가명화 샘플 Before/After |
kdpii_visual_compare.html |
KDPII 100 문서 정탐/오탐/미탐 시각 비교 |
라이선스
Apache License 2.0 — 자유 사용·상업 사용 가능. 한국 공공 부문·민간 기업 모두.
법령 참고
- 개인정보보호법 — 제2조 정의, 제23조 민감정보, 제24조 고유식별정보, 제24조의2 RRN, 제28조의2-5 가명정보, 제29조 안전조치의무
- 개인정보보호위원회 「가명정보 처리 가이드라인」 · 「개인정보 비식별 조치 가이드라인」
- 상법 제40조 (법인등록번호)
- 출입국관리법 제31조 (FRN)
- 국민건강보험법 제96조 (건강보험증)
- 금융실명거래 및 비밀보장에 관한 법률 (계좌)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ko_pii-1.1.0.tar.gz.
File metadata
- Download URL: ko_pii-1.1.0.tar.gz
- Upload date:
- Size: 380.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b86d05f27e2b627d1185296760eb7ad5be79a2859c558907f3ac3b90d472369c
|
|
| MD5 |
c1fa4ac5f78250474788959f2bd47320
|
|
| BLAKE2b-256 |
a4a701c5738787a07bd37aefa5f34f399cd9b50d937b20d145b9bbfec954f93e
|
Provenance
The following attestation bundles were made for ko_pii-1.1.0.tar.gz:
Publisher:
release.yml on modak000/ko-pii
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ko_pii-1.1.0.tar.gz -
Subject digest:
b86d05f27e2b627d1185296760eb7ad5be79a2859c558907f3ac3b90d472369c - Sigstore transparency entry: 1632109268
- Sigstore integration time:
-
Permalink:
modak000/ko-pii@a8ec0f63b18b87929ed0d913c87b934a3c5ab7f6 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/modak000
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a8ec0f63b18b87929ed0d913c87b934a3c5ab7f6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ko_pii-1.1.0-py3-none-any.whl.
File metadata
- Download URL: ko_pii-1.1.0-py3-none-any.whl
- Upload date:
- Size: 262.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5cde58c751245683f89254d6b023850049eeaf2661b028ea5a92a1656ee82e00
|
|
| MD5 |
bb00035b016cc47de571d2abc1fefd84
|
|
| BLAKE2b-256 |
be19b18efc14271c73c3c775f23a31c8f95d6038968da496bc3fc8ff162c8f44
|
Provenance
The following attestation bundles were made for ko_pii-1.1.0-py3-none-any.whl:
Publisher:
release.yml on modak000/ko-pii
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ko_pii-1.1.0-py3-none-any.whl -
Subject digest:
5cde58c751245683f89254d6b023850049eeaf2661b028ea5a92a1656ee82e00 - Sigstore transparency entry: 1632109275
- Sigstore integration time:
-
Permalink:
modak000/ko-pii@a8ec0f63b18b87929ed0d913c87b934a3c5ab7f6 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/modak000
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a8ec0f63b18b87929ed0d913c87b934a3c5ab7f6 -
Trigger Event:
push
-
Statement type: