1P by GN⁺ | ★ favorite | 댓글 1개
  • Mac의 중복 파일 정리는 보통 삭제가 전제되지만, Hyperspace는 APFS 클론으로 같은 내용의 파일을 공유시켜 파일을 남긴 채 공간을 되찾음
  • APFS의 copy-on-write 클론은 Finder의 “Duplicate”처럼 실제 데이터를 다시 쓰지 않고 원본과 공유해 대용량 복제도 거의 즉시 끝나게 함
  • 개발자는 디스크가 차오르자 같은 내용이지만 클론이 아닌 파일을 찾는 Perl 스크립트를 만들었고, 절약 가능 공간이 수십 GB로 나타나 앱으로 발전시킴
  • 앱은 무료로 내려받아 스캔하고 절약 가능 공간을 확인할 수 있지만, 실제 공간 회수에는 유료 결제가 필요함
  • 자신이 만들거나 소유하지 않은 파일을 수정하므로 위험이 크지만, 그만큼 많은 Mac 사용자에게 실질적인 도움을 줄 수 있음

APFS 클론으로 파일을 남긴 채 공간 회수

  • Hyperspace는 같은 내용을 가진 파일들이 서로 클론 관계가 아닐 때, 이를 APFS 클론으로 바꿔 디스크의 데이터 사본을 하나만 공유하게 함
  • 기존 Mac 중복 파일 앱들은 대개 중복 파일을 찾아 삭제해 공간을 절약하지만, Hyperspace는 파일을 제거하지 않고 공간을 회수함
  • APFS의 핵심 기능에는 시점 스냅샷과 copy-on-write 클론이 있음
    • 스냅샷은 Time Machine 백업을 더 안정적이고 효율적으로 만드는 데 쓰임
    • copy-on-write 클론은 디렉터리 엔트리와 파일 내용 사이의 유연한 구조를 기반으로 함
  • Finder에서 “Duplicate” 명령으로 파일을 복제하면 실제 파일 내용은 복사되지 않고, 원본과 데이터를 공유하는 클론 파일이 만들어짐
    • 이 구조 덕분에 파일 크기와 관계없이 Finder의 복제가 거의 즉시 끝남

출시 방식과 개발 메모

  • 개발자는 2024년 말 자신의 Mac 디스크가 차오르자, 같은 내용을 가진 비클론 파일을 찾아 클론으로 바꾸는 Perl 스크립트를 작성함
    • 이 스크립트는 C로 작성된 명령줄 도구와 Swift로 작성된 명령줄 도구를 호출함
    • 실행 결과 절약 가능 공간이 수십 GB로 나타났고, 이후 Mac 앱으로 발전함
  • Hyperspace는 Mac App Store에서 제공됨
  • 개발 측면에서 Hyperspace는 개발자의 두 번째 SwiftUI Mac 앱이며, SwiftUI life cycle을 사용한 첫 앱임
    • Swift 6을 사용한 두 번째 앱이기도 함
    • 개발 초기부터 Swift 6을 쓰는 편이 이미 출시된 앱을 Swift 6으로 전환하는 것보다 훨씬 쉬웠음
    • Swift 6에는 아직 거친 부분이 많아 향후 개선을 기대하고 있음
  • Hyperspace는 HFS+에서 APFS로 변환하는 과정처럼 앱이 만들지 않았고 소유하지도 않은 파일을 수정함
    • 개발자는 이를 자신이 만든 앱 중 가장 위험한 앱이라고 부름
    • 동시에 가장 많은 사람에게 유용할 수 있는 앱일 수 있다고 봄

댓글과 토론

Hacker News 의견들
  • 예전에 같은 일을 하는 명령줄 유틸리티 dedup 을 만들었음
    드라이런 모드가 있고, “지능적으로” 최적의 클론 원본을 고르며, 하드 링크와 다른 클론을 이해하고, 메타데이터를 보존하고, HFS 압축 파일도 제대로 처리함
    내 데이터는 아직 망가뜨린 적 없지만, 파일 시스템 도구가 늘 그렇듯 사용은 본인 책임
    0 - https://github.com/ttkb-oss/dedup

    • 스캔만 해보고 중복 제거는 아직 안 해본 상태에서 적어보면, 디스크 이미지·프로그램 바이너리·중간 표현을 다루는 7.6GB 작업공간 기준으로 차이가 꽤 보임
      Hyperspace는 기본적으로 모든 파일을 스캔하지 않고 허용 목록에 있는 파일만 보며, 파일 내용을 이해해야 하는 구조처럼 보임. 최종 사용자 입장에서는 Text 파일과 Source Code 파일의 차이를 어떻게 구분하는지 모르겠고, 기본 스캔은 360MB만 중복 제거 대상으로 찾았지만 모든 파일을 허용하니 842MB로 늘어남
      기본적으로 100KB 미만 파일도 스캔하지 않으며, 크기 제한을 끄고 모든 파일을 허용하니 1.1GB까지 늘어남. 전체 파일·크기 제한 없음 조건에서 Hyperspace는 68,874개 중 67,309개를 스캔했고 dedup은 67,426개를 스캔함
      Hyperspace는 29,522개 파일이 중복 제거 가능하다고 했고, dedup은 29,447개를 찾았음. 이미 중복 제거된 파일이 76개라 하나 차이가 나는데, 정확한 원인은 모르겠음
      스캔 시간은 Hyperspace가 약 50초, dedup이 14초였음. Hyperspace는 파일 시스템을 스캔한 뒤 중복 계산을 하고 그 다음 중복 제거를 하는 듯한데, 왜 앞의 두 단계를 같이 하지 않는지 모르겠음. 나는 스캔 중 파일 시스템 메타데이터를 큐에 넣고 병렬로 중복 계산을 시작하도록 했고, 대부분은 fts_read로 디렉터리를 순회할 때 “공짜로” 얻는 크기 정보만으로 불일치를 걸러낼 수 있음
      Hyperspace는 1.1GB 절약 가능하다고 했고, dedup은 1.04GB와 이미 절약된 882MB를 찾았음. Hyperspace를 살 생각은 없어서 실제 중복 제거 시간이나 메타데이터 보존, 이상한 파일 처리까지는 모르지만, dedup은 스캔과 중복 제거를 합쳐 31초 걸렸음
      dedup으로 중복 제거한 뒤에도 Hyperscan은 아직 중복 제거 가능한 파일 2개가 있다고 판단함. Hyperspace도 여러 하드 링크가 걸린 파일, 빈 파일, dedup이 검사하는 여러 예외는 처리하는 듯함. ACL이나 기타 속성 보존은 결제 없이는 테스트할 수 없지만 strings를 보면 처리하는 것으로 보이고, HFS 압축은 까다로운 경계 사례라 Hyperspace 스캔이 어떻게 다루는지는 아직 확인 못 함
    • Postgres 개발 디렉터리처럼 거의 같은 파일이 많은 곳에 돌려보니 약 1.7GB를 절약했음
      프로젝트에 라이선스가 없어서, 가능하다면 원하는 라이선스를 붙여주면 좋겠음. 설치 단계를 조금 개선해보려고 풀 리퀘스트도 만들었음: https://github.com/ttkb-oss/dedup/pull/6
    • 방금 써봤는데 잘 동작함. 실제로 특정 파일 타입, 특히 node_modules 안에 중복이 얼마나 많은지 보고서야 이 기법의 잠재력을 깨달았음
      특정 하위 디렉터리만 봐도 어떤 JS 파일 50개 사본을 하나로 바꾸는 일이 드물지 않았음. 다만 “pre-release”이고 GitHub 별도 적은 편이라, 이런 도구는 버그가 있으면 꽤 무서운 만큼 안정성이 궁금함
    • 문서가 훌륭하고, make가 거의 즉시 끝나는 점도 인상적이었음
    • 대안 목록은 https://news.ycombinator.com/item?id=38113396의 댓글을 보면 됨
      예전에 https://github.com/sahib/rmlint를 썼는데 딱히 불만 없었음
  • “Hyperspace가 다른 모든 앱이나 macOS 자체와 협력해서 파일 교체에 안전한 시점을 조율할 방법도 없고, 해당 파일을 강제로 독점 제어할 방법도 없다”는 부분을 보니, 왜 파일 시스템 자체가 백그라운드에서 비슷한 중복 제거 프로세스를 돌리지 않는지 궁금해짐
    그 정도 추상화 계층이면 이런 안전성 문제를 관리할 수 있을 것 같은데, APFS 내부에서 자동으로 일어나면 어떤 단점이 있을까 싶음

    • ZFS에서는 RAM을 많이 씀. ZFS가 블록 수준에서 처리해서, 새 블록이 쓰일 때 비교할 많은 블록을 추적해야 하기 때문인 듯함
      파일 수준으로 구현하면 자원 사용이 줄 수도 있지만, 구현이 더 단순할지 더 복잡할지는 모르겠음. 또 큰 파일에서 1바이트만 바꿔도 파일 시스템이 그 파일을 다시 복제해야 해서 디스크 활동이 많이 발생한다는 점이 직관적이지 않을 수 있음
    • 블록 저장 장치에서는 이런 일을 압축과 함께 흔히 함. 물론 파일 시스템이 장치로 보내는 블록을 암호화하면 실패함
      이 계층에서 중복 제거를 하면 여러 파일 시스템에 걸쳐 중복 제거할 수 있어 좋음. 예를 들어 같은 운영체제 파일을 가진 시스템이 천 대 있다면 저장 공간을 엄청 아낄 수 있고, 차이는 호스트 키나 호스트명 같은 시스템별 설정 정도인 경우가 많음. 단일 파일 시스템은 이런 공통성을 알아차릴 수 없음
      다만 중복 제거 때문에 사용량이 큰 파일의 복제본 수가 줄어들면 문제가 됨. 앞의 예에서 천 대를 동시에 부팅하면 커널 이미지에 엄청난 I/O 부하가 걸릴 수 있음
    • Linux에서는 이게 btrfs와 XFS에서 쓰는 표준 중복 제거 API
      지정한 범위 집합을 운영체제에 정중히 중복 제거해달라고 요청하면, OS가 범위를 잠그고 실제로 동일한지 검증한 뒤에야 중복 제거를 해줌. 각 범위에서 몇 바이트가 중복 제거됐는지도 필드로 돌려받기 때문에, 사용자 공간 프로그램이 파일을 망칠 방법이 없음
    • ZFS가 실제로 이런 일을 함: https://www.truenas.com/docs/references/zfsdeduplication/
    • Windows Server도 NTFS와 ReFS 볼륨에서 이 기능을 제공함
      Hyper-V VM을 올린 ReFS에서 꽤 많이 썼고 효과가 대단했음. 당시 Windows Server 2016/2019가 섞인 VM들이 대부분이었는데 저장소 사용량을 약 45% 줄였음
  • 스캔은 무료로 해서 이득이 있는지 확인하고, 실제 결과에 대해서만 결제하는 모델이 마음에 듦
    나도 파일을 잘 쌓아두는 편이라 돌려봤더니 7GB 회수 가능으로 나왔지만, 내겐 그 정도면 비용을 낼 만큼은 아니었음. 그래도 이런 도구가 있다는 점은 좋음

    • 팟캐스트에서 이 모델을 설명한 적이 있음. 많은 사용자가 이걸 사서 한 번 실행하고 몇 GB를 아낀 뒤 끝낼 것이기 때문에, 구독은 별로 말이 안 됨
      어차피 한 달에 완전히 같은 중복 파일을 얼마나 많이 실수로 만들겠음?
      실제로 자주 쓸 사람을 위해 구독이나 영구 구매 옵션도 있지만, 많은 사람에게는 제한된 기간 동안 쓸 수 있는 일회성 인앱 결제가 꽤 합리적임. 그리고 언제든 무료로 다시 돌려서, 다시 결제할 만큼 쌓였는지 확인할 수 있음
    • 이런 중복 제거 앱의 가치는 내게 공간 절약보다는 중복 파일이 남지 않는 것에 더 가까움
      엄청난 양을 만들지는 않지만, 일부 또는 전체를 합치면 큰 중복 파일들이 생길 수 있음. 보통 하드 드라이브 복구 결과를 다른 위치의 파일들과 맞춰야 하거나, 지저분한 다운로드 폴더를 정리된 대상 위치와 맞춰야 하는 이상한 상황에서 이런 일이 생김
      예를 들어 Time Machine 백업이 내가 기록이 있는 줄도 몰랐고 오래전에 잃어버렸다고 생각한 파일의 오래된 버전을 밀어냈는데, 디렉터리 이름을 망가뜨리고 내용도 어느 정도 난독화해버린 적이 있음. 숫자 이름의 디렉터리가 수천 개 있고, 그 안에는 보관하고 싶은 파일이 있을 수도 있지만 이미 갖고 있는지, 어디 있는지 전혀 구조화되어 있지 않아 알기 어려움. 반대로 같은 빌드 시스템 텍스트 파일 하나만 들어 있는 경우도 많아서 그런 건 당연히 버릴 수 있음
    • 10년 전만 해도 대부분의 소프트웨어가 이런 식이었던 걸 기억하는 내가 그렇게 늙은 건가 싶음
      사람들이 이미 구독 함정에 너무 익숙해져서 이걸 새로운 모델처럼 느끼는 건가
    • 가격 모델이 정말 마음에 듦
      다른 소프트웨어에서도 이렇게 하기 쉬우면 좋겠지만, 대개 가치를 보기 전까지 학습 곡선이 가로막고 있음
    • 취소를 기억해야 하는 “무료 체험”보다 훨씬 신선함
      팁을 주자면 그런 체험판에는 잠글 수 있는 가상 카드를 쓰면, 취소를 깜빡해도 결제가 막힘. 그런데 웹사이트에서 실제 라이선스 가격을 찾아낸 사람이 있는지 궁금함
  • 앱이 두 파일이 동일한지 알아내는 데 어떤 알고리즘을 쓰는지 궁금함
    해시, 비트 단위 비교 등 흥미로운 방법이 많지만 각자 단점도 있음. 파일이 아주 많을 때 가장 좋은 방법이 뭘까

    • Siracusa가 정확히 뭘 하는지는 모르지만 추측은 가능함
      각 후보 파일마다 다른 후보 파일과 같은지 확인할 “키”가 필요함. 파일이 수백만 개일 수 있으니 키는 작고 빠르게 생성되어야 하지만, 동시에 거짓 양성은 없어야 함
      요즘 obvious한 답은 파일 내용의 SHA256 해시임. 매우 빠르고 크기도 32바이트로 그리 크지 않으며, 거짓 양성이나 충돌 확률은 실제로 마주치기 전에 세상이 끝날 정도로 낮음. SHA256은 이런 용도의 사실상 표준이라, 다른 걸 했다면 꽤 놀랄 듯함
    • https://en.wikipedia.org/wiki/Venti_(software)가 떠오름
      해시를 중복 제거에 쓰는 내용 주소화 파일 시스템이었고, 해시가 쓰기 시점에 계산되기 때문에 성능 비용이 분산됨
    • 모든 파일의 처음 1024바이트를 해시하고, 충돌이 있을 때만 그다음으로 넘어가겠음
      그러면 큰 파일 전체를 해시할 필요가 없고, 같은 해시를 가진 파일만 추가로 보면 됨
  • Apple은 APFS의 클론 기능을 쓸 수 있도록 -c 플래그를 지원하는 수정된 cp 명령을 함께 제공함

    • 쓰는 cp가 그걸 지원하지 않는다면 Python으로 호출할 수도 있음
      예를 들면 import Foundation; Foundation.NSFileManager.defaultManager().copyItemAtPath_toPath_error_(...) 같은 식임
  • 파일 A가 두 위치에 있고 이걸 실행한 뒤 A_0을 수정하면, A_1도 같이 바뀌는지 아니면 A_0의 새 상태만 실체화되고 A_1은 그대로 남는지 궁금함

  • WWDC 2017에서 Apple이 APFS를 발표하기 전, iOS 10.x 업데이트의 일부로 모든 iPhone을 몰래 APFS로 시험 변환했다가 HFS+로 되돌렸다는 대목이 눈에 띄었음
    파일 시스템 변경이 잘못됐을 때 어떻게 되돌릴 수 있는지 궁금함. 코드를 충분히 검증할 수는 있겠지만, 뭔가 문제가 있으면 철회가 불가능해 보임

    • 기억이 맞다면 HFS+에서 APFS로의 마이그레이션은 데이터 블록을 건드리지 않고도 가능하고, APFS 메타데이터 블록과 슈퍼블록을 병렬로 디스크에 씀
      Apple의 테스트 마이그레이션에서는 APFS 슈퍼블록 생성까지 포함해 전체 마이그레이션을 수행하되, HFS+ 슈퍼블록을 APFS 슈퍼블록으로 영구 교체하는 커밋 직전에서 멈췄음. 되돌릴 때는 생성된 APFS 슈퍼블록과 메타데이터 블록만 “그냥” 정리하면 됐음
    • 발표의 해당 부분을 봤는데 [0], 일관성 테스트로 했다는 것 말고는 세부 내용이 거의 없음
      운영 환경에서 많은 걸 터뜨려본 입장이라, 이렇게 큰 마이그레이션을 실행하라고 하면 버튼을 누를 수 있을지 모르겠음
      [0] https://www.youtube.com/watch?v=IcyaadNy9Jk&t=1670s
    • 상상력이 부족한 것 같음. 이건 Apple만 할 수 있는 왕관의 보석 같은 기술이 아님
      오픈소스 세계에도 ext 파일 시스템을 btrfs로 변환하는 도구가 있고, (1) 되돌릴 수 있으며 (2) btrfs 파일 시스템을 쓰는 동안 원래 ext 파일 시스템도 마운트할 수 있음
  • 이야기로 시작해서 문제로 좁힌 다음, 해결책이 그 문제를 마법처럼 해결하는 모습을 보여줌
    훌륭한 마케팅의 아주 좋은 예임

  • 거대한 NodeJS 프로젝트 폴더에 돌려봤지만 8.1GB 폴더에서 1GB만 절약 가능하다고 나왔음
    사용자 홈 폴더까지 포함해 다시 돌려봤는데, 731K 파일·127K 폴더·2,755개 대상 파일 기준으로 총 1.3GB 절약에 그쳤음. NodeJS 폴더만 돌렸을 때보다 300MB 늘어난 정도임
    System과 Library도 스캔해보려 했지만 권한 문제 때문에 거부됐음. 패키지 관리자로 pnpm을 써서 이미 디스크 사용량이 거의 최적에 가까운 듯함
    아이디어는 멋지지만 현재 가격으로는 정당화하기 어렵고, 한 달에 한 번 정도 백그라운드 프로세스로 돌아가는 형태라면 좋겠음

    • 8.1GB 중 1GB면, 쓰는 공간의 12%가 낭비되고 있었다는 뜻 아닌가? 제대로 읽은 건가 싶음
    • pnpm은 npm의 드롭인 대체를 지향하고, 자동으로 중복 제거를 함
    • macOS에는 봉인된 볼륨이 있어서 System과 Library에서 권한 오류가 보이는 것임
      https://support.apple.com/guide/security/signed-system-volum...
    • 직접 해볼 시간은 없었지만, App Store 스크린샷에 고려할 최소 파일 크기 옵션이 분명히 보임
      비교 버퍼를 줄이려고 도입한 것 같은데, node_modules의 파일들이 이 크기 아래로 들어가서 고려되지 않았을 가능성이 있음
    • 가격이 얼마인지 모르겠음. 어디에도 공개되어 있지 않은 듯함
  • 같은 내용을 가진 파일을 하드 링크로 바꾸는 비슷하지만 더 단순한 스크립트를 만든 적이 있음
    주된 동기는 Python 가상 환경의 패키지였음. 비슷한 패키지를 설치해두는 경우가 많고, 버전이 달라도 많은 파일은 여전히 일치함. Numpy, PyTorch, TensorFlow 같은 일부 패키지는 꽤 거대해서 디스크 공간을 상당히 아낄 수 있었음
    https://github.com/albertz/system-tools/blob/master/bin/merg...

    • 이건 하드 링크나 심볼릭 링크를 쓰는 게 아니라, 파일 시스템의 쓰기 시 복사 클론 생성 기능을 쓰는 것임 [1]
      [1] https://en.wikipedia.org/wiki/Apple_File_System#Clones
    • uv는 이걸 기본으로 함
      poetry, hatch, pdm 같은 다른 도구들도 그럴 것 같지만, 세부 동작은 덜 써봐서 잘 모름