클릭하여 터미널 활성화

휴지통에 버려도 끝이 아니다 | macOS 잔여 파일 청소 앱 만든 이야기

// macOS · Python · PyQt6 · py2app · 앱 언인스톨러 · DMG 배포
macOS에 프로그램 제거가 없어서 직접 만들었다 — MacCleaner 개발기

macOS에는 "프로그램 추가/제거"가 없다

Windows 쓰다가 macOS로 넘어오면 제일 먼저 당황하는 게 이거다. 앱을 지우려고 "프로그램 추가/제거"를 찾는데 없다. 그냥 휴지통에 드래그하면 된다고들 하는데, 그게 끝이 아니다.

앱을 휴지통으로 버려도 설정 파일, 캐시, 로그, 컨테이너 데이터~/Library 곳곳에 그대로 남는다. 앱 본체만 없어지고 찌꺼기는 계속 쌓인다.

"Windows처럼 앱과 관련 파일을 한 번에 깔끔하게 지울 수 없나?"

CleanMyMac 같은 유료 앱이 있긴 한데, 기능 대비 가격이 부담스럽다. 그냥 만들었다.

MacCleaner v1.0.0
언어       : Python 3 + PyQt6
패키징     : py2app → DMG (hdiutil)
앱 크기    : 257MB (.app) / 109MB (.dmg)
스캔 경로  : /Applications, ~/Applications
잔여파일 탐색: ~/Library 내 10개 디렉토리
배포 방식  : GitHub Releases (무료, 계정 불필요)
라이선스   : MIT

1. 기술 스택 선정 — Swift 말고 Python을 선택한 이유

GUI 앱을 만드는 방법은 여러 가지다. 세 가지를 놓고 비교했다.

방식 장점 단점
Python + PyQt6 ✅ 빠른 개발, 풍부한 위젯 앱 크기 큼 (~100MB)
Swift + SwiftUI 네이티브, 가벼움 (~5MB) 학습 비용 높음
Electron 웹 기술로 개발 가능 매우 무거움 (~200MB)

Swift가 네이티브라 가장 이상적이지만 학습 비용이 높다. Electron은 너무 무겁다. 이미 Python 환경이 구축되어 있고, PyQt6로 충분히 네이티브에 가까운 UI를 만들 수 있어서 Python + PyQt6 + py2app 조합을 선택했다.

2. 핵심 기능 구현

기능 1 — 앱 스캔 시스템

/Applications~/Applications를 스캔해서 .app 번들을 탐지한다. 단순히 파일명만 읽는 게 아니라 Info.plist를 파싱해서 앱의 메타데이터를 전부 수집한다.

앱 이름번들 이름에서 .app 제거
번들 IDCFBundleIdentifier — Info.plist에서 추출
버전   CFBundleShortVersionString
크기   du -sk 명령으로 실제 디스크 점유량 계산
아이콘  CFBundleIconFile → .icns 파일 경로 → 앱 목록에 표시
💡
비동기 처리 (QThread)
앱 스캔을 메인 스레드에서 실행하면 앱이 많을 때 UI가 멈춘다. QThread로 백그라운드에서 실행하고 진행률을 UI에 실시간으로 표시했다. 앱이 아무리 많아도 화면이 버벅이지 않는다.
기능 2 — 잔여 파일 자동 탐색

앱을 선택하면 ~/Library 내 10개 디렉토리에서 관련 파일을 자동으로 찾아낸다. 번들 ID와 앱 이름 두 가지를 기준으로 대소문자 무시 매칭을 수행한다.

탐색 대상 디렉토리 10개
Preferences/앱 설정 파일 (.plist)
Application Support/앱 데이터
Caches/캐시 데이터
Logs/로그 파일
Containers/샌드박스 앱 데이터
Group Containers/앱 그룹 공유 데이터
Saved Application State/윈도우 상태
WebKit/WebKit 관련 데이터
HTTPStorages/HTTP 저장소
Cookies/쿠키 데이터
기능 3 — 선택적 삭제

찾아낸 잔여 파일을 전부 강제 삭제하지 않는다. 체크박스로 항목별 선택/해제가 가능하고, "Select All"로 일괄 처리도 된다. 각 항목의 카테고리와 크기를 표시하고, 앱 크기 + 선택한 파일 크기의 총합을 실시간으로 계산해서 보여준다.

기능 4 — 삭제 프로세스 (권한 처리 포함)

삭제는 5단계로 진행된다. 특히 시스템 앱이나 권한이 필요한 앱 삭제 시 macOS 기본 인증 다이얼로그가 자동으로 뜨도록 처리했다.

STEP 1확인 다이얼로그로 삭제 의사 재확인
STEP 2관련 파일 먼저 삭제 (일반 권한)
STEP 3앱 번들 삭제 시도 (일반 권한)
STEP 4권한 부족 시 osascript로 관리자 인증 요청← macOS 기본 인증 다이얼로그
STEP 5삭제 완료 후 앱 목록 자동 갱신

3. UI 디자인

다크 테마 기반의 2패널 레이아웃으로 설계했다. 좌측에 앱 목록, 우측에 상세 정보와 잔여 파일 목록.

UI 레이아웃
MacCleaner Rescan
🔍 Search applications...
앱 목록
🅰 AppName  24MB
🅱 SelectedApp 88MB
🅲 AnotherApp 12MB
Application Info
Name: SelectedApp  |  Version: 3.2.1
Bundle ID: com.selected.app  |  Size: 88MB
Related Files
☑ com.selected.app.plist  Prefs  4KB
☑ SelectedApp/          Caches 12MB
☑ SelectedApp/          Support 33MB
Total: 133MB Uninstall
컬러 시스템
#1a1a2e 배경
#16213e 카드
#0f3460 선택 강조
#e94560 삭제 버튼
#5b8def 링크·포커스

4. 패키징 및 GitHub 배포

Python 스크립트를 일반 사용자가 바로 실행할 수 있는 앱으로 만들기까지 3단계가 필요하다.

빌드 → DMG → 배포 흐름
STEP 1
py2app
빌드
→ MacCleaner.app
(257MB)
STEP 2
hdiutil
DMG 생성
→ MacCleaner.dmg
(109MB, 압축)
STEP 3
GitHub
Releases
누구나
다운로드 가능

DMG 안에는 Applications 심볼릭 링크가 포함되어 있어서 DMG를 열면 앱을 Applications 폴더로 드래그&드롭하는 익숙한 설치 방식을 그대로 쓸 수 있다.

⚠️
첫 실행 시 Gatekeeper 경고
Apple Developer 계정으로 공증(Notarization)된 앱이 아니므로 "확인되지 않은 개발자" 경고가 뜬다. 두 가지 방법으로 해결 가능하다.
① 앱 우클릭 → 열기 → 열기 클릭
② 시스템 설정 → 개인 정보 보호 및 보안 → "확인 없이 열기"

5. 프로젝트 구조

MacCleaner/
MacCleaner/
├─ mac_cleaner.py— 메인 앱 소스코드
├─ setup.py— py2app 빌드 설정
├─ icon.icns— 앱 아이콘
├─ icon.png— 아이콘 원본 (1024×1024)
├─ screenshots/— 스크린샷
├─ README.md
├─ LICENSE— MIT
├─ venv/— 가상환경 (Git 제외)
├─ build/— 빌드 임시 파일 (Git 제외)
└─ dist/— 빌드 결과물 (Git 제외)
├─ MacCleaner.app— 앱 번들 (257MB)
└─ MacCleaner.dmg— DMG 인스톨러 (109MB)

6. 향후 개선 방향

기능 개선
앱 크기 기준 정렬 기능
삭제 히스토리 로그 저장
시스템 앱 / 사용자 앱 필터 분리
LaunchAgent / LaunchDaemon 관련 파일 탐색 추가
Login Items 정리 기능
UI 개선
앱 카테고리별 그룹화
사용 빈도 / 마지막 실행일 표시 (Spotlight 메타데이터 활용)
다크 / 라이트 테마 전환
배포 개선
GitHub Actions로 자동 빌드 파이프라인 구성
Universal Binary 지원 (Intel + Apple Silicon)
Homebrew Cask 등록 (brew install --cask maccleaner)
Apple Developer 계정 취득 후 공증(Notarization) 적용
// 결론
macOS에 없는 기능이면 만들면 된다.
앱 번들만 지우고 끝내지 말고, 찌꺼기까지 같이 치워라.
Tags: #MacCleaner #macOS앱개발 #Python #PyQt6 #py2app #DMG배포 #언인스톨러 #GitHub배포 #오픈소스