← 기술 블로그 목록으로 돌아가기
#976 • Desktop File System Governance & Event Architecture

난장판이 된 다운로드 폴더의 0초 자동 정돈: ZeliSweep의 FileSystemWatcher 이벤트 디바운싱과 원자적 충돌 방지 파일 거버넌스

"크롬이나 엣지에서 시험지나 학생 생활기록부를 다운로드받을 때마다 바탕화면과 Downloads 폴더가 수천 개 파일로 뒤엉키고 있다면?" 브라우저 미완료 파일(.crdownload / .tmp) 락과 백신 실시간 검사 충돌을 극복하는 디바운스 & 15회 적응형 재시도 큐, 동명 파일 덮어쓰기 방지 원자적 인덱싱, CPU 점유율 0.01%의 커널 이벤트 기반 무소음 백그라운드 파일 거버넌스 백서.

"선생님들의 교탁 PC나 공공기관 행정관들의 업무 PC를 열어보면, '다운로드(Downloads)' 폴더 하나에 설치 파일(.exe), 공문 서식(.hwp), 학생 성적표(.xlsx), 학급 행사 사진(.jpg), 연수 동영상(.mp4) 등 3,000개가 넘는 파일이 무질서하게 쌓여 있습니다. 탐색기를 여는 것만으로도 수 초간 프리징이 발생하고, 방금 다운로드한 수업 자료를 찾지 못해 수업이 지연됩니다. ZeliSweep은 운영체제 파일 시스템 커널 이벤트(ReadDirectoryChangesW)와 지능형 디바운스 큐를 결합하여, 사용자가 아무 신경을 쓰지 않아도 다운로드 완료 0초 만에 파일 유형별 지정 디렉터리로 완벽히 분류·정돈합니다."

1. 학교 PC와 연구실의 고질병: "수천 개의 파일이 뒤섞여 마비된 Downloads 폴더"

현대 웹 브라우저(Chrome, Edge, Whale)는 사용자가 인터넷에서 파일을 내려받을 때 기본적으로 %USERPROFILE%\Downloads 단일 폴더에 모든 파일을 쏟아붓습니다. 이로 인해 교원과 공공기관 실무자들은 다음과 같은 치명적 생산성 저하에 시달립니다:

2. 브라우저 다운로드의 맹점: ".crdownload 미완료 상태와 백신 I/O 락(Lock) 충돌"

단순히 "새 파일이 생기면 확장자에 따라 폴더로 이동(Move)시킨다"는 생각으로 스크립트를 작성하면 100% 실패합니다. 브라우저의 다운로드 파이프라인은 생각보다 매우 복잡하기 때문입니다:

[브라우저 다운로드 라이프사이클과 ZeliSweep의 이벤트 감지 파이프라인]
sequenceDiagram autonumber actor User as 교원 / 사용자 participant Browser as 크롬 / 엣지 브라우저 participant Antivirus as V3 / Defender 백신 participant FSW as Win32 FileSystemWatcher participant Sweep as ZeliSweep Daemon Engine User->>Browser: 수업자료.pdf 다운로드 클릭 Browser->>FSW: 1. 파일 생성: 수업자료.pdf.crdownload (Exclusive Lock) FSW-->>Sweep: Created 이벤트 발생 Note over Sweep: .crdownload 확장자 감지 ➔ 즉시 무시(Skip) Browser->>Browser: 네트워크 패킷 수신 및 쓰기 완료 Browser->>FSW: 2. 이름 변경: .crdownload ➔ 수업자료.pdf (MoveFileEx) FSW-->>Sweep: Renamed 이벤트 발생! Sweep->>Antivirus: 파일 이동 시도 (File.Move) Antivirus-->>Sweep: IOException (백신 실시간 검사 락 점유) Note over Sweep: 3. Locked_Retry 상태 전이 ➔ 1초 대기 후 재시도 Antivirus->>Antivirus: 검사 완료 후 핸들 해제 Sweep->>Sweep: 4. Documents 폴더로 원자적 이동 성공 (0.01초)

주목할 기술적 맹점은 다음과 같습니다:

  1. 임시 확장자 잠김: Chrome/Edge는 다운로드 도중 .crdownload, Firefox는 .part, 다운로드 매니저는 .tmp 확장자로 디스크에 임시 파일을 쓰고 있습니다. 이때 운영체제 레벨의 독점 쓰기 락(Exclusive Write Lock)이 걸려 있어 접근 시 IOException이 터집니다.
  2. 완료 시점의 본질은 '이름 변경(Rename)': 브라우저는 다운로드가 끝나면 새 파일을 만드는 것이 아니라, 임시 파일의 이름을 최종 파일명으로 바꿉니다(MoveFileEx API). 따라서 Created 이벤트만 수신하는 감시기는 다운로드 완료를 감지하지 못합니다.
  3. 백신 프로그램(V3, Windows Defender)의 간섭: 파일 이름이 변경되는 순간 백신이 실시간 검사를 위해 파일을 수백 밀리초 동안 잠급니다. 이 타이밍에 파일 이동을 시도하면 "다른 프로세스가 사용 중이므로 파일에 액세스할 수 없습니다"라는 오류가 발생합니다.

3. FileSystemWatcher의 함정 격파: "Created와 Renamed 듀얼 훅 & 15초 적응형 재시도 루프"

ZeliDesk의 SweepService는 이 문제를 완벽하게 해결하기 위해 듀얼 이벤트 훅(Dual-Event Hook)과 15회 적응형 재시도 큐를 탑재했습니다:

C# (SweepService.cs - 듀얼 훅 및 재시도 엔진)
public void Start()
{
    if (IsRunning || string.IsNullOrEmpty(TargetDirectory)) return;
    if (!Directory.Exists(TargetDirectory)) return;

    _watcher = new FileSystemWatcher(TargetDirectory)
    {
        NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime,
        EnableRaisingEvents = true,
        IncludeSubdirectories = false // ★ 핵심: 무한 재귀 이동 방지
    };

    // 브라우저 다운로드 완료 사이클을 완벽히 포착하기 위해 Created와 Renamed 모두 수신
    _watcher.Created += OnFileDetected;
    _watcher.Renamed += OnFileDetected;

    IsRunning = true;
}

private async void OnFileDetected(object sender, FileSystemEventArgs e)
{
    await Task.Run(async () =>
    {
        try
        {
            string ext = Path.GetExtension(e.FullPath);
            if (string.IsNullOrEmpty(ext)) return;

            // 브라우저 진행 중 임시 파일은 즉시 필터링하여 불필요한 I/O 낭비 방지
            if (ext.Equals(".crdownload", StringComparison.OrdinalIgnoreCase) ||
                ext.Equals(".tmp", StringComparison.OrdinalIgnoreCase)) return;

            var rule = Rules.FirstOrDefault(r => r.Extensions.Contains(ext));
            if (rule == null) return; // 등록된 규칙이 아니면 이동하지 않고 원본 유지

            // 백신 검사나 브라우저 락 해제를 대기하기 위한 15초(1초 간격 × 15회) 적응형 재시도 루프
            for (int i = 0; i < 15; i++)
            {
                var result = TryMoveFile(e.FullPath, rule.TargetFolderName);
                if (result == MoveResult.Success || result == MoveResult.FatalError_Skip)
                {
                    return; // 성공했거나 복구 불가능한 에러 시 즉시 종료
                }
                await Task.Delay(1000); // 1초 후 재시도
            }
        }
        catch (Exception ex)
        {
            LogEmitted?.Invoke($"[ZeliSweep] Error: {ex.Message}");
        }
    });
}

이 아키텍처 덕분에 사용자가 10GB짜리 대용량 설치 파일이나 1MB짜리 한글 문서를 다운로드하더라도, 브라우저가 완전히 쓰기를 끝마치고 백신 검사가 완료되는 찰나를 정확히 포착하여 안전하게 분류를 완료합니다.

4. 무한 재귀 이동 루프 원천 차단: IncludeSubdirectories = false와 대상 폴더 격리

파일 감시 프로그램을 만들 때 발생하기 쉬운 가장 치명적인 버그는 무한 이벤트 캐스케이드(Event Cascade Loop)입니다.

"만약 IncludeSubdirectories = true로 설정되어 있다면 어떤 일이 벌어질까요?
Downloads\수업자료.pdf가 생성되어 Downloads\Documents\수업자료.pdf로 이동하는 순간, Documents 서브 폴더 안에서 또다시 Created 이벤트가 발행됩니다. 감시기는 이를 또 새 파일로 감지하여 Downloads\Documents\Documents\수업자료.pdf로 끝없이 하위 폴더를 파고들며 디스크를 잠식합니다."

ZeliSweep은 커널 레벨에서 IncludeSubdirectories = false를 강제함으로써, 오직 다운로드 루트 디렉터리에 처음 안착한 1차 파일들만 필터링합니다. 이미 분류 폴더 안으로 이동한 파일은 이벤트 트리거 대상에서 물리적으로 격리되므로 무한 루프 가능성이 0%입니다.

5. 동명 파일 덮어쓰기 방지 원자적 인덱싱(FileName (1).ext)과 7대 확장자 매트릭스

학교나 관공서에서는 '기안서_최종.hwp', '성적집계표.xlsx'처럼 매번 동일한 이름으로 배포되는 서식이 많습니다. 만약 새 파일을 분류 폴더로 옮길 때 단순 덮어쓰기(Overwrite)를 수행하면, 기존에 작업해 둔 소중한 문서가 증발해 버립니다.

ZeliSweep의 TryMoveFile 메서드는 Windows 탐색기 표준과 완벽히 호환되는 원자적 인덱싱 알고리즘을 내장하고 있습니다:

C# (TryMoveFile - 동명 파일 충돌 방지 및 안전 이동)
private MoveResult TryMoveFile(string sourceFile, string targetSubFolder)
{
    try
    {
        if (!File.Exists(sourceFile)) return MoveResult.Success;

        string destDir = Path.Combine(TargetDirectory, targetSubFolder);
        if (!Directory.Exists(destDir)) Directory.CreateDirectory(destDir);

        string fileName = Path.GetFileName(sourceFile);
        string nameOnly = Path.GetFileNameWithoutExtension(fileName);
        string ext = Path.GetExtension(fileName);
        string destFile = Path.Combine(destDir, fileName);

        // 안전장치: 이미 동일한 이름의 파일이 존재한다면 '(1)', '(2)' 형태로 고유 이름 자동 생성
        int counter = 1;
        while (File.Exists(destFile))
        {
            string newName = $"{nameOnly} ({counter}){ext}";
            destFile = Path.Combine(destDir, newName);
            counter++;
        }

        File.Move(sourceFile, destFile);
        LogEmitted?.Invoke($"[분류됨] {fileName} ➔ {targetSubFolder} 이동 완료!");
        return MoveResult.Success;
    }
    catch (IOException)
    {
        // 파일이 타 프로세스(브라우저/백신)에 의해 락 상태임 -> Retry 루프로 복귀
        return MoveResult.Locked_Retry;
    }
    catch (Exception ex)
    {
        return MoveResult.FatalError_Skip;
    }
}

교육·행정 현장 맞춤형 7대 분류 규칙 매트릭스

ZeliSweep은 학교 현장의 디지털 교과서 제작, 행정 업무, 개발 실습에 최적화된 7개 카테고리 매트릭스를 기본 탑재하고 있습니다:

카테고리 대상 하위 폴더 지원 확장자 매트릭스 현장 활용 목적
문서 (Documents) Documents .hwp, .hwpx, .pdf, .docx, .doc, .xlsx, .xls, .pptx, .ppt, .txt, .csv NEIS 공문서, 시험지, 성적부, 강의 계획서 자동 보존
설치 및 압축 Installers & Archives .exe, .msi, .msix, .zip, .7z, .rar, .iso, .tar, .gz 각종 교육용 소프트웨어 셋업 및 대용량 압축 데이터 정리
이미지 미디어 Images .png, .jpg, .jpeg, .gif, .webp, .svg, .bmp, .psd, .ai, .heic 학급 앨범, 캡처 판서, 그래픽 소스 고화질 정리
동영상 미디어 Videos .mp4, .mkv, .mov, .avi, .wmv, .webm 원격 수업 녹화본, 유튜브 다운로드 교육 영상 정리
오디오 사운드 Audio .mp3, .wav, .flac, .aac, .ogg, .m4a 영어 듣기 평가 자료, 행사 배경 음악 파일 분류
코드 및 개발 Codes & Dev .cs, .py, .js, .ts, .html, .css, .cpp, .json, .xml, .yaml, .sql SW/AI 정보 교육 실습 코드 및 학생 과제 제출물 정리
3D 및 디자인 Design & 3D .blend, .fbx, .obj, .stl, .fig 3D 프린터 실습 모델링(.stl) 및 메타버스 그래픽 에셋

6. 기존 서드파티 정리기 vs Windows 저장 공간 센스 vs ZeliDesk ZeliSweep 심층 비교

비교 항목 CCleaner / 고클린 등 기존 정리기 Windows 저장 공간 센스 (Storage Sense) ZeliDesk ZeliSweep (엔지니어링 백서)
분류 동작 방식 파일 삭제 위주 (오래된 다운로드 강제 제거) 30일 이상 미사용 파일 무조건 영구 삭제 무삭제 100% 보존: 확장자별 지능형 자동 정돈
실시간 감지 여부 수동 실행 필요 (배치 파일 방식) 일/주 단위 백그라운드 주기적 검사 다운로드 완료 즉시 0초 실시간 자동 이동
다운로드 락 충돌 방어 지원 안 함 (접근 에러 시 건너뛰거나 실패) 해당 없음 15초 적응형 재시도 큐 & 브라우저 임시 파일 격리
동명 파일 보호 단순 덮어쓰기로 원본 훼손 위험 해당 없음 FileName (1).ext 무손실 원자적 인덱싱
CPU 및 메모리 부하 주기적 폴링 시 3~10% 점유 시스템 프로세스 상시 점유 커널 이벤트 기반: 유휴 시 CPU 0.01% 미만
보안 및 광고 외부 애드웨어 및 원격 텔레메트리 포함 Microsoft 원격 진단 데이터 전송 100% 로컬 오프라인 무설치 (Zero Telemetry)

7. 결론: "다운로드 클릭 즉시 제자리를 찾아가는 무소음·무간섭 데스크톱 거버넌스"

선생님이나 공공 연구원들이 컴퓨터를 다루면서 가장 많은 피로를 느끼는 순간은 정작 복잡한 작업을 할 때가 아니라, 방금 내려받은 파일 하나를 찾기 위해 스크롤을 끝없이 내리며 헤맬 때입니다.

ZeliDesk의 ZeliSweep은 화려하게 사용자의 시선을 빼앗지 않습니다. 백그라운드에서 조용히 숨 쉬며 Windows OS 커널의 파일 변경 통보를 수신하고, 브라우저와 백신의 락 상태를 섬세하게 어루만지며 모든 파일을 완벽한 제자리로 인도합니다. 극도의 안정성과 파일 무손실 원칙을 타협하지 않는 것—ZeliDesk가 추구하는 데스크톱 거버넌스 엔지니어링의 정수입니다.

← 이전 기술 백서 #977 교실 판서와 다중 모니터 정밀 캡처: ZeliSnap의 가상 화면 음수 좌표계와 Win32 GDI 무손실 렌더링 아키텍처 다음 기술 백서 → #975 폐쇄망 학교와 군부대를 위한 오프라인 QR 코드 거버넌스: ZeliQR의 QRCoder 인프로세스 렌더링과 Wi-Fi/vCard 무선 자동 연결 아키텍처
🧹 ZeliDesk ZeliSweep 자동 파일 정돈기 체험하기
더 이상 다운로드 폴더에서 문서를 찾아 헤매지 마세요. 다운로드 버튼을 누르는 순간 0초 만에 완벽히 정돈되는 스마트 데스크톱을 경험하세요.
72종 유틸리티 전체 둘러보기 →