TrendShift 딥다이브 · 2026-07-07 · Daily #8

FluentCleaner 딥다이브
— "CCleaner는 변질됐다": 15년치 winapp2.ini 커뮤니티 청소 규칙을 재사용하는 .NET 10 · WinUI 3 오픈소스 청소기

FluentCleanerCCleaner의 후속을 자처하는 현대적 오픈소스 윈도우 디스크 청소기다. 직접 청소 규칙을 손으로 짜는 대신, 커뮤니티가 15년간 쌓아 온 winapp2.ini 규칙 데이터베이스(3,721개 항목)를 자체 파서로 읽어 수천 개 앱의 캐시·임시파일을 지운다. UI는 .NET 10 + WinUI 3 최신 스택이고, 레지스트리 청소·보안 삭제 같은 "미신"은 일부러 뺐다고 README가 대놓고 선언한다. 앱 안에 명령어 터미널Groq AI 설명기까지 넣은, C#/XAML로 짠 진짜 데스크톱 앱이다. (저장소: builtbybel/FluentCleaner · C# · MIT · v26.06.04 · Windows 10/11 · ⭐ 약 2.7k · TrendShift 오늘 Daily 8위)
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어/시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

이 저장소가 대체 무엇인가.

FluentCleaner(제작자 Belim, GitHub 조직 builtbybel)는 윈도우의 불필요한 임시파일·캐시·로그를 지워 디스크를 비우는 청소 도구다. 겉보기엔 흔한 "CCleaner류" 유틸리티지만, 핵심은 두 가지다. 첫째, UI를 마이크로소프트의 최신 WinUI 3로 짜서 윈도우 11의 Mica·Fluent 디자인을 그대로 입었고, 둘째, 청소 규칙을 스스로 만들지 않고 커뮤니티 표준인 winapp2.ini를 재사용한다는 점이다.

한 줄 개념
규칙 기반 디스크 청소기 (rule-driven disk cleaner)
"어떤 폴더의 어떤 파일을 지워도 되는가"를 프로그램에 하드코딩하지 않고, 사람이 읽을 수 있는 규칙 파일(winapp2.ini)에 적어 둔다. FluentCleaner는 그 규칙을 해석해 실행하는 엔진일 뿐이다. 규칙만 갈아끼우면 새 앱을 청소할 수 있다.
비유로 이해하기

"청소 로봇 + 청소 매뉴얼을 분리한 것"

보통의 청소 앱은 "여기 청소해, 저기 청소해"라는 지시가 프로그램 안에 콘크리트처럼 굳어 있다. 새 앱이 나오면 개발자가 코드를 고쳐야 한다.

FluentCleaner는 청소 로봇(엔진)청소 매뉴얼(winapp2.ini)을 분리했다. 매뉴얼에는 "크롬 캐시는 여기 있다"처럼 사람이 읽고 고칠 수 있는 규칙이 3,721개 적혀 있다. 로봇은 그 매뉴얼만 읽고 움직인다. 매뉴얼을 커뮤니티가 15년째 갱신해 주니, 로봇은 코드 한 줄 안 바꿔도 최신 앱을 청소한다.

코드 기준 버전은 26.06.04(2026년 6월의 4번째 빌드)로, 날짜 기반 버전(YY.MM.build)을 쓴다. 라이선스는 관대한 MIT이고, 저장소는 2026년 4월 13일 생성돼 아직 3개월도 안 된 신생 프로젝트인데도 별점이 약 2.7k에 이른다. 실행 파일 이름은 FCleaner.exe이며, 설치 없이 바로 실행되는 언패키지드(unpackaged) 방식이다.

먼저 짚고 갈 것 — 이 문서의 초점
"청소 앱"이 아니라 "현대 WinUI 3 데스크톱 앱의 교과서"

"임시파일 지우기"는 겉모습이다. 이 레포의 진짜 가치는 WinUI 3 + .NET 10 최신 UI · CommunityToolkit.Mvvm 소스 제너레이터 · INI 파서 직접 구현 · 2단계(분석→삭제) 파이프라인 · P/Invoke로 파일 잠금 검사 · 하나의 엔진을 GUI·터미널·무인모드 3곳에서 재사용하는 설계를 한 코드베이스에서 통째로 볼 수 있다는 점이다. "네이티브 윈도우 유틸리티를 요즘 방식으로 만드는 법"의 살아있는 표본이다.

2왜 주목받는가

트렌딩 이유와, 흔한 "시스템 청소기"와의 결정적 차이.

① "CCleaner는 죽었다"는 서사 — 향수 + 저항

README는 사실상 CCleaner를 향한 성토문이다. 한때 모두가 쓰던 CCleaner가 VPN 끼워팔기·가짜 "레지스트리 마법"·광고로 bloatware(군더더기 소프트웨어)로 변질됐다는 것. FluentCleaner는 그 빈자리를 노린 "안티-CCleaner"를 표방한다. 텔레메트리 없음, 업셀 없음, 오픈소스. 이 감정적 서사가 r/windows·해커뉴스에서 강하게 먹혔다.

비교

변질된 CCleaner = "청소는 덤, 진짜 목적은 VPN·유료판 팔기"(광고·끼워팔기·추적). FluentCleaner = "청소만 한다, 오픈소스로, 추적 없이"(MIT, 텔레메트리 0).

② 정직한 엔지니어링을 '기능'으로 내세운다

가장 인상적인 부분. README에는 "옵티마이저 미신(Optimizer Myths)" 섹션이 있어, 다른 청소기들이 파는 기능을 왜 일부러 안 넣었는지를 설명한다. 보안 삭제(secure delete)는 SSD의 TRIM 때문에 무의미하고, 레지스트리 청소는 효과는 위약(placebo) 수준인데 위험만 크다는 것. "기능을 빼는 이유를 당당히 문서화"하는 이 투명함이 개발자 커뮤니티의 신뢰를 샀다.

③ 진짜 최신 스택 — 요즘 나온 청소기가 2009년처럼 안 생겼다

대부분의 윈도우 청소기가 낡은 Win32 룩앤필에 머물러 있는 사이, FluentCleaner는 .NET 10 + WinUI 3 + Windows App SDK 2.0.1이라는 2026년 최신 조합으로 네이티브 Mica/Acrylic 배경, Fluent 컨트롤을 그대로 쓴다. 심지어 x64와 ARM64를 모두 네이티브 지원한다. "청소기도 예쁠 수 있다"를 증명한 셈이다.

④ 15년 커뮤니티 자산에 무임승차 — 똑똑한 재사용

winapp2.ini는 2010년대 초부터 커뮤니티(MoscaDotTo/Winapp2)가 CCleaner용으로 쌓아 온 청소 규칙 사전이다. FluentCleaner는 이걸 파싱해 3,721개 항목을 즉시 지원한다 — 크롬·파이어폭스·오피스·수천 개 앱을 손으로 코딩할 필요가 없다. 특히 CCleaner 7이 winapp2.ini 지원을 끊어버린 지금, FluentCleaner는 그 유산의 새 안식처가 됐다는 점에서 더욱 화제였다.

⑤ 스크립트가 아니라 살아 움직이는 제품

생긴 지 11주밖에 안 됐지만 릴리스가 쉴 새 없이 나온다. 전역 예외 목록(Global Exclusions)·설정 내보내기/가져오기·포터블 모드·무인 청소(/AUTO)가 몇 주 새 차례로 추가됐고, 8개 언어 현지화가 커뮤니티 PR로 들어온다. 자매 앱 FluentTweaker까지 출시돼 하나의 생태계를 이룬다. Renovate 봇이 의존성을 자동 관리하고 GitHub Actions로 x64/arm64를 자동 빌드하는 등, 1인 개발치고 운영이 탄탄하다.

배움 관점 핵심
"이 레포에서 무엇을 얻는가"

청소라는 용도는 부차적이다. 진짜 가치는 WinUI 3 앱 부트스트랩 · MVVM 소스 제너레이터 · 직접 만든 INI 파서 · 와일드카드 경로 확장 · P/Invoke 파일 잠금 검사 · 2단계 안전 삭제 · 하나의 코어를 GUI/CLI/무인모드로 재사용하는 실전 코드를 통째로 관찰할 수 있다는 것.

3기술 스택 전체 지도

무엇으로 만들어졌나 — 계층별로. (FluentCleaner.csproj에서 실제 확인)

언어 & 빌드 시스템

항목메모
언어C# + XAMLNullable enable, ImplicitUsings enable — 최신 C# 스타일
런타임.NET 10net10.0-windows10.0.19041.02026년 기준 최신 .NET
UI 프레임워크WinUI 3UseWinUI true, Microsoft.UI.Xaml — 윈도우의 현행 네이티브 UI 스택
최소 OSWindows 10 (17763) 이상Windows 11에서 Mica 배경이 온전히 나온다
플랫폼x64 + ARM64RuntimeIdentifiers win-x64;win-arm64 — ARM 네이티브
솔루션 포맷.slnx (신형 XML)구형 .sln 대신 새 XML 기반 FluentCleaner.slnx

NuGet 의존성 (딱 3개 — 매우 가볍다)

패키지버전역할
Microsoft.WindowsAppSDK2.0.1WinUI 3 런타임 + Fluent 컨트롤 + Mica/Acrylic 배경
Microsoft.Windows.SDK.BuildTools10.0.28000.1839빌드 도구(PrivateAssets=all)
CommunityToolkit.Mvvm8.4.2MVVM 소스 제너레이터 ([ObservableProperty], [RelayCommand])

주목할 점: 의존성이 단 3개다. SQLite도, Entity Framework도, 무거운 서드파티 프레임워크도 없다. "다중 데이터베이스"라는 표현 때문에 DB 엔진을 떠올리기 쉽지만 여기엔 SQL이 전혀 없다(→ 4장에서 상술). 설정 저장조차 System.Text.Json 파일 하나(%AppData%\FluentCleaner\settings.json)로 끝낸다. 플랫폼 API를 직접 쓰는 미니멀한 취향이 드러난다.

패키징 & 실행 방식

항목왜 중요한가
패키지 타입None (언패키지드)WindowsPackageType=None — MSIX·스토어 없이 그냥 .exe
자기완결SelfContained true.NET 런타임을 품고 배포 → 사용자가 .NET 설치 불필요
권한asInvoker (관리자 아님)app.manifest가 일반 권한으로 실행 — 필요할 때만 승격
런타임 의존Windows App Runtime ≥ 1.8run.ps1이 실행 전 존재를 검사

도구 & CI

GitHub Actions build.ymlx64/arm64 매트릭스windows-latest에서 dotnet build -c Release를 돌리고, codeql.yml이 코드 보안을 스캔한다. Renovate 봇이 NuGet·Actions 의존성을 자동으로 갱신·머지(패치 버전은 자동)한다. 릴리스는 미리 빌드된 FCleaner.exe와 선택적 확장팩 Custom.zip을 함께 배포한다.

헷갈리기 쉬운 용어 — "다중 데이터베이스"
DB = SQL 데이터베이스가 아니라 winapp2.ini "규칙 파일" 여러 개
이 앱에서 "데이터베이스"는 MySQL·SQLite 같은 DB가 전혀 아니다. winapp2 형식의 텍스트 규칙 파일을 여러 개(Winapp2·Winapp3·Winappx·사용자 커스텀) 겹쳐 쓴다는 뜻이다. 각 파일이 "청소 규칙 묶음" 하나다.

4아키텍처 심화 분석

"청소 버튼"을 누르면 안에서 무슨 일이 벌어지는가.

전체 그림 — 규칙 파일에서 삭제까지

모든 것은 winapp2.ini 형식의 규칙에서 출발한다. 규칙 하나가 CleanerEntry 객체로 파싱되고, "이 앱이 깔려 있나?"를 판단한 뒤, 경로의 변수·와일드카드를 실제 경로로 펼치고, 2단계(분석 → 삭제)로 안전하게 지운다. 이 앱이 지우는 대상은 딱 두 가지 — 파일레지스트리 키/값뿐이다. 조각모음도, 보안 삭제도 없다.

┌──────────────────────────────────────────────────────────────────────┐ │ FCleaner.exe (WinUI 3, 언패키지드) │ │ │ │ MainWindow = TitleBar + NavigationView (페이지 전환 셸) │ │ ┌───────────┬───────────┬───────────┬───────────┬───────────┐ │ │ │ Cleaner │ Terminal │ Custom │ Tools │ Settings │ Views │ │ │ Page │ Page │ Page │ Page │ Page │ │ │ └─────┬─────┴─────┬─────┴───────────┴─────┬─────┴─────┬─────┘ │ │ │ 바인딩 │ 바인딩 │ │ │ │ ▼ ▼ ▼ ▼ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ ViewModels (CommunityToolkit.Mvvm) │ │ │ │ CleanerPageViewModel(759줄) · CliViewModel · SettingsVM … │ │ │ └───────────────────────────┬──────────────────────────────────┘ │ │ │ 위임(delegate) │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ Services (엔진) │ │ │ └──────────────────────────────────────────────────────────────┘ │ └────────────────────────────────┬───────────────────────────────────────┘ │ ┌──────────────┬──────────────┼──────────────┬──────────────────┐ ▼ ▼ ▼ ▼ ▼ [Winapp2Parser] [DetectionSvc] [PathExpander] [CleaningService] [부가] INI → 규칙객체 앱 설치 감지 변수·*·? 확장 2단계 분석/삭제 AI·Appx·무인 FileKey/RegKey DET_CHROME… %LocalAppData% ①분석 ②삭제 Groq·PowerShell
핵심 개념 — 하나의 엔진, 세 개의 얼굴
GUI · 터미널 · 무인모드가 같은 CleaningService를 공유
청소 로직(CleaningService·DetectionService)은 딱 한 벌만 존재한다. 사용자가 GUI 버튼을 누르든, 터미널에 clean all을 치든, FCleaner.exe /AUTO로 무인 실행하든, 전부 같은 엔진을 부른다. 프론트엔드만 셋, 두뇌는 하나다.

1단계 — INI 파싱 (Winapp2Parser)

82줄짜리 작은 파서가 winapp2 형식을 읽는다. 이 형식은 번호가 붙는 다중 키(FileKey1, FileKey2…, RegKey1…, ExcludeKey1…)와 감지 규칙(Detect, DetectFile, SpecialDetect)을 갖는다. 실제 규칙 한 덩어리는 이렇게 생겼다:

; winapp2.ini 규칙 예시 — "구글 크롬 캐시"
[Google Chrome Cache *]
LangSecRef=3021
Detect=HKCU\Software\Google\Chrome     # 이 레지스트리 키가 있으면 크롬 설치됨
Default=False
FileKey1=%LocalAppData%\Google\Chrome\User Data\*\Cache|*.*|RECURSE
FileKey2=%LocalAppData%\Google\Chrome\User Data\*\Code Cache|*.*|RECURSE

파서는 한 항목에 "감지 방법"과 "지울 대상"이 둘 다 있을 때만 유효한 규칙으로 인정한다. 사소하지만 중요한 디테일: WinUI의 TextBox는 줄바꿈을 \r만으로 저장할 때가 있어, 파서는 \r\n 양쪽으로 잘라 준다.

2단계 — 설치 감지 (DetectionService)

"이 앱이 설치돼 있나?"를 확인해, 없는 앱의 청소 규칙은 목록에서 숨긴다. 판단 방법은 세 가지 — 레지스트리 키 존재, 파일/폴더 존재, 그리고 SpecialDetect 단축코드(DET_CHROME, DET_FIREFOX, DET_EDGE, DET_OPERA, DET_WINSTORE 등)다. 여러 조건 중 하나만 맞아도(OR) 해당 규칙을 보여준다.

3단계 — 경로 확장 (PathExpander)

129줄짜리 이 서비스가 규칙 속 추상 경로를 진짜 경로로 바꾼다. 두 가지 일을 한다:

# (1) winapp2 전용 환경변수 치환
%LocalAppData%   →  C:\Users\나\AppData\Local
%ProgramFiles%   →  C:\Program Files   (없으면 자동으로 (x86) 재시도)
%SystemDrive%    →  C:

# (2) 경로 안의 와일드카드(* ?)를 재귀적으로 실제 폴더로 펼침
%LocalAppData%\Google\Chrome\User Data\*\Cache
   →  ...\User Data\Default\Cache
   →  ...\User Data\Profile 1\Cache      # 프로필마다 실제로 펼쳐짐

맨 끝 드라이브 루트(C: vs C:\) 처리 같은 함정까지 코드에 주석으로 남아 있어, "경로 다루기가 왜 어려운가"를 배우기 좋다.

4단계 — 2단계 삭제 (CleaningService, 349줄)

이 앱에서 가장 중요한 안전장치. 청소를 "분석"과 "삭제" 두 국면으로 확실히 분리한다.

┌─ ① 분석 (Analyze) — 읽기 전용, 아무것도 안 지움 ──────────────┐ │ · FileKey 규칙을 따라 지울 파일 목록을 모은다 │ │ · 총 몇 바이트 확보되는지 미리 계산한다 │ │ · 각 파일이 "삭제 가능한지" P/Invoke로 미리 확인: │ │ CreateFileW(..., DELETE 권한 요청) │ │ → 잠긴 파일(다른 앱이 쓰는 중)은 조용히 건너뜀 │ │ · reparse point(정션/심볼릭 링크)는 건너뜀 │ │ → All Users ↔ ProgramData 무한 루프 방지 │ └───────────────────────────────┬─────────────────────────────┘ ▼ 사용자가 확인 후 "청소" 클릭 ┌─ ② 삭제 (Clean) — 실제로 지움 ──────────────────────────────┐ │ · 대기열의 파일 삭제 │ │ · REMOVESELF 플래그면 빈 폴더까지 (깊은 곳부터) 제거 │ │ · 레지스트리: 키 전체(DeleteSubKeyTree) 또는 값 하나 │ │ (DeleteValue) — HKCU/HKLM/HKU/HKCC/HKCR 지원 │ │ · 파일마다 취소 토큰 확인 → "규칙 절반만 지우는" 사고 방지 │ └──────────────────────────────────────────────────────────────┘
설계 포인트
"먼저 보여주고, 확인받고, 그다음 지운다"

분석과 삭제를 나눈 이유는 사용자가 무엇이 지워질지 먼저 검토할 수 있게 하려는 것이다. 게다가 삭제 전에 P/Invoke로 파일 잠금까지 미리 확인해, 어차피 못 지울 파일로 에러를 내지 않고 조용히 건너뛴다(CCleaner의 동작을 일부러 흉내 냈다). 취소를 파일 단위로 확인하므로 도중에 멈춰도 "규칙 하나가 반만 처리된" 어중간한 상태가 남지 않는다.

규칙마다 예외(ExcludeKey: FILE|·PATH|·REG|)를 둘 수 있고, 여기에 더해 26.06.01에서 전역 예외(Global Exclusions) — 모든 DB에 걸쳐 특정 경로를 절대 안 지우는 화이트리스트 — 가 추가됐다.

5디렉토리 구조 해부

파일들이 어디에 어떻게 놓였나 — 교과서적 MVVM.

FluentCleaner/ ├── FluentCleaner.slnx # 신형 XML 솔루션 파일 ├── version.txt → "26.06.04" # 실행 시 이 값으로 업데이트 알림 판단 ├── Winapp2.ini (3,721개 항목) # 번들된 청소 규칙 DB (CCleaner 호환) ├── Winappx.ini (253줄) # AppX 블로트웨어 목록 (디블로터용) ├── renovate.json / .github/ # 의존성 자동화 + CI └── FluentCleaner/ ← 실제 프로젝트 ├── App.xaml(.cs) # 앱 부트스트랩, 배경·테마, /AUTO 분기 ├── MainWindow.xaml(.cs) # TitleBar + NavigationView 셸 ├── AppInfo.cs, app.manifest ├── Models/ # 순수 데이터 (파싱 결과) │ └── CleanerEntry · FileKeyEntry · RegKeyEntry · ExcludeKeyEntry · ScanResult ├── Services/ # 엔진 (두뇌) │ ├── Winapp2Parser · PathExpander · DetectionService · CleaningService │ ├── AppSettings · CategoryResolver · CustomEntryService │ └── SilentRunner · AppxService · AiExplainer · HeaderSpec ├── ViewModels/ # 화면 상태 + 명령 (MVVM 중간층) │ ├── CleanerPageViewModel (759줄, 핵심) │ ├── CliViewModel + CliCleanerModule / CliDebloatModule / CliExtensionsModule │ └── SettingsPageViewModel · CleanerEntryViewModel · CleanerCategoryViewModel ├── Views/ # 화면 (XAML, "멍청하게" 유지) │ └── CleanerPage · TerminalPage · CustomPage · SettingsPage · ToolsPage · NewCleanerDialog ├── Converters/ # XAML 값 변환기 (Bool→Visibility 등) ├── Assets/ # 아이콘·배너 └── Strings/ # 8개 언어 (.resw): de en fr id it pt-BR ru zh-CN
패턴 — MVVM (Model-View-ViewModel)
View는 화면만, ViewModel은 상태만, Service는 일만, Model은 데이터만
폴더 이름이 곧 역할이다. Views/(XAML 화면)는 ViewModels/에 데이터 바인딩으로 묶이고, ViewModel은 실제 작업을 Services/에 위임하며, Models/는 파싱된 순수 데이터일 뿐이다. 이렇게 나누면 "화면을 안 띄우고도" 엔진(Service)을 테스트·재사용할 수 있다 — 그래서 터미널·무인모드가 같은 엔진을 쓸 수 있는 것.

꼭 알아둘 핵심 파일

파일줄 수역할
Services/CleaningService.cs3492단계(분석→삭제) 엔진의 심장. P/Invoke 잠금 검사·취소 처리
Services/Winapp2Parser.cs82INI 형식 → CleanerEntry 객체 파서
Services/PathExpander.cs129환경변수·와일드카드 경로 확장
Services/DetectionService.cs앱 설치 여부 감지(레지스트리·파일·SpecialDetect)
ViewModels/CleanerPageViewModel.cs759메인 화면 상태·명령의 총괄 VM
ViewModels/CliViewModel.cs앱 내 터미널의 명령 디스패처
Services/SilentRunner.cs/AUTO 무인 청소 실행기 (Task Scheduler용)
Services/AiExplainer.csGroq API로 규칙 설명·INI 생성·PowerShell 생성

디렉토리만 봐도 설계 의도가 보인다. 엔진(Services)과 화면(Views)이 철저히 분리돼 있어, 새 프론트엔드(예: 터미널)를 붙일 때 청소 로직을 건드리지 않는다.

6학습 포인트 (기술별)

이 코드베이스에서 무엇을 훔쳐 배울 수 있나.

① 현대 WinUI 3 데스크톱 앱의 뼈대

NavigationView로 페이지를 전환하는 셸, TitleBar 커스터마이징, Mica/Acrylic 배경(MicaBackdrop/DesktopAcrylicBackdrop) 적용, 그리고 MSIX 없이 그냥 .exe로 배포하는 언패키지드 + self-contained 구성까지. "요즘 윈도우 앱은 이렇게 만든다"의 표준 템플릿을 통째로 얻는다.

② MVVM 소스 제너레이터 — 보일러플레이트 없는 데이터 바인딩

CommunityToolkit.Mvvm의 백미. 옛날엔 INotifyPropertyChanged를 손으로 구현하느라 프로퍼티마다 수십 줄을 썼는데, 이제는 애트리뷰트 한 줄이면 컴파일 타임에 자동 생성된다:

// 이 한 줄이…
[ObservableProperty]
public partial bool IsBusy { get; set; }

// …컴파일 타임에 "값이 바뀌면 UI에 알림" 코드로 자동 확장된다.
// 명령도 마찬가지:
[RelayCommand(CanExecute = nameof(CanClean))]
private async Task CleanAsync() { ... }   // → CleanCommand 자동 생성

C# partial 프로퍼티 + 소스 제너레이터가 어떻게 결합하는지 보는 최고의 실전 예제다.

③ 2단계 비동기 파이프라인 — 안전한 파괴적 작업

"파일을 지운다"처럼 되돌릴 수 없는 작업을 분석(읽기)과 실행(쓰기)으로 분리하고, IProgress<T>로 진행률을 보고하며, CancellationToken으로 중간 취소를 처리하고, Task.Run으로 무거운 I/O를 백그라운드로 밀어낸다. Progress<T>가 UI 동기화 컨텍스트를 자동으로 잡아 주는 덕에 백그라운드에서 진행률만 UI로 안전하게 흘려보내는 깔끔한 패턴을 배운다.

④ 직접 만든 INI 파서 + 와일드카드 경로 확장

정규식 라이브러리에 기대지 않고 도메인 특화 형식(winapp2)을 직접 파싱한다. 번호가 붙는 다중 키, 다중 감지 조건, 예외 규칙을 다루고, 경로 안의 */?재귀적으로 실제 폴더로 펼치는 로직은 "실무에서 마주치는 파싱/경로 처리"의 축소판이다. 순환(정션) 방지까지 들어 있다.

⑤ P/Invoke — 관리 코드에서 Win32 API 직접 호출

파일이 잠겨 있는지 알아내려고 CreateFileWDELETE 권한을 요청해 보는 트릭, 메모리 용량을 읽는 GetPhysicallyInstalledSystemMemory 호출 등. .NET에서 네이티브 API가 필요할 때 어떻게 다리를 놓는지(SafeFileHandle 포함)를 실물로 본다.

⑥ 하나의 코어, 세 개의 프론트엔드

같은 CleaningService/DetectionService(a) GUI 페이지, (b) 앱 내 터미널(CliViewModel), (c) 무인모드(SilentRunner)가 공유한다. "비즈니스 로직을 UI에서 떼어 놓으면 재사용이 얼마나 쉬워지는가"의 교과서. 특히 /AUTO 무인모드는 SilentRunner가 창 없이 저장된 선택을 청소하고 로그를 남긴 뒤 선택적으로 종료까지 하는데, Task Scheduler에 걸어 두면 자동 청소가 된다.

토론거리 — 배울 점이자 논쟁점
catch { } 블록이 곳곳에 있다

이 코드베이스는 잠긴/사라진 파일을 "조용히 건너뛰려고" 빈 예외 처리(catch { })를 의도적으로 많이 쓴다. 청소기 특성상 실용적인 선택이지만, 일반적으로는 "삼켜진 예외"가 디버깅을 어렵게 하는 안티패턴으로도 꼽힌다. "언제 예외를 무시해도 되는가"를 두고 스스로 판단해 보기 좋은 실제 사례다.

7하드웨어/시스템 요구사항

돌리려면 / 빌드하려면 무엇이 필요한가.

구분요구사항메모
실행 OSWindows 10 (17763) 이상Windows 11에서 Mica 배경이 온전히 나온다. x64 또는 ARM64
런타임Windows App Runtime ≥ 1.8self-contained라 .NET 자체는 불필요 — App Runtime만 있으면 됨
메모리·CPU사실상 무관(매우 가벼움)파일 열거·삭제·레지스트리 조작이 전부. 무거운 연산 없음
권한일반 권한(asInvoker)관리자로 시작하지 않는다. 시스템 영역 청소 시에만 승격 필요
빌드 도구.NET 10 SDK / Visual Studio 2022+CI는 windows-latest + dotnet build -c Release
설치(사용자)Releases의 .exe 직접 실행MSIX·스토어 불필요. 포터블 모드: exe 옆에 settings.json
주의 — 청소 도구의 본질적 위험
"지운다"는 되돌릴 수 없다 — 규칙을 믿고 실행하는 도구다

FluentCleaner는 파일과 레지스트리를 삭제한다. 2단계 설계(먼저 분석·미리보기)와 예외 목록으로 안전장치를 두었지만, 근본적으로 "winapp2 규칙이 맞다"는 전제 위에서 동작한다. 낯선 커스텀 규칙(Winapp3나 외부 .ini)을 켤 때는 무엇을 지우는지 확인하고, 중요한 데이터는 백업해 두는 것이 좋다. 특히 레지스트리 청소는 README 스스로 "효과 대비 위험이 크다"고 인정하는 영역이다.

라이선스 참고
앱은 MIT, 그러나 번들된 규칙 데이터는 CC-BY-SA-4.0

FluentCleaner 코드 자체는 MIT(2026 A Belim app creation)로 자유롭게 쓸 수 있다. 다만 함께 배포되는 Winapp2.ini 데이터는 원저작자(MoscaDotTo/Winapp2)의 CC-BY-SA-4.0이다. 코드와 데이터의 라이선스가 다르다는 점 — 오픈소스를 재배포할 때 흔히 놓치는 부분이라 짚어 둔다.

8직접 해볼 수 있는 실습 과제

읽기만 하면 안 남는다 — 난이도별로 손을 움직여 보자.

과제 ① 난이도: 입문

소스에서 빌드해 실행하기

레포를 클론하고 dotnet build -c Release(또는 Visual Studio에서 FluentCleaner.slnx 열기)로 FCleaner.exe를 만든 뒤 실행한다. 먼저 "분석(Analyze)"만 눌러 무엇이 지워질지 목록만 확인하고 실제 삭제는 하지 말자. 목표: WinUI 3 앱이 소스에서 어떻게 빌드·실행되는지 감각 잡기.

과제 ② 난이도: 초급

winapp2.ini 규칙 한 개 손으로 써 보기

[내 앱 캐시] 섹션을 만들어 Detect=(감지할 레지스트리 키)와 FileKey1=%LocalAppData%\...\Cache|*.*|RECURSE를 채운 뒤, Settings → Database → Custom에 물려 앱에 뜨는지 확인한다. 목표: "규칙 파일이 곧 기능"이라는 이 앱의 핵심 아이디어를 몸으로 이해. 형식은 저장소의 Winapp2-Format_EN.md 참고.

과제 ③ 난이도: 중급

파이썬으로 미니 INI 청소기 만들기

Winapp2Parser를 흉내 내, 파이썬 40줄로 .ini를 읽어 FileKey%LocalAppData%·와일드카드를 glob으로 펼쳐 "지울 파일 목록과 총 용량만 출력"(실제 삭제 X)하는 스크립트를 짠다. 목표: 파싱 → 변수 치환 → 와일드카드 확장 → 크기 합산이라는 이 앱의 1~4단계를 축소 재현.

과제 ④ 난이도: 고급

MVVM 소스 제너레이터로 미니 WinUI 앱 만들기

빈 WinUI 3 프로젝트에 CommunityToolkit.Mvvm을 넣고, [ObservableProperty]로 상태를, [RelayCommand]로 버튼 동작을 만들어 손으로 INotifyPropertyChanged를 안 쓰고 값이 바뀌면 화면이 갱신되게 한다. 목표: FluentCleaner의 ViewModel 계층이 어떻게 그토록 간결한지 체득.

과제 ⑤ 난이도: 심화

"삭제 전 잠금 검사"를 P/Invoke로 구현

C#에서 CreateFileWDllImport로 선언하고, 특정 파일에 DELETE 공유 권한으로 핸들을 열어 본 뒤 성공/실패로 "지금 이 파일을 지울 수 있는가"를 판별하는 함수를 만든다. 열려 있는(잠긴) 파일과 아닌 파일로 결과가 갈리는지 확인한다. 목표: CleaningService의 분석 단계 핵심 트릭 재현 — 관리 코드에서 Win32로 내려가는 경험.

9관련 기술 심화 학습 로드맵

이 레포를 온전히 읽으려면 — 5주 코스.

주차주제무엇을 / 왜
1주차C# 최신 문법비동기(async/awaitpartial·nullable·레코드·System.Text.Json. 코드 전반을 읽는 토대
2주차WinUI 3 + XAMLNavigationView·데이터 바인딩·Converters·Mica 배경. 이 앱의 표면
3주차MVVM + Toolkit[ObservableProperty]·[RelayCommand]·소스 제너레이터. ViewModel 계층의 뼈대
4주차파일·레지스트리·P/InvokeDirectory/File API, Microsoft.Win32.Registry, DllImport. 엔진의 핵심
5주차배포·자동화언패키지드 self-contained 배포, GitHub Actions 매트릭스 빌드, Renovate. "제품화" 마일
학습 순서 팁

거꾸로 읽지 말자. 2주차(WinUI로 화면 띄우기)에서 성취감을 먼저 얻고, 3주차(MVVM)에서 "화면과 로직이 어떻게 분리되나"를 이해한 다음, 4주차에서 진짜 알맹이인 "파일·레지스트리를 안전하게 다루는 엔진"으로 들어가는 게 좌절이 적다. winapp2 형식 자체는 저장소의 Winapp2-Format_EN.md를 먼저 훑어 두면 4주차가 훨씬 수월하다.

10핵심 키워드 사전

문서에 나온 용어를 한 번에.

용어
WinUI 3
마이크로소프트의 현행 네이티브 윈도우 UI 프레임워크. Windows App SDK로 배포되며, 윈도우 11의 Fluent 디자인·Mica 배경을 앱에 입힌다. FluentCleaner의 모든 화면이 이걸로 그려진다.
용어
Windows App SDK
WinUI 3와 최신 윈도우 API를 옛 OS 버전에서도 쓸 수 있게 묶어 주는 라이브러리 묶음. 여기선 2.0.1을 쓴다.
용어
MVVM (Model-View-ViewModel)
화면(View)·화면 상태와 명령(ViewModel)·데이터(Model)를 분리하는 UI 설계 패턴. 로직을 화면에서 떼어 내 테스트·재사용을 쉽게 한다.
용어
소스 제너레이터 (Source Generator)
컴파일 시점에 코드를 자동으로 만들어 주는 C# 기능. [ObservableProperty] 한 줄이 "값 변경 알림" 코드로 자동 확장되어 보일러플레이트를 없앤다.
용어
winapp2.ini
커뮤니티가 15년째 유지해 온 "어떤 앱의 어떤 경로를 청소해도 되는가" 규칙 사전(텍스트). CCleaner 호환 형식이며 FluentCleaner의 청소 대상이 여기서 온다.
용어
FileKey / RegKey / ExcludeKey
winapp2 규칙의 구성 요소. FileKey=지울 파일 경로, RegKey=지울 레지스트리, ExcludeKey=지우면 안 되는 예외. 번호를 붙여(FileKey1, FileKey2…) 여러 개를 쓴다.
용어
P/Invoke (Platform Invoke)
C# 같은 관리 코드에서 Win32 같은 네이티브 C 함수를 직접 호출하는 다리. FluentCleaner는 CreateFileW로 파일 잠금을, GetPhysicallyInstalledSystemMemory로 RAM을 읽는다.
용어
reparse point (정션/심볼릭 링크)
한 폴더가 다른 폴더를 가리키는 윈도우의 "바로가기". 청소기가 이걸 따라 들어가면 무한 루프에 빠질 수 있어, FluentCleaner는 일부러 건너뛴다.
용어
언패키지드 / self-contained
언패키지드=MSIX·스토어 없이 그냥 .exe로 배포. self-contained=.NET 런타임을 앱에 품어, 사용자가 .NET을 따로 안 깔아도 실행되게 하는 방식.
용어
CancellationToken / IProgress<T>
비동기 작업의 두 표준 도구. 전자는 "작업 도중 취소"를, 후자는 "진행률을 UI로 안전하게 보고"를 담당한다. 2단계 청소가 이 둘로 안전하게 굴러간다.
용어
디블로트 (debloat)
윈도우에 기본 설치된 불필요한 스토어 앱(AppX)을 제거하는 것. FluentCleaner는 Winappx.ini 목록으로 Remove-AppxPackage를 PowerShell로 실행한다.

11참고 링크

더 깊이 파고들 곳.