업무용 스크립트 요청문에 입력·출력·오류 원칙 담기
“자동화해 줘”를 실행 가능한 작업 설명으로 바꿉니다. 민감정보 없는 합성 샘플과 손으로 확인한 기대 결과를 붙여, 팀별 작업 기록 집계 스크립트를 요청하는 문장을 완성합니다.
AI 채팅 도우미에 텍스트를 보내기 전에 눈에 띄는 개인정보를 제거하거나 대체하고, 마스킹된 결과와 탐지 보고서를 함께 검토해 놓친 항목을 찾습니다. 이 글은 작은 합성 예제를 사용하며 원본 파일은 변경하지 않습니다.
이런 분께이름, 이메일, 전화번호가 포함될 수 있는 업무 텍스트를 AI 도우미에 보내기 전에 실용적인 사전 점검을 하려는 사람을 위한 글입니다.
이 예제는 전부 합성 데이터입니다. 이름, 이메일 주소, 전화번호, 회사 관련 정보, 메시지 내용은 모두 튜토리얼을 위해 만든 것입니다. 목적은 텍스트가 로컬 작업공간을 벗어나기 전에 눈에 띄는 개인정보를 제거하는 연습을 하는 것입니다.
다음 텍스트를 message.txt로 저장합니다. 이름 2개, 이메일 주소 2개, 전화번호 2개가 들어 있으며, 단순한 마스킹 규칙이 놓치도록 일부러 애매하게 만든 참조 1개도 포함되어 있습니다.
Project review notes
Alice Kim asked us to send the revised table to alice.kim@example.com.
If the file is delayed, call Alice at 010-1234-5678.
Bob Lee will review the second draft. His email is bob.lee@example.com and his office number is 02-345-6789.
The final approval should also be checked with A. Kim before Friday.
실제 민감한 문서를 보내지 않고도 AI 도우미에게 마스킹 규칙 설계 방법을 물어볼 수 있습니다. 탐지할 범주를 설명하고 실제 값 대신 가상의 예시를 사용합니다.
유용한 답변이라면 구조화된 식별자와 일반 자연어를 구분해야 합니다. 이메일 주소와 전화번호는 비교적 일정한 패턴을 가지는 경우가 많습니다. 반면 사람 이름은 더 어렵습니다. 일반 단어가 이름일 수도 있고, 이니셜이나 여러 문자 체계를 사용할 수도 있으며, 같은 사람이 여러 형태로 언급될 수 있습니다.
이 합성 예제에서는 이름에 대해 작은 승인 목록을 사용하고, 이메일 주소와 전화번호에는 정규식을 사용합니다. 이는 일반적인 개인정보 탐지기보다 의도적으로 범위를 좁힌 방식입니다.
| 데이터 유형 | 규칙 | 플레이스홀더 |
|---|---|---|
| 전체 이름 | 승인된 정확한 이름 Alice Kim과 Bob Lee만 교체 | [NAME_1], [NAME_2] |
| 이메일 | 로컬 부분, @, 도메인을 포함하는 일반적인 주소 형태 탐지 | [EMAIL_1], [EMAIL_2] |
| 전화번호 | 이 예제에 등장하는 두 가지 전화번호 형식 탐지 | [PHONE_1], [PHONE_2] |
| 기타 텍스트 | 수동 검토를 위해 그대로 유지 | 자동 교체 없음 |
일관된 플레이스홀더를 사용하면 원래 식별자를 남기지 않고도 반복되는 참조 관계를 이해할 수 있습니다. 아래 스크립트는 동일한 탐지 값이 반복되면 같은 플레이스홀더를 사용합니다.
다음 Python 스크립트는 message.txt를 읽고 승인한 규칙을 적용한 뒤 새 outputs 폴더에 결과를 저장합니다. 탐지된 원래 값과 플레이스홀더를 모두 담은 replacements.csv 보고서도 생성합니다. outputs가 이미 존재하면 중단하며 message.txt는 절대 덮어쓰지 않습니다.
import csv
import re
import sys
from pathlib import Path
INPUT = Path("message.txt")
OUTPUT_DIR = Path("outputs")
MASKED_FILE = OUTPUT_DIR / "message_masked.txt"
REPORT_FILE = OUTPUT_DIR / "replacements.csv"
KNOWN_NAMES = ["Alice Kim", "Bob Lee"]
EMAIL_RE = re.compile(
r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b"
)
PHONE_RE = re.compile(
r"(?<!\d)(?:010-\d{4}-\d{4}|02-\d{3}-\d{4})(?!\d)"
)
def make_replacer(kind, mapping, rows):
def replace(match):
original = match.group(0)
if original not in mapping:
placeholder = f"[{kind}_{len(mapping) + 1}]"
mapping[original] = placeholder
rows.append(
{
"type": kind,
"original": original,
"replacement": placeholder,
}
)
return mapping[original]
return replace
def mask_known_names(text, mapping, rows):
for name in KNOWN_NAMES:
if name not in text:
continue
if name not in mapping:
placeholder = f"[NAME_{len(mapping) + 1}]"
mapping[name] = placeholder
rows.append(
{
"type": "NAME",
"original": name,
"replacement": placeholder,
}
)
text = text.replace(name, mapping[name])
return text
def main():
if not INPUT.is_file():
sys.exit(f"Input file not found: {INPUT}")
if OUTPUT_DIR.exists():
sys.exit(f"Stop: {OUTPUT_DIR} already exists. Review or rename it first.")
original = INPUT.read_text(encoding="utf-8")
rows = []
name_map = {}
email_map = {}
phone_map = {}
masked = mask_known_names(original, name_map, rows)
masked = EMAIL_RE.sub(make_replacer("EMAIL", email_map, rows), masked)
masked = PHONE_RE.sub(make_replacer("PHONE", phone_map, rows), masked)
OUTPUT_DIR.mkdir()
MASKED_FILE.write_text(masked, encoding="utf-8")
with REPORT_FILE.open("x", encoding="utf-8", newline="") as target:
writer = csv.DictWriter(
target,
fieldnames=["type", "original", "replacement"],
)
writer.writeheader()
writer.writerows(rows)
print(f"Unique names masked: {len(name_map)}")
print(f"Unique emails masked: {len(email_map)}")
print(f"Unique phones masked: {len(phone_map)}")
print(f"Masked copy: {MASKED_FILE}")
print(f"Replacement report: {REPORT_FILE}")
if __name__ == "__main__":
main()
mask_personal_data.py를 message.txt와 같은 폴더에 둔 뒤 python mask_personal_data.py로 실행합니다. 이 예제에서 예상되는 요약은 고유 이름 2개, 고유 이메일 2개, 고유 전화번호 2개가 마스킹되는 것입니다.
결과를 신뢰하기 전에 예상 교체 내용을 먼저 직접 계산합니다. 이 스크립트는 고유하게 탐지된 값 6개를 기록해야 합니다.
| 유형 | 원래 값 | 교체 값 |
|---|---|---|
| NAME | Alice Kim | [NAME_1] |
| NAME | Bob Lee | [NAME_2] |
| alice.kim@example.com | [EMAIL_1] | |
| bob.lee@example.com | [EMAIL_2] | |
| PHONE | 010-1234-5678 | [PHONE_1] |
| PHONE | 02-345-6789 | [PHONE_2] |
Alice Kim은 원문에 두 번 등장하지만 두 경우 모두 [NAME_1]을 사용해야 합니다. 보고서에는 각 등장 횟수가 아니라 고유하게 탐지된 값만 기록됩니다.
자동 교체는 첫 번째 단계일 뿐입니다. 어디에 붙여넣기 전에 outputs/message_masked.txt 전체를 직접 읽어야 합니다. 이 예제에서는 마지막 문단에 A. Kim이 그대로 남습니다. 승인 목록만으로는 스크립트가 이것이 Alice Kim을 가리킬 수 있다는 사실을 알 수 없습니다.
실제 업무에서는 문서 성격, 적용 정책, AI 작업 목적에 따라 무엇을 제거해야 하는지 정의해야 합니다. 가능하다면 큰 문서를 전부 마스킹해 보내기보다 작업에 필요한 최소한의 텍스트만 제공하는 편이 더 안전합니다.
2026-09-21 · hand-checked example · Python 3.12
설명과 예제는 직접 작성했습니다. 관련 동작과 개념은 아래 공식 자료에서 확인할 수 있습니다.