AI 개인정보 마스킹, 형식까지 지정했더니 288건 중 124건이 남았습니다

「개인정보를 지워 주세요」라고 요청한 첫 이력서 출력은 얼핏 정리돼 보였습니다.

성명: [개인정보 삭제]
연락처: [개인정보 삭제]
전자우편: [개인정보 삭제]
주소: 서울특별시 … 1203호

네 줄 가운데 세 줄은 가렸습니다. 주소만 원문 그대로 남았습니다.

저는 AI 마스킹 결과를 어디까지 믿을지 정하려고, 이력서·회의록 같은 짧은 문서에 가상 개인정보를 넣어 검사했습니다.

짧은 답부터 말하면, 이번 조건에서는 그대로 믿기 어려웠습니다. 2026년 9월 로컬 gemma3:4b에 가상 개인정보 288건을 넣어 54회 처리했더니 124건이 원문 그대로 남았습니다. 열 건을 맡기면 네 건 넘게 남은 비율입니다.

첫 유출 — simple/gemma3:4b/D1-이력서/r1 · 잔존 1/5건 · 유형 주소

지시를 자세히 쓰면 더 안전해질까

같은 문서를 세 가지 방식으로 맡겼습니다.

지시 방식 모델에 준 요구
막연한 지시 개인정보를 지워 달라고만 요청
유형 열거 이름·주민등록번호·휴대폰·이메일·계좌·주소·카드를 명시
형식 지정 유형을 열거하고 지운 자리를 대괄호로 바꾸며 나머지는 그대로 두라고 요청

상식적으로는 세 번째가 가장 단단해 보입니다. 무엇을 찾을지 알려 주고, 어떻게 표시할지도 정했으니까요.

결과는 반대였습니다.

지시 방식 남은 개인정보 유출률 완전 클린 출력
유형 열거 30/96 31.3% 9/18
막연한 지시 37/96 38.5% 7/18
유형+형식 지정 57/96 59.4% 0/18

형식까지 지정한 조건에서는 완전히 가려진 출력이 한 번도 없었습니다.

그렇다고 「지시를 짧게 써야 안전하다」로 일반화할 수는 없습니다. 세 조건은 길이만 달랐던 게 아닙니다. 형식 지정 조건에는 대괄호 예시와 원문 보존 요구가 함께 들어갔습니다. 확실한 건 하나입니다. 자세한 프롬프트가 마스킹 완료를 보장하지 않았습니다.

회의록에서는 주민번호만 사라졌습니다

차이는 집계표보다 원출력에서 더 잘 보입니다.

회의록에는 참석자 이름 두 개, 주민등록번호, 휴대폰, 계좌번호를 넣었습니다. 질문은 짧았습니다.

이 문서에서 개인정보를 지워 주세요.
처리한 문서 전체를 출력해 주세요.

응답에서는 주민등록번호가 든 줄만 없어졌습니다. 나머지는 이런 모양으로 남았습니다.

참석: 총무팀 [이름 원문] 과장, 인사팀 [이름 원문] 대리
담당자 직통 [휴대폰 원문]으로 문의 접수
공용 계좌 [계좌번호 원문] 유지

다섯 건 중 네 건 잔존.

모델이 개인정보 처리 요청 자체를 거부한 것도 아니고, 문서를 그대로 복사한 것도 아닙니다. 주민등록번호는 찾아 지웠습니다. 일부를 처리했기 때문에 결과가 더 그럴듯해 보이는 실패였습니다.

첫 유출 — simple/gemma3:4b/D4-회의록/r1 · 잔존 4/5건 · 유형 계좌번호, 이름, 휴대폰

형식을 자세히 지정한 같은 회의록은 더 이상했습니다.

참석: 총무팀 [이름 원문] 과장, 인사팀 [이름 원문] 대리
입사 예정자 [주민등록번호] [주민번호 원문] 확인 완료
담당자 직통 [휴대폰 원문]으로 문의 접수

대괄호 표시는 붙였습니다. 값은 그대로 뒀습니다.

겉모양만 보면 마스킹 작업을 수행한 듯합니다. 하지만 [주민등록번호] 뒤에 실제 형식의 번호가 따라왔고, 이름도 대괄호 안에 원문을 넣었습니다. 표시를 바꾸는 일과 값을 제거하는 일은 같지 않았습니다.

가장 많이 남은 것은 주소였습니다

개인정보 종류별 결과는 차이가 컸습니다.

개인정보 유형 남은 건수 유출률
주소 22/36 61.1%
계좌번호 16/27 59.3%
이름 38/72 52.8%
카드번호 13/27 48.1%
휴대폰 27/63 42.9%
주민등록번호 5/27 18.5%
이메일 3/36 8.3%

주소가 가장 자주 남았고 이메일은 상대적으로 적게 남았습니다. 주민등록번호처럼 형태가 뚜렷한 값만 확인해서는 마스킹 품질을 판단하기 어렵다는 뜻입니다.

문서별 차이도 컸습니다. 배송 안내는 45건 중 7건이 남았지만 회의록은 45건 중 31건이 남았습니다. 동호회 명단도 72건 중 40건이 잔존했습니다. 한 문서에서 잘 됐다는 사실을 다른 문서의 안전성으로 옮길 수 없습니다.

특히 이름은 문맥에 섞였습니다. 성명처럼 라벨이 붙은 이름도 있었고, 참석자·고객·회원처럼 문장 안에 들어간 이름도 있었습니다. 계좌와 휴대폰 역시 번호 모양만으로 안정적으로 걸러지지 않았습니다.

지우면 안 되는 내용은 이번에는 보존됐습니다

마스킹 도구는 두 방향으로 실패할 수 있습니다.

하나는 개인정보를 남기는 것. 다른 하나는 날짜·금액·문서번호처럼 업무에 필요한 내용을 함께 지우는 것입니다.

이번 문서에는 계약번호, 주문번호, 날짜, 금액, 부서명 같은 보존 대상을 따로 심었습니다. 총 252건입니다. 이 항목은 252건 모두 남았고 소실은 0건이었습니다.

좋은 결과입니다. 다만 유출 문제를 상쇄하지는 않습니다. 문서가 온전히 보존됐더라도 개인정보가 남으면 외부 공유용 파일로 쓸 수 없습니다.

완전히 깨끗한 출력은 54회 중 16회뿐이었습니다. 나머지 38회에는 개인정보가 하나 이상 남았습니다. 출력이 자연스럽고 업무 정보가 잘 보존됐다는 이유만으로 통과시키면 놓치기 쉬운 구조입니다.

이번 수치가 나온 조건

실험 문서는 입사 지원서, 임대차 계약서, 배송 안내, 회의록, 고객 문의, 동호회 명단 여섯 종류입니다. 문서마다 이름·주민등록번호·휴대폰·이메일·계좌번호·주소·카드번호 가운데 해당하는 값을 심었습니다.

모든 문서와 이름, 번호는 자체 작성한 가상값입니다. 주민등록번호와 카드번호는 형식만 맞춘 무효값이고, 이메일은 예약 도메인 .example을 사용했습니다.

세 지시 방식과 여섯 문서를 조합해 각각 세 번 실행했습니다. 파일럿 6회를 포함한 총 생성은 54회입니다. 요청은 한 번에 하나씩 보냈고 응답 뒤 모델을 메모리에서 내리도록 설정했습니다.

문서는 짧았습니다. 문서당 230~272글자, 프롬프트에 실린 입력 토큰도 200~360 수준이었습니다. 회차당 응답 시간은 짧은 회차가 4.015초, 긴 회차가 4.917초 수준으로 4~5초 안에 끝났고, 생성 토큰은 짧게는 62개, 길게는 198개였습니다. 즉 이번 실험은 길거나 복잡한 문서가 아니라 짧고 단순한 문서에서도 놓친 겁니다. 채점은 원문 문자열이 응답에 남았는지를 프로그램으로 확인했습니다. 응답 거부, 무변경, 빈 응답, 인프라 오류는 모두 0회였습니다. 부분 문자열 잔존은 네 건이 별도로 잡혔습니다.

PII 마스킹 집계 — 잔존 124/288건 · 유출률 0.4306 · 보존소실 0 · infra=0

이번 결과는 gemma3:4b 한 모델과 짧은 자체 문서 6종에서 나왔습니다. 실제 조직 문서는 표, 첨부, OCR 오류, 여러 사람의 정보가 섞여 더 복잡할 수 있습니다.

프롬프트 뒤에 결정론 검사를 붙여야 합니다

이번 결과로 정할 수 있는 운영선은 분명합니다. AI 마스킹 출력은 초안으로 받고, 외부 공유 전에 별도 검사를 거쳐야 합니다.

제가 이 결과를 작업 절차로 옮긴다면 다음 순서로 끊겠습니다.

  1. 원본은 로컬에 둡니다. 실제 개인정보가 든 문서를 검증되지 않은 외부 서비스에 먼저 올리지 않습니다. 로컬에서 문서를 읽어 처리한 앞선 측정은 무료 로컬 RAG 실측에서 따로 확인했습니다.
  2. 찾아야 할 유형은 명시합니다. 이번에는 유형 열거 조건이 세 조건 중 누락이 가장 적었습니다. 다만 30/96이 남았으므로 이 단계만으로 끝내지 않습니다.
  3. 원문과 결과를 문자열·패턴으로 다시 대조합니다. 주민등록번호·휴대폰·이메일·카드·계좌처럼 형식이 있는 값은 정규식과 허용 규칙으로 검사합니다.
  4. 이름과 주소는 별도 목록으로 확인합니다. 형식 검사만으로 잡기 어려웠고 이번 실험에서도 잔존률이 높았습니다.
  5. 남은 후보가 0건이어도 사람이 문맥을 봅니다. 대괄호 안에 원문을 넣거나 일부 숫자만 바꾸는 출력은 단순한 마커 개수로 통과시킬 수 없습니다.
  6. 검사에서 걸리면 원문 공유를 중단합니다. 다시 프롬프트를 바꾸는 것보다 해당 값을 코드로 치환한 뒤 문서를 재검사하는 편이 결과를 확인하기 쉽습니다.

AI가 주민등록번호 한 줄을 지웠다는 사실은 마스킹 성공의 증거가 아니었습니다. 같은 응답에 이름과 휴대폰, 계좌가 그대로 남아 있었기 때문입니다.

마지막 판정은 프롬프트가 아니라 원문 대조 검사가 맡아야 합니다.

재현: cinevyze-bench의 run_pii_bench.py와 pii_bench_cases.json (문항 파일 포함). 본문의 원자료 경로(test_runs/…)는 비공개 작업 폴더라 외부에서 열 수 없습니다.


문서 6종의 마스킹 결과는 로컬 gemma3:4b로 실행해 원문과 기계로 대조한 것입니다. 측정 코드와 글 작성에 AI 도구를 활용했고, 조건 승인과 발행 전 검수는 제가 합니다.

cinevyze 운영자 — 로컬에서 gemma3:4b를 한 번에 하나씩 실행해 한국어 문서 6종의 개인정보 마스킹 결과를 검증했습니다. 측정 시점은 2026년 9월입니다. 운영자 소개

댓글

이 블로그의 인기 게시물

AI 프롬프트를 영어로 바꿔도 60쌍 중 우열이 갈린 건 9쌍뿐이었습니다

AI가 준 출처 링크 37개를 다시 열었더니, 판정 가능한 35개가 모두 깨졌습니다

AI 답변 재현성 실험: 온도 0에서 답은 고정됐고 오답도 반복됐습니다