홈시리즈멘토링

© 2026 정기창. All rights reserved.

본 블로그의 콘텐츠는 CC BY-NC-SA 4.0 라이선스를 따릅니다.

☕후원하기소개JSON Formatter러닝 대기질개인정보처리방침이용약관

© 2026 정기창. All rights reserved.

콘텐츠: CC BY-NC-SA 4.0

☕후원하기
소개|JSON Formatter|러닝 대기질|개인정보처리방침|이용약관

Vrew %TEMP% 18GB 자동 정리 — 5-layer 가드와 OS file lock 의 한계

정기창·2026년 5월 27일

1편에서 Vrew 의 autosave_backup 폴더를 PowerShell + Task Scheduler 로 30 분마다 자동 정리한 이야기를 정리했었습니다. 그 글을 마친 뒤로 같은 PC (winbox 라고 부르고 있습니다) 에서 VrewAutosaveBackupCleanup task 가 안정적으로 돌고 있었습니다. autosave_backup 폴더는 35 MB 근방에서 더 늘지 않았고, 9.5 시간 가동 중이던 Vrew 도 영향을 받지 않았습니다. 한동안은 이 문제가 끝났다고 생각했습니다.

며칠 뒤 지인이 보내준 %TEMP% 폴더 스샷에 18.3 GB 라는 숫자가 찍혀 있는 것을 봤을 때, autosave 는 정리했는데 다른 영역에서 또 누적되고 있다는 사실이 분명해졌습니다. 같은 PC, 같은 앱이었습니다. 한 번 닫았다고 생각한 문이 옆에 또 하나 열려 있었습니다.

%TEMP% 진단 — 99.7% 가 Vrew 출처

1편에서 했던 진단 패턴을 거의 그대로 가져왔습니다. %TEMP% 의 하위 항목을 종류별로 묶고 용량을 합산했습니다.

$temp = $env:TEMP
$folders = Get-ChildItem -LiteralPath $temp -Directory -Filter 'vrew-asset_*' -Force
$folderSize = ($folders | ForEach-Object {
    (Get-ChildItem $_.FullName -Recurse -File -Force -ErrorAction SilentlyContinue |
        Measure-Object Length -Sum).Sum
} | Measure-Object -Sum).Sum

$uuidPattern = '^[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}\.mp4$'
$looseMp4 = Get-ChildItem -LiteralPath $temp -File -Filter '*.mp4' -Force |
    Where-Object { $_.Name -match $uuidPattern }
$looseSize = ($looseMp4 | Measure-Object Length -Sum).Sum

결과를 표로 정리했더니 의외로 깔끔하게 나뉘었습니다.

범주 항목 수 크기 비중
vrew-asset_* 폴더 758 개 12.7 GB 69.4%
loose UUID mp4 732 개 5.5 GB 30.3%
Vrew .log 18 개 7.8 MB 0.04%
Windows .tmp 등 ~458 개 ~45 MB 0.24%
Vrew 합계 1,505 개 18.2 GB 99.7%

%TEMP% 18.3 GB 중 사실상 전부가 Vrew 였습니다. loose UUID mp4 (8-4-4-4-12 hex 패턴) 의 mtime 을 보니 11:36 ~ 11:56 의 20 분 안에 집중돼 있었습니다. clip 분할이나 자막 작업 흔적으로 추정되는 패턴이었습니다. 반면 vrew-asset_* 폴더 안 mp4 의 mtime 은 10:31 ~ 13:10 사이로 길게 흩어져 있었는데, 그 사이가 그 날 작업 시간대였습니다.

첫 시도 — 1편 패턴을 그대로 답습

1편의 4 겹 가드 패턴이 잘 통했으니, 이번에도 똑같이 해도 될 것 같았습니다. process guard (Vrew 가 실행 중이면 무조건 SKIP) + mtime + size + try/catch 의 4-layer 가드를 짜고 winbox 에서 dry-run 을 돌렸습니다.

dry-run 결과 자체는 정확했습니다. 758 폴더 / 12.7 GB 가 후보로 잡혔고, Phase A (Vrew 가동 중) 에서는 process guard 가 1 순위로 SKIP, -IgnoreProcessGuard 를 붙인 Phase B 에서는 후보 그대로 나왔습니다. 1편과 정확히 같은 그림이었습니다.

문제는 dry-run 이 끝난 뒤에 보였습니다. winbox 의 지인은 영상 편집 특성상 Vrew 를 거의 닫지 않습니다. 1편의 autosave_backup 폴더는 Vrew 가 켜져 있어도 백업 파일을 잠그지 않았기 때문에 회수가 가능했지만, %TEMP% 의 자산은 Vrew 가 계속 들고 있을 가능성이 있었습니다. process guard 가 1 순위로 SKIP 을 깔아 두면, 자동 회수가 영원히 0 GB 라는 의미였습니다. 1편의 성공이 2편에서 그대로 통하지 않았습니다.

두 번째 시도 — process guard 폐기, 5-layer 가드

그래서 process guard 의 1 순위 SKIP 을 폐기했습니다. 대신 다섯 겹의 가드로 안전망을 다시 짰습니다.

# 가드 default 역할
1 Vrew session 마커 – Vrew StartTime 캡처 (이전 세션 잔여물 식별)
2 Age guard -MinAgeMinutes 120 mtime ≥ 120 분 후보만 통과
3 Vrew session 가드 – mtime < StartTime (이전 세션) 추가 후보
4 Total size guard -MinTotalMB 1024 합계 1 GB 미만이면 SKIP
5 Per-folder try/catch – OS lock 발생 시 자동 skip

가설은 이랬습니다. "mtime 이 오래된 폴더 = active 가능성 낮음 = lock 풀려 있을 가능성 높음." mtime 이 두 시간 이상 지난 자산이라면 Vrew 가 그 사이에 한 번쯤 file handle 을 놓을 거라고 생각했습니다.

Vrew session 마커는 다음과 같이 캡처했습니다. 여러 프로세스 (Electron 메인 + helper 들) 중 가장 먼저 시작된 시각을 잡으면 "이번 세션이 언제 시작됐는지" 를 알 수 있습니다.

$vrew = @(Get-Process -Name 'vrew' -ErrorAction SilentlyContinue)
$vrewStart = $null
if ($vrew.Count -gt 0) {
    try {
        $vrewStart = ($vrew |
            Where-Object { $_.StartTime } |
            Sort-Object StartTime |
            Select-Object -First 1).StartTime
    } catch {
        $vrewStart = $null
    }
}

그리고 age 가드와 session 가드를 OR 로 묶었습니다. 둘 중 하나라도 통과하면 후보입니다.

$cutoff = (Get-Date).AddMinutes(-$MinAgeMinutes)
$stale = @($candidates | Where-Object {
    $byAge     = $_.LastWriteTime -lt $cutoff
    $bySession = ($null -ne $vrewStart) -and ($_.LastWriteTime -lt $vrewStart)
    $byAge -or $bySession
})

구조적으로는 깔끔하다고 생각했습니다. PR 도 통과시키고 winbox 에 배포한 뒤, 직접 invoke 로 실측을 돌려봤습니다.

실측 충격 — 7 시간 전 폴더까지 전부 lock

Task 우회로 cleanup 함수를 직접 호출했습니다. dry-run 이 아닌 실 invoke 였습니다. 결과는 다음과 같았습니다.

candidates: 755 folders / 12.69 GB
reclaimed : 0 folders / 0 GB
locked    : 755 folders
duration  : 2.3 sec
exit      : 0

755 폴더가 전부 IOException 으로 lock skip 됐습니다. 회수는 0 GB. 2.3 초만에 끝났는데, lock check 만 빠르게 돌고 모든 후보가 skip 됐기 때문이었습니다. 가설이 깨졌습니다. mtime 이 7 시간 전인 폴더까지 Vrew 가 전부 쥐고 있었습니다.

정확히 어떤 파일이 잠겨 있는지 확인하기 위해 FileShare.None 으로 직접 열어보는 진단 코드를 짰습니다.

foreach ($f in $files) {
    try {
        $s = [System.IO.File]::Open($f.FullName, 'Open', 'ReadWrite', 'None')
        Write-Output "FREE  $($f.Name)"
        $s.Close(); $s.Dispose()
    } catch {
        Write-Output "LOCK  $($f.Name)"
    }
}

각 vrew-asset_* 폴더 안에는 단일 <uuid>.mp4 가 한 개씩 들어 있었는데, 큰 폴더 (2.8 GB) 든 작은 폴더 (0 MB, 빈 파일) 든 동일하게 LOCK 으로 떨어졌습니다. mtime 이나 파일 크기와 무관한 결과였습니다.

본질은 그제서야 분명해졌습니다. Vrew 는 timeline 에 import 된 mp4 를 decode 캐시 형태로 폴더 안에 풀어두고, 그 timeline 이 살아있는 한 모든 fd 를 영구적으로 들고 있습니다. mtime 이 7 시간 전이든 1 분 전이든, 그 자산이 어딘가의 timeline 에 한 번이라도 올라간 적이 있으면 풀리지 않는 구조였습니다.

안전성 vs 운영 효과의 패러독스

여기서 묘한 결과가 나왔습니다. 5-layer 가드는 약속한 일을 정확히 해냈습니다. 데이터 손실이 0 이었고, Vrew 의 5 개 프로세스 어느 쪽도 freeze 되지 않았습니다. 다만 회수도 0 GB 였습니다.

항목 결과
5-layer 가드 안전성 데이터 손실 0, Vrew freeze 없음
5-layer 가드 운영 효과 회수 0 GB (PR #1 의 process guard 1 순위 SKIP 과 동일)
추가 비용 매 30 분 발화마다 LOCK 로그 755 줄 노이즈

돌이켜 생각해보면, 5-layer 가드를 어렵게 짰는데 결과적으로는 PR #1 의 "Vrew 가 켜져 있으면 SKIP" 한 줄과 운영적으로 동일한 그림이었습니다. process guard 가 application-level 에서 했던 역할을 OS file lock 이 OS-level 에서 동일하게 해주고 있었던 셈입니다. 1편에서 "마지막 안전망" 이라고 적었던 OS file lock 이 이번에는 사실상 유일한 안전망이 됐습니다.

세 번째 발견 — loose UUID mp4 통합

그래도 한 가지 짚이는 게 남아 있었습니다. 진단 표의 두 번째 줄, loose UUID mp4 5.5 GB 가 cleanup 의 scope 밖이었습니다. 폴더만 보고 있었기 때문이었습니다. UUID 정규식 가드를 한 줄 추가해서 통합했습니다.

$folderCandidates = @(Get-ChildItem -LiteralPath $TempRoot -Directory `
    -Filter 'vrew-asset_*' -Force)

$uuidMp4Pattern = '^[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}\.mp4$'
$fileCandidates = @(Get-ChildItem -LiteralPath $TempRoot -File -Filter '*.mp4' -Force |
    Where-Object { $_.Name -match $uuidMp4Pattern })

$candidates = @() + $folderCandidates + $fileCandidates

UUID 패턴 + %TEMP% 위치 한정의 이중 조건으로 좁혔습니다. 사용자의 다른 파일이 우연히 같은 이름 규칙을 가질 가능성을 차단하기 위해서였습니다. Remove-Item -Recurse -Force 는 폴더든 단일 파일이든 모두 동작하기 때문에 처리 경로는 한 줄로 통합할 수 있었습니다.

그런데 통합을 끝내고 다시 dry-run 을 돌려본 사이, 11 분 전에는 732 개였던 loose mp4 가 0 개로 줄어 있었습니다. Vrew 가 자체적으로 정리한 것이었습니다. 이 두 자산의 lifecycle 이 사실 달랐다는 게 그제서야 보였습니다.

자산 Vrew 의 정리 동작
vrew-asset_* 폴더 안 mp4 (timeline 원본 decode 캐시) timeline 살아있는 한 영구 fd, 정리 안 함
loose UUID mp4 (clip 분할 / 자막 임시) 작업 단계 끝나면 Vrew 가 자체 해제하고 삭제

그렇다면 PR #4 는 무의미한가 싶었는데, 그렇지는 않았습니다. Vrew 가 비정상 종료되거나 작업이 중간에 끊기면 loose mp4 가 정리되지 못한 채 남습니다. PR #4 는 그 잔존 시나리오를 덮어주는 안전망 역할을 합니다.

의외의 발견 — "닫았다고 생각" 했는데 백그라운드 잔존

지인이 어느 시점에 "Vrew 를 끄고 작업관리자에서 봤는데 아직 cleanup 회수가 안 됐다" 고 알려왔습니다. 화면에 Vrew 창이 보이지 않는데 task 가 여전히 LOCK 만 쌓고 있는 상황이었습니다. 진단을 한 단계 더 들어가봤습니다.

Get-Process -Name 'vrew' |
    Select-Object Id, ProcessName, `
        @{n='MainWnd';e={if ($_.MainWindowHandle -ne 0) {'YES'} else {'NO'}}}, `
        @{n='CPU(s)';e={[math]::Round($_.CPU,1)}}, `
        @{n='MemMB';e={[math]::Round($_.WorkingSet64/1MB,0)}}

출력은 5 개의 vrew.exe 프로세스가 전부 MainWindowHandle = 0 으로 잡혀 있었습니다. 화면에 띄워진 창은 없지만 프로세스는 살아있는 상태였습니다. 부모-자식 관계를 따라가보니 Electron 의 전형적인 구조였습니다.

Vrew.exe (main process)
├─ Vrew.exe --type=gpu-process
├─ Vrew.exe --type=utility (Network Service)
├─ Vrew.exe --type=utility (Audio Service)
└─ Vrew.exe --type=renderer  ← 23% CPU 사용 중

그 중 renderer 한 개 (PID 22596) 가 누계 CPU 4.8 시간, 메모리 1.4 GB, 현재 CPU 23% 를 점유하고 있었습니다. 9 시간 전 시작된 프로세스인데 지금도 일하고 있는 모양새였습니다. 백그라운드에서 export 인코딩이 돌고 있을 가능성이 가장 높아 보였습니다.

"Vrew 를 닫았다" 와 "Vrew 가 종료됐다" 는 다른 이야기였습니다. 시스템 트레이로 최소화돼 있거나, 메인 창은 닫혔지만 백그라운드 작업이 계속 돌고 있는 상태일 수 있습니다. 어느 쪽이든 fd 는 그대로였습니다.

OS lock 을 강제로 풀면 안 되는 이유

여기서 "그러면 OS 레벨에서 fd 를 끊으면 되지 않냐" 는 생각이 자연스럽게 떠오릅니다. Windows 에는 그런 도구가 실제로 몇 가지 있습니다. 다만 어느 것도 안전하지는 않았습니다.

방법 작동 부작용
handle.exe (Sysinternals) fd kill OS 레벨에서 핸들을 끊음 Vrew 디코더 ERROR_INVALID_HANDLE → renderer 크래시 + 진행 중 autosave 데이터 손실
taskkill /F vrew 모든 fd 자동 해제 진행 중 export 강제 중단 + 미저장 변경분 손실
MoveFileEx + DELAY_UNTIL_REBOOT 다음 부팅 시 삭제 예약 즉시 회수 0, 재부팅까지 폴더 그대로
Restart Manager API 앱에 "fd 풀어라" 신호 Vrew 가 RM 을 지원하지 않음 (Electron 일반)

결국 어느 방법으로도 "안전하면서 즉시 회수" 가 되는 길은 없었습니다. 데이터 손실을 감수하고 끊거나, 재부팅까지 기다리거나, 사용자가 직접 Vrew 를 정상 종료하도록 안내하거나의 셋 중 하나였습니다. 자동화의 본질적 한계가 거기에 있었습니다.

정리 — OS file lock 이 결국 가장 강력한 안전망이었다

1편에서 적었던 문장 중에 "OS file lock 이 사실상 마지막 안전망입니다" 가 있었습니다. 그 때는 process guard 와 시간 가드와 KeepCount 가 본 무대이고, OS lock 은 보험 정도로 적었습니다. 2편을 통과하면서 그 순서가 거꾸로 됐습니다. 시간 가드도 session 가드도 size 가드도 결국 OS lock 한 줄의 안전망 위에서만 의미가 있었습니다.

그렇다면 5-layer 가드 작업이 무의미했던 것은 아닌가 싶기도 합니다. 회수 효과만 보면 그렇습니다. 다만 안전성을 입증한 부분은 따로 의미가 있다는 생각이 들었습니다. 매 30 분 발화마다 task 가 안전하게 SKIP 로그만 쌓고 끝납니다. 어느 시점에 Vrew 가 정상 종료되거나 timeline 이 닫히면, 그 다음 30 분 boundary 에서 12.7 GB 가 일괄 회수됩니다. 그 전까지는 가만히 기다리는 task 가 되는 셈입니다.

사용자 안내도 한 줄 바뀌었습니다. 처음에는 "회수가 안 되면 Vrew 를 한 번 종료해주세요" 였는데, 위의 백그라운드 잔존 사례를 본 뒤로는 "시스템 트레이 (시계 옆 ^) 를 확인하고, 거기에 Vrew 가 있으면 우클릭 종료하고, 그래도 회수가 안 되면 작업관리자의 vrew.exe 도 마저 작업 끝내기" 로 좀 더 길어졌습니다. MainWindowHandle = 0 인 helper 프로세스까지 사용자 눈에 보이게 안내하는 쪽이 결국 정확했습니다.

1편을 마치면서 "공식이 안 주는 옵션을 외부에서 메우기" 라고 적었던 기억이 납니다. 2편에서는 그 한 줄이 "공식이 안 풀어주는 핸들은 외부에서도 못 풀더라" 로 한 번 더 echo 됐습니다. 다만 한계를 안다는 것 자체도 다음 사람에게는 작은 단축키가 된다는 생각이 들었습니다. 같은 문제로 5-layer 가드 같은 것을 짜기 전에, "OS lock 까지 닿으면 거기까지가 한계" 라는 한 줄을 미리 알아둘 수 있다면 그 시간만큼은 아낄 수 있을 것 같습니다.

스크립트는 1편과 같은 비공개 레포에서 함께 운영 중입니다. 5-layer 가드 코드와 통합 후보 수집 코드는 본문 단락에 그대로 옮겨두었으니, 같은 문제를 마주하신 분은 그 조각만으로도 비슷한 도구를 만드실 수 있을 것 같습니다. 전체가 필요하시면 1편처럼 댓글이나 이메일로 가볍게 문의 주시면 되겠습니다.

VrewPowerShellTask SchedulerWindows 자동화디스크 정리ElectronOS file lock

관련 글

Vrew autosave_backup 96GB 자동 정리 — Windows 자동화 함정 5가지

Vrew 의 autosave_backup 폴더가 Windows 의 %APPDATA% 에 96.5GB 까지 누적되는 사례를 PowerShell + Task Scheduler 로 30 분마다 자동 정리했습니다. UTF-8 BOM, vertical tab, SSH ACL 같은 Windows 자동화 함정 다섯 가지도 함께 정리합니다.

관련도 96%

Vrew 내보내기가 0%에서 멈출 때 — 로그가 알려준 진짜 원인

Vrew 내보내기가 특정 프로젝트만 0%에서 멈췄습니다. 처음엔 투명 GIF의 알파 채널이 원인이라 확신했지만, 과거 성공 로그에도 투명 GIF가 멀쩡히 들어 있었습니다. 가설을 버리고 성공·실패 GIF를 나란히 비교하니 진짜 차이는 길이였습니다. 안 되는 사례만이 아니라 되는 사례를 함께 봐야 진짜 변수가 드러난다는 것을, 로그를 따라가며 다시 배운 기록입니다.

관련도 89%

Playwright MCP 19개 프로세스 사고, LaunchAgent HTTP 상주로 통합한 회고

Claude Code 대화 세션을 닫으면 stdio Playwright MCP 도 같이 종료될 줄 알았습니다. 8개 세션을 띄워두고서야 19개 프로세스 1.6GB 점유를 측정했고, LaunchAgent HTTP 상주 모델로 통합한 운영 회고입니다.

관련도 86%