필요한 정보 모아보기
테크

개인정보(PII) 탐지 온프레미스 실측 — Jev·JevK5 로컬 판단 모델과 정규식 하이브리드

#개인정보#Jev#JevK5#로컬LLM#개인정보 마스킹

개인정보(PII) 탐지를 외부 API 없이 사내 장비에서 돌릴 수 있을까. 직접 해 봤다. 판단 전용 공개 모델 JevK5를 맥미니의 Docker에서 CPU만으로 돌려, 가짜 한국어 문장 183건을 판별시켰다. 정규식만 쓴 탐지기의 F1은 0.486, JevK5 4B 모델은 0.961이었다. 같은 문장을 TypeSafe의 호스팅 모델 Jev(jev-1.13.0)에도 보내 봤더니 0.989가 나왔다. 다만 모델을 정규식과 엮는 방식에 따라 결과가 꽤 갈렸고, 그 이야기가 이 글의 절반이다.

여기서 PII는 특정 개인을 알아볼 수 있는 정보를 말한다. 우리 개인정보 보호법은 범위를 넓게 잡는다. 제2조는 "해당 정보만으로는 특정 개인을 알아볼 수 없더라도 다른 정보와 쉽게 결합하여 알아볼 수 있는 정보"까지 개인정보로 본다. 전화번호 패턴만 찾는 걸로는 부족하다는 뜻이다.

정규식만으로는 왜 절반인가

정규식은 빠르고 설명하기 쉽다. 문제는 '모양'만 본다는 데 있다. 이번 실험에서 정규식(주민번호 체크섬·카드 Luhn 검증 포함)이 놓친 문장들은 이랬다. 모두 실험용으로 만든 가짜 문장이다.

  • "제 번호는 공일공 일이삼사 오육칠팔 이에요." — 한글로 읽은 번호
  • "김OO 대리 휴대폰은 끝자리 5678 입니다." — 부분 정보
  • "총무팀 박지현 대리는 공황장애로 3개월 병가 중입니다." — 이름과 건강정보(법상 민감정보)의 결합

반대로 엉뚱한 걸 잡기도 했다. "상품코드 010-4432-9910"은 휴대폰 번호 모양이고, "시리얼 넘버 KR4111-1111-1111-1111"은 Luhn 검사까지 통과한다. 회사 대표번호나 support@ 같은 공용 주소도 걸렸다.

이건 정규식을 잘못 짜서 생기는 문제가 아니다. 오픈소스 탐지 도구 Microsoft Presidio 문서도 같은 한계를 인정한다. 우편번호 같은 짧은 숫자 패턴에 대해 "Zip regex patterns (essentially 5 digits) are very weak, so we would want the initial confidence to be low, and increased with the existence of context words."(패턴 자체는 아주 약하니 처음엔 신뢰도를 낮게 두고, 주변 단어가 있으면 올려야 한다)라고 적었다. 결국 맥락을 읽는 단계가 하나 더 필요하다.

주민등록번호에는 함정이 하나 더 있다. 2020년 10월부터 뒷자리 6자리가 임의 번호로 바뀌었다. 행정안전부 발표는 "뒷자리는 성별 표시 첫 자리를 제외하고 6자리의 임의번호를 부여받게 된다"였다. 그 뒤 발급된 번호는 체크섬이 맞지 않을 수 있다. 실제로 이번 실험에서 체크섬이 틀린 주민번호 10건을 정규식이 전부 놓쳤다. 체크섬 검증은 이제 '거르는' 용도로 쓰면 안 된다.

Jev와 JevK5, 무엇이 다른가

Jev는 TypeSafe AI가 9월 15일 공개한 판단 전용 모델이다. 문장을 만들지 않고 "예/아니오" 같은 질문에 확률로 답한다(자세한 소개). JevK5는 TypeSafe와 무관한 개인 개발자가 9월 22일 공개한 오픈 가중치 대안이다. Qwen3.5-4B에 LoRA를 병합해 Jev와 같은 질문 형식에 답하도록 만들었다.

항목JevJevK5
제공 방식TypeSafe 호스팅 API가중치 공개, 직접 실행
라이선스비공개 모델Apache-2.0 (베이스 Qwen3.5 조건 별도)
데이터 위치TypeSafe 서버로 전송내 장비 밖으로 안 나감
언어"CJK scripts… not equally well"(공식 문서)모델 카드상 "English-only"
비용입력 100만 토큰당 0.042달러장비 비용만
실행 환경—CUDA, llama.cpp(GGUF, CPU·Mac)

독립 벤치마크 JevBench(운영자 Florian Standhartinger, README에 "not affiliated with or endorsed by TypeSafe AI"라고 명시) 9월 27일 판(v1.4.2.2)에서 Jev는 4위, JevK5는 5위였다. JevK5 README에는 아직 "2위"라는 문구가 남아 있는데, 9월 23일 판 기준이다.

두 가지는 짚고 넘어가자. JevK5 제작자는 9월 24일 변경 기록에서 일부 버전 학습 데이터에 MMLU-Pro 시험 문항 940개가 섞였다고 스스로 정정했다. 벤치마크 점수는 그만큼 조심해서 읽어야 한다. 또 출시 직후 'jev'가 들어간 도메인이 쏟아졌다. 보안 업체 Eye Security는 8일 동안 약 670개 도메인이 인증서를 받았다고 집계했다. 비교 사이트처럼 보여도 TypeSafe가 운영하지 않는 곳이 많다. 이 글의 출처는 원 저장소와 공식 문서만 썼다.

맥미니 Docker에서 직접 돌려봤다

실험 조건은 이렇다.

  • 장비: 맥미니(M4, 16GB)의 Docker VM(4 vCPU, 7.7GiB), CPU만 사용. 같은 VM에서 다른 서비스 컨테이너 24개가 함께 돌았다.
  • 런타임: llama.cpp 서버(빌드 11277)에 JevK5 GGUF 파일. 가중치는 해시를 대조해 받았다.
  • 데이터: 직접 만든 가짜 한국어 문장 183건(개인정보 포함 89건). 위에서 본 함정 사례를 일부러 섞었다.
  • 질문: 영어 noul 하나("특정 개인을 식별하거나 연락할 수 있는 정보가 있는가"), 기준 확률 0.5.

결과는 다음과 같다.

방식정밀도재현율F1건당 지연(p50)메모리
정규식만0.6670.3820.4860.03ms—
JevK5 2B (Q8)0.8450.9780.906953ms2.1GiB
JevK5 4B (Q4_K_M)0.9760.9330.9544,634ms3.1GiB
JevK5 4B (Q8)0.9560.9660.9612,443ms4.9GiB
Jev (호스팅 API)0.9781.0000.989211ms—

2B는 재현율은 높았지만 단어 자체에 반응했다. "주민등록번호는 법령 근거 없이 수집할 수 없다."라는 안내 문장에도 0.93을 줬다. 4B는 그런 실수가 거의 없었다. 4B가 놓친 건 "taeyang 골뱅이 example 점 com"(0.21) 같은 변형 이메일, 그리고 0.47~0.48로 기준선 바로 아래에 걸린 한글 번호 몇 건이었다. 기준 확률을 0.45로만 내려도 결과가 달라질 여지가 있다.

Jev는 같은 영어 질문으로 한국어 문장 89건의 개인정보를 하나도 놓치지 않았다. 틀린 건 2건뿐인데 둘 다 오탐이었다. "회의실 예약번호 010-2026-0930"(0.77)과 "테스트용 카드번호 4224-4720-…는 결제 샌드박스 전용"(0.62)이다. 공식 문서가 한중일 언어는 "not equally well"이라고 했지만, 이 데이터에선 JevK5보다 오히려 나았다. 표본이 183건이고 문장도 우리가 만든 것이라, 한국어 성능을 일반화하긴 이르다.

Jev 실행 조건도 적어 둔다. 10월 1일 같은 데이터(한국어 183건, 영어 30건)를 공식 파이썬 SDK(typesafe-sdk 0.7.2)로 한 번씩 보냈다. 요청 213건, 입력 토큰 기준 예상 비용은 약 0.004달러였다. 211ms라는 지연에는 외부 API까지 다녀오는 네트워크 왕복 시간이 들어 있으니, 로컬 CPU 수치와 맞대 비교할 숫자는 아니다.

속도는 예상보다 느렸다. README는 CPU에서 4B 0.6초라고 했는데, 이번 환경에선 2.4초가 걸렸다. 4스레드 VM에 다른 서비스까지 돌던 조건이니 그대로 비교할 순 없다. 흥미롭게도 더 작게 양자화한 Q4가 Q8보다 오히려 느렸다. 언어 차이도 봤다. 같은 문장 30건을 영어로 옮기자 4B Q8은 30건을 모두 맞혔다(F1 1.000). 모델 카드의 "English-only"가 빈말은 아니었다.

하이브리드는 '누가 결정하나'가 핵심이다

처음 짠 하이브리드는 상식적인 구조였다. 체크섬·Luhn 검증을 통과한 강한 근거는 정규식이 바로 개인정보로 확정하고, 애매한 것만 모델에 넘긴다. 결과는 F1 0.930. 모델 단독(0.961)보다 낮았다.

원인을 보니 정규식이 '확정'한 것들이었다. "주문번호 260915-1254927"은 우연히 주민번호 체크섬이 맞았고, "회의실 예약번호 010-2026-0930"은 휴대폰 패턴 그대로였다. 정작 모델은 이 문장들에 0.05~0.45를 주며 개인정보가 아니라고 봤다. 정규식이 확신한 곳에서 틀린 셈이다.

그래서 역할을 바꿔 봤다. 정규식은 '거르는' 쪽이 아니라 '골라내는' 쪽만 맡는다.

  1. 정규식과 단서 규칙(숫자 패턴, 한글 숫자, 이름+직함, '끝자리' 같은 표현, 건강 관련 단어 등)으로 후보를 찾는다.
  2. 단서가 하나도 없는 문장은 개인정보 아님으로 바로 통과시킨다.
  3. 단서가 있는 문장만 JevK5가 최종 판단한다.

같은 모델 출력으로 다시 계산하자 JevK5 4B는 F1이 0.972로 모델 단독보다 높아졌고, 모델 호출은 전체의 71%로 줄었다. Jev로도 같은 결과가 나왔다. 정규식이 확정하는 구조에선 0.957로 떨어졌고, 후보만 고르는 구조에선 모델 단독과 같은 0.989를 유지하면서 호출을 71%로 줄였다. 다만 이 단서 규칙은 데이터를 본 뒤에 다듬은 것이라 과적합 위험이 있다. 0.972는 "이 방향이 맞다"는 신호로만 읽어야 한다. 실제 운영 문서로 다시 재 봐야 하는 숫자다.

코드로 옮기면 뼈대는 이 정도다. JevK5 저장소의 llama.cpp 클라이언트(jevk5/gguf.py)를 그대로 썼다.

from jevk5.gguf import JevK5GGUF  # llama-server 에 붙는 공식 클라이언트(표준 라이브러리만 사용)

model = JevK5GGUF("http://127.0.0.1:8080", temperature=MODEL_CARD_TEMPERATURE)

def is_pii(text: str) -> bool:
    if not find_clues(text):          # 정규식·단서 규칙: 후보가 없으면 모델을 부르지 않는다
        return False
    out = model.decide(text, PII_QUESTION)   # {"type": "noul", "instructions": ..., "criteria": ...}
    return out["noul"] >= 0.5

find_clues가 '확정'하지 않고 '후보'만 돌려준다는 게 핵심이다. Jev 공식 문서의 'Pre-parsed value extraction' 쿡북도 같은 발상이다. 정규식으로 후보를 뽑고 판단은 모델에 맡긴다.

온프레미스로 붙이기 전에

데이터가 밖으로 나가지 않는다는 건 큰 장점이다. 개인정보보호위원회와 한국인터넷진흥원이 5월 15일 내놓은 2025년 유출 신고 분석(이데일리 보도)에 따르면 지난해 유출 신고는 447건으로 전년보다 45.6% 늘었다. 9월 11일부터는 고의·중과실로 인한 대규모 유출에 전체 매출액의 최대 10%까지 과징금을 매길 수 있다. 탐지 단계부터 외부 전송을 피하고 싶은 수요가 생길 수밖에 없다.

호스팅 Jev를 쓴다면 문서의 이 문장을 놓치지 말자. "Request and response bodies are not redacted."(요청·응답 본문은 가려지지 않는다) — 파이썬 SDK의 디버그 로그에 원문이 그대로 남는다는 뜻이다. 데이터 무보존(ZDR)도 엔터프라이즈 고객에게만 제공된다.

직접 돌릴 때 확인할 것도 정리해 둔다.

  1. 런타임 보안 — llama.cpp는 올해 악성 GGUF 파싱 취약점(CVE-2026-33298, CVE-2026-27940)이 고쳐졌다. 이번에 쓴 빌드(11277)는 수정 이후 버전이다. 원격 실행 위험이 큰 RPC 백엔드 취약점(CVE-2026-34159)도 있었으니 RPC 기능은 켜지 않는다.
  2. 가중치 형식 — JevK5는 전부 safetensors·GGUF로 배포된다. pickle 파일을 쓰는 모델이라면 받기 전에 다시 보자.
  3. 저장소 출처 — 공식 저장소는 allebee/jevk5다. 같은 이름의 포크가 이미 여럿 있다.
  4. 메모리 설정 — llama-server의 프롬프트 캐시 기본값(8GiB) 때문에 6GB VM에서는 2B조차 메모리 부족으로 죽었다. --cache-ram 0으로 끄자 해결됐다.
  5. 한국어 검증 — 영어 질문으로 한국어 문장을 판별해도 4B는 꽤 잘 맞혔다. 그래도 모델 카드가 영어 전용이라고 밝힌 이상, 자기 문서 샘플로 기준 확률부터 다시 잡는 게 순서다.

그럼 무엇을 고를까. 이번 수치만 보면 Jev가 정확도·속도 모두 앞섰다. 비용도 이 규모에선 1센트가 안 된다. 다만 문장 원문이 TypeSafe 서버로 간다. 실제 고객 데이터라면 그 자체가 개인정보 처리 위탁이 될 수 있는 일이고, 데이터 무보존(ZDR)은 엔터프라이즈 계약에서만 보장된다. 개인적으로는 외부로 보낼 수 없는 데이터는 JevK5를 사내에서 돌리고, 공개 문서나 이미 가린 데이터는 Jev로 처리하는 식의 분리가 현실적이라고 본다. 어느 쪽이든 정규식은 '후보를 고르는' 자리에 두는 게 이번 실험의 결론이다.

실험 데이터는 전부 합성(가짜)이며, 실제 개인정보는 쓰지 않았다(Jev API에도 합성 문장만 보냈다). JevK5 README(Apache-2.0), JevBench 문서(MIT), Presidio 문서(MIT)는 원문을 인용했고, TypeSafe 문서는 짧게 인용했다.

출처 github.com/…/jevk5github.com/…/CHANGELOG.mdhuggingface.cogithub.com/…/jevbenchdocs.typesafe.ai/modelsdocs.typesafe.ai/…/usage.mdgithub.com/…/06_context.mdresearch.eye.securitylaw.go.krkorea.kredaily.co.krpypi.org

내용이 틀렸나요? 정정 요청

관련 글