1. 학생 명부와 공문서 개인정보를 외부 웹 정규식 테스터에 넣는 보안 위기

초·중·고 교무실이나 군사 행정실에서 수백 명의 학생 명부, 시험 성적표, 장병 신상명세서를 다루다 보면 주민등록번호, 전화번호, 이메일 주소를 일괄 추출하거나 마스킹(예: 950101-*******)해야 하는 일이 빈번히 발생합니다.

이때 많은 실무자가 인터넷 검색으로 찾은 온라인 정규식 테스트 사이트(Regex101 등)에 실제 학생들의 실명과 주민등록번호가 포함된 원본 텍스트를 그대로 복사·붙여넣기하는 심각한 개인정보보호법 위반 및 데이터 유출 사고를 범하고 있습니다. 외부 웹 서버로 전송된 민감 데이터는 서버 로그나 서드파티 CDN에 영구적으로 보존될 위험이 있습니다.

ZeliDesk의 ZeliRegex는 단 1바이트의 외부 통신도 없이 로컬 온디바이스에서 15대 한국형 공공 정규식 프리셋을 제공하고, 300ms 비동기 디바운싱으로 타이핑 프리징을 방어하며, 실시간 캡처 그룹 시각화와 1단계 가역 치환(Undo) 샌드박스를 구축하여 공교육과 공공기관의 프라이버시를 완벽히 수호합니다.

2. 대한민국 공공·행정 규격: 15대 표준 정규식 프리셋 카탈로그

복잡한 정규식 문법은 비전공 교사나 행정관에게 높은 진입장벽입니다. ZeliRegexWindow.xaml.cs는 한국 공공 서식에서 가장 많이 요구되는 15대 핵심 패턴을 정밀 사전 정의(Dictionary)하여 콤보박스 선택 한 번으로 즉각 적용합니다.

프리셋 명칭 정규식 패턴 (Pattern) 공공 행정 및 교육 현장 적용 목적
주민등록번호 (SSN) \d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12][0-9]|3[01])-[1-4]\d{6} 월·일 및 성별(1~4) 유효성 검증 기반 학생/교원 주민번호 추출 및 뒷자리 마스킹
010 휴대전화 01(?:0|1|[6-9])-(?:\d{3}|\d{4})-\d{4} 학부모 비상 연락망 및 교직원 휴대전화 서식 통일 및 추출
일반/지역 전화번호 (01[016789]|02|0[3-9][0-9])-?[0-9]{3,4}-?[0-9]{4} 전국 02(서울), 031~064(지역), 070 인터넷 전화 정규화
이메일 주소 [a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ 교육청 공직자 통합메일(korea.kr) 및 대외 도메인 일괄 수집
한글 음절/자모 [가-힣ㄱ-ㅎㅏ-ㅣ]+ 다국어 혼합 텍스트에서 순수 국문 단어 및 학생 이름만 선별
IPv4 주소 \b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}... 0~255 옥텟 범위 초과 방어 학내망 IP 주소 유효성 검증
날짜 (YYYY-MM-DD) \d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]) 학사 일정, 수료일, 출결 기록의 날짜 포맷 무결성 감사
C# — ZeliRegex/Views/ZeliRegexWindow.xaml.cs (15대 공공 표준 프리셋 딕셔너리)
private static readonly Dictionary<string, string> _presets = new()
{
    [Loc.Get("Regex_PresetCustom")]    = "",
    [Loc.Get("Regex_PresetSSN")]       = @"\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12][0-9]|3[01])-[1-4]\d{6}",
    [Loc.Get("Regex_PresetMobile010")] = @"01(?:0|1|[6-9])-(?:\d{3}|\d{4})-\d{4}",
    [Loc.Get("Regex_PresetPhoneKR")]   = @"(01[016789]|02|0[3-9][0-9])-?[0-9]{3,4}-?[0-9]{4}",
    [Loc.Get("Regex_PresetEmail")]     = @"[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+",
    [Loc.Get("Regex_PresetUrl")]       = @"https?://(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b[-a-zA-Z0-9()@:%_+.~#?&/=]*",
    [Loc.Get("Regex_PresetIPv4")]      = @"\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b",
    [Loc.Get("Regex_PresetKorean")]    = @"[가-힣ㄱ-ㅎㅏ-ㅣ]+",
    [Loc.Get("Regex_PresetDateYMD")]   = @"\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])",
    [Loc.Get("Regex_PresetPassword")]  = @"^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{}|;',./`~])[\S]{8,}$",
    [Loc.Get("Regex_PresetMAC")]       = @"([0-9A-Fa-f]{2}[:\-]){5}[0-9A-Fa-f]{2}",
    [Loc.Get("Regex_PresetGUID")]      = @"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}",
    [Loc.Get("Regex_PresetHexColor")]  = @"#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\b",
    [Loc.Get("Regex_PresetHtmlTag")]   = "</?([\\w][^>]*)>",
    [Loc.Get("Regex_PresetDigits")]    = @"\d+"
};

3. UI 프리징 없는 실시간 컴파일: 300ms DispatcherTimer 디바운스 엔진

사용자가 정규식 패턴(예: `[a-zA-Z0-9...`)을 입력하는 도중 키 입력마다 Regex.Matches가 동기적으로 실행되면, 수만 자의 텍스트가 로드된 상태에서 타이핑 지연(Latency)과 심각한 UI 멈춤 현상이 발생합니다. 심지어 미완성된 괄호나 이스케이프 문자로 인해 매 키마다 구문 에러가 발생합니다.

ZeliRegex는 300ms 윈도우 디바운서(Debouncer)를 적용하여, 사용자가 타이핑을 멈춘 뒤 0.3초 동안 추가 입력이 없을 때만 단 한 번 파싱 엔진을 구동합니다.

C# — ZeliRegex/Views/ZeliRegexWindow.xaml.cs (DispatcherTimer 기반 300ms 디바운서)
// 1) 300ms 디바운스 타이머 설정
_debounceTimer = new DispatcherTimer();
_debounceTimer.Interval = TimeSpan.FromMilliseconds(300);
_debounceTimer.Tick += DebounceTimer_Tick;

// 2) 텍스트 입력 발생 시 타이머 리셋
private void TxtInput_TextChanged(object sender, TextChangedEventArgs e)
{
    if (_debounceTimer == null || TxtStatus == null) return;

    // 입력마다 이전 타이머를 정지하고 재시작
    _debounceTimer.Stop();
    _debounceTimer.Start();
    TxtStatus.Text = Loc.Get("Regex_Typing"); // "입력 대기 중..." 표시
    TxtStatus.Foreground = new SolidColorBrush(Color.FromArgb(180, 255, 255, 255));
}

// 3) 사용자의 타이핑이 300ms 이상 멈추었을 때만 단 1회 원자적 평가 수행
private void DebounceTimer_Tick(object sender, object e)
{
    _debounceTimer.Stop();
    EvaluateRegex(); // 안전한 정규식 컴파일 및 매칭
}

4. 대용량 문서 렌더링 안전망: 1,000건 매칭 상한 및 서브 그룹 캡처 시각화

학교 학적부나 수천 줄의 로그 파일을 분석할 때, 정규식이 모든 공백이나 단어에 일치하면 수십만 개의 UI 리스트 아이템이 한꺼번에 생성되어 메모리 부족(OOM) 크래시가 발생할 수 있습니다. ZeliRegex는 1,000건 엄격 상한(Cap)을 두어 대용량 문서에서도 0ms 반응성을 보장합니다.

C# — ZeliRegex/Views/ZeliRegexWindow.xaml.cs (1,000건 상한 및 서브그룹 투영)
private void EvaluateRegex()
{
    Matches.Clear();
    string pattern = TxtPattern.Text;
    string source = TxtSource.Text;

    if (string.IsNullOrEmpty(pattern)) return;

    RegexOptions options = RegexOptions.Compiled;
    if (ChkIgnoreCase.IsChecked == true) options |= RegexOptions.IgnoreCase;
    if (ChkMultiline.IsChecked == true)  options |= RegexOptions.Multiline;

    try
    {
        var regex = new Regex(pattern, options);
        if (string.IsNullOrEmpty(source))
        {
            TxtStatus.Text = Loc.Get("Regex_SyntaxOK"); // 구문 정상 안내
            return;
        }

        var matches = regex.Matches(source);
        int count = 0;

        foreach (Match match in matches)
        {
            count++;
            var item = new RegexResultItem
            {
                Number = count,
                Text   = match.Value,
                Index  = match.Index,
                Length = match.Length
            };

            // ★ 서브 캡처 그룹 정보 ($1, $2 ...) 시각적 태깅
            if (match.Groups.Count > 1)
            {
                var groupInfo = new StringBuilder();
                for (int g = 1; g < match.Groups.Count; g++)
                {
                    if (match.Groups[g].Success)
                        groupInfo.Append($" [${g}=\"{match.Groups[g].Value}\"]");
                }
                if (groupInfo.Length > 0)
                    item.Text += $"  {groupInfo}";
            }

            Matches.Add(item);

            // ★ 1,000건 초과 시 조기 종료하여 UI 스레드 방어
            if (count >= 1000) break;
        }

        TxtStatus.Text = count >= 1000 
            ? Loc.Get("Regex_MatchOver1000") 
            : string.Format(Loc.Get("Regex_MatchCount"), count);
    }
    catch (ArgumentException ex)
    {
        // 미완성 정규식 문법 오류를 빨간색으로 안전하게 안내 (크래시 0초 차단)
        TxtStatus.Text = Loc.Get("Regex_SyntaxError") + ": " + ex.Message;
    }
}

5. 실수를 용납하는 데이터 정제: 1단계 가역 치환(Undo) 샌드박스

정규식 치환(Replace)은 강력하지만, 치환 문자열(예: `$1-*******`)을 잘못 입력하면 소중한 원본 데이터가 통째로 망가질 수 있습니다. ZeliRegex는 치환 직전의 원본 상태를 _lastSourceBeforeReplace 버퍼에 원자적으로 보관하여 언제든 단 한 번의 클릭으로 완벽히 이전 상태로 되돌리는 Undo 샌드박스를 제공합니다.

C# — ZeliRegex/Views/ZeliRegexWindow.xaml.cs (가역 치환 및 Undo 복원 메커니즘)
private string _lastSourceBeforeReplace;

// 1) 정규식 치환 실행
private void BtnReplace_Click(object sender, RoutedEventArgs e)
{
    string pattern = TxtPattern.Text;
    string source = TxtSource.Text;
    string replacement = TxtReplace.Text ?? "";

    if (string.IsNullOrEmpty(pattern) || string.IsNullOrEmpty(source)) return;

    try
    {
        var regex = new Regex(pattern, RegexOptions.Compiled);
        string result = regex.Replace(source, replacement);

        // ★ 치환 직전 원본 스냅샷 보존
        _lastSourceBeforeReplace = source;
        BtnUndoReplace.IsEnabled = true;

        TxtSource.Text = result; // 결과 반영
        TxtStatus.Text = "✅ " + Loc.Get("Regex_ReplaceComplete");
    }
    catch (Exception ex)
    {
        TxtStatus.Text = Loc.Get("Regex_ReplaceError") + ": " + ex.Message;
    }
}

// 2) 원클릭 되돌리기 (Undo)
private void BtnUndoReplace_Click(object sender, RoutedEventArgs e)
{
    if (_lastSourceBeforeReplace == null) return;

    TxtSource.Text = _lastSourceBeforeReplace; // 원본 복원
    _lastSourceBeforeReplace = null;
    BtnUndoReplace.IsEnabled = false;

    TxtStatus.Text = "↩️ " + Loc.Get("Regex_UndoComplete");
}

6. 종합 아키텍처 다이어그램

ZeliRegex의 300ms 디바운스 이벤트 흐름과 가역 치환 파이프라인 구조도입니다:

ZeliRegex 디바운스 파서 및 프라이버시 마스킹 토폴로지
┌────────────────────────────────────────────────────────────────────────────────────────┐
│                                ZeliRegex Engine Host                                   │
│                                                                                        │
│  [ 15 Public Presets ] ──► SSN (\d{6}-[1-4]\d{6}), Phone, IPv4, Korean Hangul          │
│                                      │                                                 │
│                                      ▼                                                 │
│  [ User Typing Event ] ──► 300ms DispatcherTimer (Debounce Reset)                      │
│                                      │                                                 │
│                                      ▼ (Idle for 300ms)                                │
│  [ EvaluateRegex() ]   ──► RegexOptions.Compiled + Safe Exception Sandbox              │
│                                      │                                                 │
│                      ┌───────────────┴───────────────┐                                 │
│                      ▼                               ▼                                 │
│         [ Match Collection ]                [ Replace & Undo Sandbox ]                 │
│         • Max 1,000 Matches Cap             • Snapshot: _lastSourceBeforeReplace       │
│         • Subgroup Capture [$1, $2]         • regex.Replace(source, "$1-*******")      │
│         • Text / Index / Length             • Reversible One-Click Undo Restore        │
│                      │                               │                                 │
│                      └───────────────┬───────────────┘                                 │
│                                      ▼                                                 │
│              [ Zero-Telemetry Local Privacy Masking Completed ]                        │
└────────────────────────────────────────────────────────────────────────────────────────┘

7. 결론: 개인정보 유출 0바이트, 신뢰받는 온디바이스 행정의 표준

개인정보 보호는 거창한 솔루션 도입 이전에, "업무 현장의 교사와 실무자가 일상적으로 데이터를 다루는 도구에서부터 클라우드 유출 위험을 원천 차단하는 것"에서 시작됩니다.

ZeliRegex는 인터넷 연결 없이도 대한민국 공공 표준에 최적화된 15대 정규식 프리셋과 부드러운 300ms 디바운싱, 그리고 실수를 즉시 되돌릴 수 있는 가역 치환 샌드박스를 통해 초·중·고 학내망과 공공기관 전산망이 믿고 사용할 수 있는 최고의 온디바이스 텍스트 정제 솔루션을 완성했습니다.