Skip to content

Latest commit

 

History

69 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DiskOUT

DiskOUT

한국어 · English · 日本語 · 简体中文

“디스크가 제대로 추출되지 않음”, 이제 그만.

디스크가 제대로 추출되지 않음 경고가 네 번 쌓인 macOS 한국어 알림 화면

DiskOUT은 경고를 숨기는 앱이 아닙니다.

덮개를 닫거나 설정한 유휴 잠자기 전에 외장 디스크의 정상 추출을 시도해, 경고가 뜰 상황을 예방합니다.

Download Latest

핵심 기능 무료 · macOS 13+ · Apple Silicon · Apple 공증

다운로드 · 변경 내역 · 이슈 / 피드백 · 이용약관 · 환불 정책 · 개인정보 처리방침


매번 Finder를 열 필요 없습니다

  • 덮개 닫기 자동 추출을 켜두면, MacBook을 닫을 때 대상 디스크의 정상 추출을 시도합니다.
  • 설정한 유휴 잠자기 전에도 자동으로 추출합니다.
  • DiskOUT이 clean 추출한 디스크는 다음 wake에도 연결되어 있으면 다시 마운트합니다.
  • 메뉴와 단축키로 개별 추출, 모두 추출, 추출하고 잠자기를 바로 실행할 수 있습니다.

추출 완료를 확인한 뒤 케이블을 분리하세요. 완료 전이나 추출 실패 후 분리하면 macOS 경고가 나타날 수 있습니다.

쓰기 중인 디스크는 구분해서 처리합니다

DiskOUT은 normal unmount(정상 마운트 해제)를 먼저 시도합니다. 수동 추출과 추출하고 잠자기는 쓰기 중이면 먼저 확인하고, 추출하지 못하면 이유와 다음 행동을 보여줍니다. Time Machine 디스크와 사용자가 제외한 디스크는 자동 추출하지 않습니다.

추출·마운트·잠자기 자동화와 숫자 메뉴바 표시는 무료입니다. USD 4.99 일회성 Premium은 숫자를 메뉴바 캐릭터로 바꾸는 선택 기능입니다.


다운로드

DiskOUT-X.Y.Z.dmg · Apple Silicon 전용

설치 (30 초)

  1. DMG 더블클릭 → 응용 프로그램 폴더로 드래그
  2. 첫 실행 시 macOS 가 한 번 묻습니다 → 열기 클릭
  3. 메뉴바에 숫자 아이콘이 뜸 (꽂힌 외장이 없으면 0)

시스템 요구사항

  • macOS 13 (Ventura) 이상
  • Apple Silicon (M1 / M2 / M3 / M4)

사용법

동작 방법
Finder 열기 / 개별 추출 메뉴바 아이콘 → 드라이브 이름 클릭 / +클릭
모두 추출 메뉴 "모두 추출" 또는 E
즉시 모두 추출 메뉴바 아이콘 우클릭
추출 + 잠자기 메뉴 "추출하고 잠자기" — 모두 성공해야 잠자기 시작
마운트 안 된 외장 마운트 메뉴 하단 섹션 클릭 또는 E
마운트 + Finder 열기 마운트 안 된 외장에 +클릭
환경설정 메뉴 "환경설정…" 또는 ,

자동 실행

환경설정 → General의 **“로그인 시 자동 실행”**에서 설정합니다. macOS가 추가 승인을 요청하면 시스템 설정 → 일반 → 로그인 항목에서 한 번 허용해주면 됩니다.


FAQ

App Store(앱 스토어) 에 안 올라온 이유는?

mount / eject 같은 디스크 조작은 sandbox(샌드박스) 환경에서 제약이 많습니다. 안정성을 충분히 확보하기 어려워 Developer ID 직접 배포 노선으로 갔습니다. 대신 Apple 공증(notarization) 을 받아서 Gatekeeper 는 문제 없이 통과합니다.

안전한가요? 데이터 손실 위험은?

DiskOUT이 관리하는 모든 추출은 Disk Arbitration(디스크 중재)의 kDADiskUnmountOptionWhole로 물리 디스크 전체 normal unmount(정상 마운트 해제)를 먼저 시도합니다. 강제 마운트 해제 설정이 켜진 수동 추출·“추출하고 잠자기”·뚜껑 닫기는 busy(사용 중) 응답 또는 2초 동안 clean callback(정상 완료 응답)이 없을 때 Whole|Force를 1회 시도합니다. idle sleep(유휴 잠자기), display sleep(화면 잠자기), forced sleep(강제 잠자기)은 Force를 사용하지 않습니다. clean 추출된 동일 물리 디스크가 다음 wake(깨우기)에도 연결되어 있으면 재마운트를 시도합니다.

단, force 단계까지 가도 추출되지 않는 디스크는 그대로 두고 알림만 띄웁니다 — 데이터 위험을 감수하면서 강제 추출하지 않습니다.

무료인가요?

네. 추출·마운트·잠자기 자동화 등 실질 기능과 기존 숫자 메뉴바 표시는 그대로 무료입니다. 선택 사항인 Premium 메뉴바 캐릭터는 USD 4.99 일회성 구매이며, 운영 결제 설정 전 개발 빌드에서는 구매 메뉴가 표시되지 않습니다.

Premium을 새 Mac으로 옮길 수 있나요?

네. 구매 후 환경설정 → Premium의 복구 코드 복사…에서 recovery code(복구 코드)를 안전한 곳에 보관하면 새 Mac에서 Premium을 이전할 수 있습니다. 이전이 완료되면 기존 Mac의 권한은 다음 온라인 확인 때 해제됩니다. 서버 확인에 연결할 수 없는 동안에는 마지막으로 검증된 권한을 최대 30일간 사용할 수 있습니다.

GitHub 아이디 없어도 다운로드 받을 수 있나요?

네. yooongZa/DiskOUT 은 public 저장소이고, GitHub Releases 의 DMG 도 anonymous 다운로드가 됩니다. GitHub 가입 / 로그인 없이도 그냥 받으실 수 있습니다.

Intel Mac 도 되나요?

현재 빌드는 Apple Silicon 전용입니다. Intel 빌드는 계획에 없습니다.

"비정상 추출" 알림이 떠요

DiskOUT 의 자동(잠자기) 추출은 정상 unmount 단계를 먼저 시도하기 때문에 보통은 알림이 뜨지 않습니다. 그래도 뜬다면 보통 다음 경우입니다.

  • 사용 중이거나 normal 응답이 늦음: 강제 마운트 해제 허용이 ON인 수동 추출·“추출하고 잠자기”·뚜껑 닫기에서 busy 응답 또는 2초 동안 clean callback이 없으면 Whole|Force를 1회 시도합니다. idle/display/forced sleep은 Force를 사용하지 않습니다.
  • macOS 가 먼저 추출을 시작한 케이스: 잠자기 직전 다른 시스템 컴포넌트가 먼저 시도.

해결: 외장에 액세스하던 앱을 종료한 뒤 잠자기, 또는 환경설정에서 강제 마운트 해제 허용을 끄세요.


알려진 제한

  • 클램쉘 모드 (외장 모니터 + 전원 + 뚜껑 닫음): macOS가 잠들지 않더라도 raw lid-close(뚜껑 닫힘) 신호에서 자동 추출은 시작됩니다. 계속 작업할 예정이면 뚜껑 닫기 자동 추출을 꺼야 합니다.
  • 잠자기 원인 판별: macOS public IOKit 알림은 Apple 메뉴·전원키·앱/명령 같은 정확한 요청자를 알려주지 않습니다. 원인을 active/unknown으로 판정해도 Whole normal은 실행하며, 원인 판별은 Force 허용 여부에만 사용합니다.
  • 사용 중 드라이브: 수동 추출·뚜껑 닫기·“추출하고 잠자기”는 busy 응답 또는 2초 무응답 뒤 force unmount(강제 마운트 해제)를 1회 시도할 수 있어 진행 중인 작업이 끊길 위험이 있습니다. idle/display/forced sleep은 normal Whole만 사용합니다.
  • 잠자기 직후 케이블 분리: 자동 추출의 clean callback(정상 완료 응답)이 오기 전에 케이블을 뽑거나 추출이 실패한 디스크를 뽑으면 macOS 비정상 추출 경고가 나타날 수 있습니다. 성공 알림을 확인한 뒤 분리해야 합니다.
  • 재마운트 신뢰도: DiskOUT이 clean 추출한 정확한 동일 디스크만 다음 wake 후 재마운트합니다. 물리적으로 이미 빠진 디스크는 앱이 다시 마운트할 수 없습니다.

자세한 기술적 제한 사항은 릴리스 노트 참고.


전체 기능

기능 매트릭스 펼치기
기능 설명
메뉴바 드롭다운 연결된 외장 드라이브 목록. stale cache(오래된 캐시)는 즉시 표시하고 background refresh(백그라운드 갱신) 완료 후 메뉴를 다시 채워 창 열림 지연을 줄임. 갱신 실패 시 기존 cache 를 유지하고 실패 row(행)를 표시. 갱신 source 는 DA event-driven 인벤토리가 1순위 → SD 카드 삽입으로 storagekitd 가 막혀도 메뉴 즉시 정상
메뉴바 아이콘 = 캐릭터/⏏ + 마운트 개수 무료 버전은 기존 eject 글리프(⏏)와 숫자를 유지. 검증된 Premium 은 0~12에 해당하는 21pt 반전 캐릭터와 오른쪽 9pt 숫자를 표시하며, count별 안전한 trailing trim(뒤쪽 여백 제외)으로 둘 사이 간격을 줄임. 13 이상·asset 누락·권한 미검증 시 무료 표시로 안전하게 복귀. 다중 파티션·RAID·APFS 합성 볼륨은 1개로 집계. DAInventory 변화에 이벤트 기반 자동 갱신하며, 추출 진행/결과 심볼(↻·✓·✗)이 animation보다 우선. Reduce Motion(동작 줄이기)에서는 첫 frame으로 정지
읽기/쓰기 활동 표시 외장에 읽기·쓰기 I/O 가 진행 중이면 메뉴바 숫자 옆 작은 systemBlue + "읽는 중 / 쓰는 중 — 분리하지 마세요" tooltip (업데이트 알림의 빨간 와 색으로 구분). 메뉴에서는 그 디스크 항목 옆에만 파란 — 어떤 디스크가 바쁜지 바로 식별하고, tooltip 으로 읽기·쓰기·둘 다를 구분. 물리 디스크 I/O 카운터(IORegistry)를 1.5s 폴링, 볼륨→물리 매핑은 parent-walk 로 RAID·APFS 합성·직결 모두 처리. 읽기는 background 인덱싱 오탐 방지로 임계가 높음. 추출 뒤 최신 마운트 목록과 물리 매핑이 확인되면 해당 디스크의 파란 점을 제거하고, 아직 마운트된 다른 디스크의 활동 상태는 유지. 외장 있을 때만 가동 (배터리)
디스크 용량 / 사용률 각 디스크 메뉴 항목 2번째 줄에 남은 용량 · 사용률 표시 — 예 2.9 TB free · 40% used. 메뉴 열 때 URLResourceValues 로 조회 (프로세스 호출 없음)
Finder 열기 / 개별 추출 드라이브 이름 클릭 = Finder 열기, +클릭 = 개별 추출. 목록 맨 위에 기본 메뉴 크기의 흐린 클릭: Finder에서 열기 / ⌘클릭: 드라이브 추출 안내를 두 줄로 한 번 표시하고, 드라이브 행에는 이름·상태·용량만 표시
쓰는 중 추출 확인 쓰는 중인 디스크를 수동 추출하거나 **“추출하고 잠자기”**를 실행하면 확인 대화상자(취소가 기본). 설정이 켜진 뚜껑 닫기는 확인 없이 busy 또는 2초 무응답 뒤 Force가 가능하고, idle/display/forced sleep은 Force하지 않음
점유 앱 끄고 재시도 추출 실패 시 디스크를 잡고 있는 앱 중 끌 수 있는 일반 앱이 있으면 알림에 "앱 종료 후 재시도" 버튼. 누르면 graceful 종료(forceTerminate 안 씀) → 1회 재추출. Finder · 시스템 데몬 · 자기 자신 제외
모두 추출 메뉴 항목 또는 단축키
추출하고 잠자기 메뉴 항목. 물리 디스크별 DA Whole normal을 병렬 실행하고, 강제 마운트 해제 허용이 켜져 있으며 busy 또는 2초 동안 clean callback이 없으면 `Whole
전역 단축키 (추출) 기본 E (한/영 IME 무관, 물리 키 코드 비교). 환경설정에서 E 기반 preset(프리셋) 변경 가능
전역 단축키 (마운트) 기본 E — 마운트 안 된 외장 일괄 마운트. 환경설정에서 변경 가능
우클릭 = 모두 추출 메뉴바 아이콘 우클릭 또는 ctrl+좌클릭. 환경설정 → Eject Behavior 에서 끄면 우클릭이 메뉴를 띄움 (실수 추출 방지 opt-out)
마운트 안 된 외장 마운트 메뉴에 "마운트 안 된 외장" 섹션 자동 노출 (후보 있을 때만). 클릭 = 마운트, +클릭 = 마운트 + Finder 열기
마운트/미마운트 상태 정합성 diskutil list -plist external 한 snapshot(스냅샷)에서 mounted(마운트됨) / unmounted(마운트 안 됨)를 함께 계산해, 실제 마운트가 없는데 mounted 섹션에 남는 stale state(오래된 상태)를 줄임
디스크 종류 아이콘 diskutil info -plist 의 SD card 신호가 확인되면 sdcard 아이콘, 그 외 외장은 externaldrive 계열 아이콘 사용
시스템 잠자기 시 자동 추출 환경설정 → 추출 동작에서 설정. idle sleep·Apple 메뉴/전원키·active/unknown 판정 모두 whole-disk DA normal unmount를 시도. 원인 판별은 Force 정책만 결정
화면 꺼질 때도 자동 추출 (옵션) 환경설정 → 추출 동작에서 설정, default OFF. pmset sleep=0 (자동 sleep 끈) 환경의 도킹 분리 사고 방지. whole-disk DA normal unmount만 시도하고 화면이 켜지면 성공한 디스크를 재마운트
wake / 화면 켜질 때 재마운트 DiskOUT이 clean 추출한 정확한 동일 물리 디스크만 재마운트. enumerate(열거) 안 되면 사용자가 분리한 것으로 보고 silent
DMG / sparseimage 제외 마운트된 이미지는 hdiutil info -plist 1초 timeout + diskutil info fallback, unmounted 후보는 BusProtocol == "Disk Image" 로 제외
추출 경로 수동·lid·idle·display·forced sleep·“추출하고 잠자기” 등 DiskOUT이 실행하는 모든 추출은 DA Whole normal로 시작. 정책과 강제 마운트 해제 허용이 모두 허용한 경로는 busy 또는 2초 clean 무응답 시 `Whole
결과 알림 무음 banner + 메뉴바 아이콘 ✓/!/✗ (circle 계열 심볼로 통일). 부재 중 발생하거나 negative 결과 (실패·재마운트 실패·sleep 추출 실패) 만 알림 센터에 보관, 본인 trigger + 성공은 banner 만 잠깐 표시
병렬 추출 DispatchGroup 으로 N개 드라이브 동시 추출
로그인 시 자동 실행 환경설정 → General에서 설정. SMAppService.mainApp 사용. .requiresApproval이면 혼합 상태와 “로그인 항목 허용 필요” 라벨을 표시하고 시스템 설정으로 안내
환경설정 창 , 또는 메뉴의 “환경설정…”. 시스템 설정식 툴바 페인 6개 — General(로그인·언어·오류 보고) / Eject Behavior(잠자기·화면 꺼짐·Music/Photos·강제 마운트 해제·우클릭) / Notifications / Hotkeys / Premium(구매·복원·상태) / About(버전·업데이트·링크). 페인별 높이 자동 전환, 비자명 옵션마다 설명 줄
단축키 충돌 자동 정정 추출 / 마운트 / 추출하고 잠자기 단축키가 같은 preset 으로 저장되면 충돌 감지 + 다른 preset 으로 자동 이동 + alert
권한 누락 메뉴 안내 Accessibility(손쉬운 사용) / 알림 권한이 미허용 상태면 메뉴 상단에 ⚠ 경고 row 표시. 클릭하면 시스템 설정의 해당 페이지로 이동
알림 세부 제어 전체 알림, 성공 알림, 실패 알림을 각각 토글. 기본은 모두 ON. macOS 시스템 설정에서 알림이 차단됐으면 상태를 표시하고 해당 설정을 바로 열 수 있음
다국어 (ko + en + ja + zh-Hans) Localizable.xcstrings 177개 키. 시스템 선호 언어 목록 전체에서 첫 지원 언어를 자동 선택하고, 모두 미지원이면 영어로 fallback. 환경설정 General → Language에서 시스템 따라가기 또는 명시 언어 선택 가능
자동 업데이트 (Sparkle 2) 24시간 주기로 백그라운드 체크. 새 버전 발견 시 다이얼로그 안 띄우고 메뉴바 아이콘에 작은 systemRed + 메뉴 안 "X.Y.Z 업데이트…" 항목 (같은 빨간 점 prefix) 으로만 표시 (gentle reminder). 사용자가 클릭하면 상태 메뉴를 닫은 뒤 Sparkle 확인을 시작하고, 확인 중·업데이트 안내·업데이트 없음/오류 modal 단계에서 전면 표시를 제한된 횟수로 다시 요청. EdDSA(Ed25519) + Apple Code Signing 이중 검증. appcast 호스팅은 GitHub Pages, DMG 호스팅은 GitHub Releases — 무료 운영
Per-disk 자동 추출 제외 메뉴 하단 "자동 추출 제외 디스크" submenu 에서 디스크별 토글. Volume UUID 기반 (케이블 슬롯 바뀌어도 유지). 자동 path 만 영향, 명시적 추출은 그대로.
Time Machine 자동 보호 TM 백업 디스크 자동 식별 (Backups.backupdb / .com.apple.timemachine.donotpresent 검사) → 첫 등장 시 자동 추출에서 제외 + 1회 알림. 메뉴에 시계 아이콘 + Time Machine badge(배지) 표기 (macOS 14+, 13은 괄호)
외장 라이브러리 앱 처리 환경설정 → Eject Behavior 토글 (default OFF). ON이면 자동 sleep/display sleep 또는 “추출하고 잠자기” 직전에 Music / Photos를 정상 종료해 외장 라이브러리 lock을 풀고, wake 후 종료했던 앱만 백그라운드에서 한 번 재실행. 겹친 sleep 이벤트도 재실행 기록을 잃지 않음

개발자 안내

빌드 · 설치 · 기술 메모 펼치기

기술 스택

항목
Bundle ID com.yongza.ejectdrives
Hardened Runtime YES
App Sandbox NO (ENABLE_APP_SANDBOX = NO)
빌드 시스템 Xcodegen + xcodebuild
진입점 main.swift (명시적 NSApplication.shared.run())
디스크 작업 모든 추출은 Disk Arbitration API의 whole-disk normal. 허용된 경로는 busy 또는 2초 clean 무응답 시 whole-disk Force 1회. clean 추출은 정확한 BSD+연결 세대+IOMedia ID가 유지될 때 다음 wake에 whole-disk remount

파일 구성

diskOUT/
├── AppDelegate.swift            # 메인 로직 (diskutil 실행, 메뉴 캐시, sleep/wake 처리)
├── LanguageRuntime.swift        # 언어 협상·저장값 검증·안전한 재시작 정책
├── PaddleBilling.swift          # Paddle checkout·서명 lease·Keychain 상태
├── PremiumAccessPolicy.swift    # fail-closed entitlement 검증 정책
├── StatusCharacterAnimator.swift # 0~12 메뉴바 character animation
├── StatusItemPresentation.swift # 무료/Premium 표시와 Preview build 경계
├── Localizable.xcstrings        # ko + en + ja + zh-Hans 번역 (Xcode String Catalog, 177 키)
├── main.swift                   # 명시적 entry point (NSApp.run)
├── Info.plist                   # bundle metadata (xcodegen 자동 생성)
├── DiskOUT.entitlements         # 빈 plist. project.yml 의 entitlements 명시 함정 방지용
├── project.yml                  # xcodegen 설정 (sandbox OFF)
├── DiskOUT.xcodeproj/           # Xcode 프로젝트 (xcodegen 으로 재생성 가능)
├── Tests/LanguageRuntimePolicyTests.swift # 언어 fallback·저장값·재시작 상태 테스트
├── CHANGELOG.md
└── README.md

빌드

한 번만 (프로젝트 생성)

cd ~/Documents/diskOUT
xcodegen generate                  # project.yml → DiskOUT.xcodeproj

매 빌드

cd ~/Documents/diskOUT
BUILD_WORK_DIR="/private/tmp/diskout-build.$(uuidgen)"
xcodebuild -project DiskOUT.xcodeproj -scheme DiskOUT -configuration Release \
  -derivedDataPath "$BUILD_WORK_DIR/DerivedData" build
printf 'Built app: %s\n' "$BUILD_WORK_DIR/DerivedData/Build/Products/Release/DiskOUT.app"

설치하려면 아래 rollback(롤백) 가능한 절차를 사용합니다. 또는 Xcode에서 DiskOUT.xcodeproj를 열고 R을 누릅니다.

Premium Preview 로컬 빌드

결제 운영 credential(자격 증명) 없이 캐릭터 UI만 검수할 때 사용하는 개발 전용 build flag(빌드 플래그)입니다. Paddle lease(이용 권한) 검증 코드는 바꾸지 않으며, 일반 빌드에는 플래그가 없어서 계속 fail-closed(안전 차단)됩니다. 배포·Archive 빌드에는 사용하지 않습니다.

cd ~/Documents/diskOUT
xcodebuild -project DiskOUT.xcodeproj -scheme DiskOUT -configuration Release \
  -derivedDataPath /private/tmp/DiskOUT-premium-preview-derived \
  'SWIFT_ACTIVE_COMPILATION_CONDITIONS=$(inherited) DISKOUT_PREMIUM_PREVIEW' build

정상 배포 경로와 Preview 경계를 함께 확인하려면 같은 표시 정책 테스트를 플래그 없이 한 번, -D DISKOUT_PREMIUM_PREVIEW와 함께 한 번 실행합니다.

안전 설치 (롤백 가능)

새 빌드 검증이 안 끝났을 때 권장. 기존 .app 을 먼저 백업 후 교체.

set -euo pipefail

# 1. 명시된 임시 경로에 빌드하고 bundle ID 검증
cd ~/Documents/diskOUT
INSTALL_WORK_DIR="/private/tmp/diskout-install.$(uuidgen)"
SOURCE_APP="$INSTALL_WORK_DIR/DerivedData/Build/Products/Release/DiskOUT.app"
TARGET_APP="$HOME/Applications/DiskOUT.app"
BACKUP_APP="$HOME/Applications/DiskOUT.app.backup.$(uuidgen)"
xcodebuild -project DiskOUT.xcodeproj -scheme DiskOUT -configuration Release \
  -derivedDataPath "$INSTALL_WORK_DIR/DerivedData" build
[ "$(plutil -extract CFBundleIdentifier raw -o - "$SOURCE_APP/Contents/Info.plist")" = \
  "com.yongza.ejectdrives" ]

# 2. 정확한 process(프로세스)만 종료하고 기존 앱을 고유 경로에 백업
mkdir -p "$HOME/Applications"
pkill -x DiskOUT 2>/dev/null || true
if [ -e "$TARGET_APP" ]; then
  mv "$TARGET_APP" "$BACKUP_APP"
  printf 'Rollback backup: %s\n' "$BACKUP_APP"
else
  BACKUP_APP=""
  printf 'Rollback backup: none (기존 설치 없음)\n'
fi

# 3. 검증한 산출물만 설치·실행
ditto "$SOURCE_APP" "$TARGET_APP"
xattr -cr "$TARGET_APP"
open "$TARGET_APP"

# 4. 검증
log show --predicate 'subsystem == "com.yongza.ejectdrives"' --info --last 1m

문제가 있으면 위에서 출력된 정확한 backup(백업) 경로를 BACKUP_APP에 넣고, 현재 앱도 삭제하지 말고 고유한 failed 경로로 옮긴 뒤 복구합니다.

set -euo pipefail
TARGET_APP="$HOME/Applications/DiskOUT.app"
BACKUP_APP="<위에서 출력된 Rollback backup 절대 경로>"
FAILED_APP="$HOME/Applications/DiskOUT.app.failed.$(uuidgen)"
[ -d "$BACKUP_APP" ]
pkill -x DiskOUT 2>/dev/null || true
mv "$TARGET_APP" "$FAILED_APP"
mv "$BACKUP_APP" "$TARGET_APP"
open "$TARGET_APP"
printf 'Failed build preserved at: %s\n' "$FAILED_APP"

검증이 끝난 백업·failed 앱은 Finder의 휴지통으로 옮겨 정리합니다.

옵션 변경 위치

대부분은 메뉴의 환경설정… 에서 바로 변경. 코드 수정이 필요한 항목만:

바꿀 것 위치
단축키 preset 추가 AppDelegate.swiftSettingsHotkeyPreset
자동추출 기본값 SleepEject.enabled 의 default 값
재마운트 backoff 간격 tryRemount(bsd:delays:operationID:) 호출 시 delays: [0, 1, 3, 7] 수정
메뉴 텍스트 populateMenu(_:snapshot:isRefreshing:) 의 문자열

키 코드는 Carbon Events.hkVK_ANSI_* 상수 참조.

진단 메모 — NSStatusBarWindow height = 0

초기 빌드 중 메뉴바에 status item 이 표시되지 않는 문제 발생. 코드는 100% 정상 동작 (NSLog 출력, statusItem / button / image 모두 정상 생성) 인데 메뉴바엔 안 보였음. 진단:

DIAG: NSApp.windows.count=1
DIAG window: class=NSStatusBarWindow frame=(0.0, 0.0, 32.0, 0.0) visible=true level=25
                                                            ^^^ height=0

NSStatusBarWindow 가 process 안에는 만들어졌지만 WindowServer 에 등록되지 않거나 height=0 으로 갇혀 있었음. macOS 26 의 새 정책 또는 status item 시스템의 미묘한 변경으로 추정.

우회 코드 (AppDelegate.swiftsetupStatusItem):

if let win = button.window {
    let thickness = NSStatusBar.system.thickness
    win.setFrame(NSRect(x: 0, y: 0, width: 32, height: thickness),
                 display: true, animate: false)
    win.orderFrontRegardless()
}

이 두 줄이 빠지면 메뉴바에 표시되지 않음.

진단 메모 — 외장 드라이브 필터

원래 필터:

guard !isInternal, isBrowsable, (isEjectable || isRemovable) else { continue }

macOS 26 에서는 Thunderbolt 외장 SSD / 일부 USB 가 isEjectable=false, isRemovable=false 로 보고되는 케이스 발견. 수정:

guard !isInternal, isBrowsable, isLocal else { continue }

isLocal 가드로 네트워크 마운트만 제외. 외장 디스크는 모두 통과.

다른 깨알 정보

  • 노치 모델: status items 가 메뉴바 좌측 (앱 메뉴 옆) 에도 배치될 수 있음 — 우측이 가득 차면 노치 너머 좌측에 등장.
  • com.apple.provenance xattr: macOS 의 fileprovider 서비스 (iCloud Drive / OneDrive 등) 가 ~/Documents/ 안의 파일에 자동으로 붙임. codesign 이 이걸 보면 "resource fork, Finder information, or similar detritus not allowed" 로 사인 거부. xattr -cr 로 정리해도 곧 다시 붙음. 빌드는 /tmp/ 등 fileprovider 영향 없는 곳에서 하는 게 안전.
  • CGWindowList 의 한계: kCGWindowOwnerName == "DiskOUT" 검색으로 윈도우 0개라도 메뉴바에 떠 있을 수 있음. ControlCenter 가 status item 의 view 를 자체 윈도우 안에 그리는 케이스가 있어 외부에서는 안 보임.
  • ProcessRunner stdout/stderr drain: Process 의 stdout/stderr 를 readabilityHandler 로 비동기 drain 하고 종료 후 남은 data 도 회수. lsof 는 3초 timeout, pmset sleepnow 는 5초 timeout.

DiskOUT · © 2026 LIMOD · All rights reserved

릴리즈 · 변경 내역 · 이슈 · 이용약관 · 환불 정책 · 개인정보 처리방침

About

macOS 메뉴바 외장 드라이브 안전 추출 유틸 (Apple Silicon · sleep eject · 다국어)

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages