← 모든 글

Mac App Store는 윈도우 매니저의 존재를 허락하지 않습니다. 그래도 저는 출시했습니다.

Apple 개발자 기술 지원(DTS) 엔지니어는 제 앱이 불가능하다고 말했습니다. “샌드박스 앱에서 Accessibility API를 사용하는 것은 불가능합니다… 여기서 앞으로 나아갈 유일한 길은 앱을 직접 배포하는 것입니다.”1 그 앱은 윈도우 타일러였고, Accessibility API는 모든 윈도우 매니저가 딛고 서 있는 토대이며, Mac App Store의 모든 앱은 그것을 금지하는 샌드박스 안에서 실행됩니다. 그런데도 지난주 941 Tiles는 Mac App Store에 출시됐습니다. 메뉴 막대에서 한 번 클릭하면 흩어져 있던 앱의 창이 모두 균등한 그리드로 정렬됩니다. 샌드박스 규칙은 한 번도 휘어지지 않았습니다. 열쇠는 macOS 안에 이미 Apple의 온전한 승인을 받은 특권적인 창 이동 주체가 존재하고, 그것이 JSON을 받는다는 사실을 알아차린 것이었습니다.

TL;DR

  • App Sandbox는 앱 간 Accessibility 호출을 전면적으로 차단하며, 이를 풀어 주는 entitlement는 없고, Mac App Store의 모든 앱은 샌드박스가 필수입니다. 윈도우 매니저라는 범주는 2012년 6월 이후 신규 진입자에게 사실상 닫혀 있었습니다. 지금도 스토어에 남아 있는 타일러들은 의무화 이전에 등록된 것이며 영원히 샌드박스 없이 실행됩니다.134
  • 허용된 문. 샌드박스 앱도 Apple events를 통해 Apple의 Shortcuts 엔진을 구동할 수 있고, Shortcuts에는 다른 앱의 창을 Apple 자신의 권한으로 옮기는 Find Windows / Move Window / Resize Window 액션이 들어 있습니다.25 제 앱은 각 창이 어디로 가야 하는지를 계산할 뿐이고, 실제로 옮기는 일은 동반 단축어가 합니다. 허가가 아니라 위임입니다.
  • 이것을 현실로 만든 엔지니어링. 동반 .shortcut 파일을 WFWorkflowActions의 바이너리 plist로 프로그램에서 생성하기, 창의 z 순서에 맞춘 JSON 프레임 페이로드 규약, 유령 창과 인덱스 초과를 막는 가드레일, 그리고 Developer ID 빌드에서는 직접 Accessibility를, 스토어 빌드에서는 Shortcuts 위임을 쓰는 이중 엔진 코드베이스입니다.
  • 스토어 빌드 5개가 실패했던 그 기계에서의 첫 성공 실행. 흩어져 있던 터미널 창 4개가 0.6초 만에 정확한 사분면 프레임으로 들어갔습니다.
  • 공은 마땅한 곳에. 이 메커니즘은 snApp(오픈 소스, 2023년)과 Align(2024년)이 실제로 입증했습니다. “불가능”을 구현 계획으로 바꾼 것은 snApp의 소스를 읽은 일이었습니다.2

벽

941 Tiles의 첫 버전은 전형적인 메뉴 막대 타일러였습니다. macOS에는 Accessibility라는 체계가 있어, 사용자의 허가가 있으면 한 앱이 다른 앱의 창을 열거하고 옮길 수 있습니다(창의 위치와 크기 속성에 대한 AXUIElementSetAttributeValue). 그 위에 올린 앱은 몇 주간의 개발 내내 흠 없이 동작했습니다. App Store Connect 등록 정보도 전부 준비했습니다. 17개 언어의 메타데이터, 스크린샷, 가격, 업로드한 빌드 21개.

그런 다음 TestFlight 빌드를 두 번째 Mac에 올렸습니다. Accessibility 권한은 부여됐고 앱도 접근이 켜져 있다고 보고했지만, 읽어 낸 창은 0개였습니다.

버그가 아니었습니다. Mac App Store의 모든 앱은 Apple의 App Sandbox 안에서 실행됩니다. 이는 앱이 자기 것만 건드릴 수 있게 하는 격리 규칙이며, 바로 Mac의 나머지 부분을 엿보거나 방해하지 못하게 하려는 것입니다.4 다른 앱의 창에 손을 뻗는 행위야말로 샌드박스가 막으려고 존재하는 대상입니다. 개발 빌드는 샌드박스 없이 실행되기 때문에 몇 주 동안 모든 것이 잘 돌아갔던 것이고, 스토어 빌드는 제가 무시해 온 규칙을 그대로 적용했을 뿐입니다. 어떤 권한 대화상자도, entitlement도, 설정도 이를 풀어 주지 않습니다. Apple에 직접 물었을 때 돌아온 DTS의 답이 이 글 맨 앞에 있는 그 문장입니다. 스토어 밖에서 배포하라는 것이었습니다.1

2012년부터 닫힌 범주

이 벽이 흥미로운 지점은 그 반대편에 누가 서 있느냐입니다. Magnet, Moom, BetterSnapTool. 오늘도 Mac App Store에서 살 수 있는 창 관리 도구들입니다. 이들의 등록 정보는 2012년 6월 샌드박스 의무화보다 앞서기 때문에, 다른 누구에게도 이전되지 않는 유예 조항 아래 샌드박스 없이 실행됩니다. 2012년 이후에 태어난 윈도우 매니저는 모두(Rectangle, Swish, Loop, BetterTouchTool, Amethyst) 스토어 밖에서 배포됩니다. Apple은 이 선을 반대 방향으로도 집행했습니다. 거의 13년간 스토어에 있던 PopClip은 Accessibility 기반의 새 기능을 샌드박스 업데이트로는 출시할 수 없다는 심사 통보를 받고 2024년 3월에 스토어를 떠났습니다.3

제 생각은 분명히 밝혀 두겠습니다. 샌드박스 규칙은 합리적입니다. 화면의 모든 창을 소리 없이 읽고 옮길 수 있는 앱은 사용자를 감시할 수 있는 앱이며, “저를 믿으세요”는 보안 모델이 아닙니다. 스토어의 약속은 거기서 설치한 어떤 것도 그런 일을 할 수 없다는 것입니다. 불공정함은 좁고 구체적입니다. 기존 사업자는 신규 진입자가 아무리 많은 사용자 동의를 받아도 얻을 수 없는 능력을 계속 보유합니다. 이 범주는 금지된 것이 아니라 화석이 된 것입니다.

전환점

DTS의 판정에는 결함이 하나 있었습니다. 스토어에는 이미 반례가 있었다는 점입니다. Align(2024년)과 snApp(2023년)은 둘 다 창을 타일 배치하고, 둘 다 Mac App Store에 있으며, 둘 다 2012년보다 한참 뒤에 태어났습니다. snApp은 오픈 소스이므로 이 메커니즘은 비밀이 아닙니다. 저장소로 공개돼 있습니다.2

샌드박스 앱은 다른 앱의 창을 절대 건드리지 않습니다. 대신 Apple 자신의 자동화 앱인 Shortcuts를, 선언한 두 개의 entitlement를 통해 구동합니다.

<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.temporary-exception.apple-events</key>
<array>
  <string>com.apple.shortcuts.events</string>
</array>

Shortcuts는 macOS 12부터 Find Windows, Move Window, Resize Window라는 이름의 액션을 제공합니다.5 Shortcuts가 창을 옮길 수 있는 이유는 그것이 Apple의 것이기 때문입니다. 그래서 앱은 각 창이 어디로 가야 하는지를 계산하고, 그 계획을 사용자가 설치한 동반 단축어에 넘기며, 실제 이동은 Apple 자신의 엔진이 수행합니다. 샌드박스는 결코 침해되지 않습니다. 샌드박스 프로세스가 특권적인 행위를 직접 수행하지 않기 때문입니다. 허가가 아니라 위임입니다.

이 구조에는 거의 건축적이라 할 만한 면이 있습니다. 보안 경계는 Apple이 그은 자리에 그대로 남아 있고, 능력 쪽이 시스템에서 그것을 정당하게 보유한 단 하나의 프로세스를 거쳐 경계를 넘어갑니다.

구현 과정

메커니즘을 아는 것과 그것을 출시하는 것은 서로 다른 주의 일입니다. 이것을 실제 제품 수준으로 만든 조각들은 다음과 같습니다.

단축어를 프로그램으로 생성하기. 사용자가 12개 액션짜리 워크플로를 손으로 조립해서는 안 됩니다. 동반 .shortcut 파일은 WFWorkflowActions의 바이너리 plist이며, 앱이 빌드 시점에 생성하고 shortcuts sign -m anyone으로 서명합니다. 덕분에 설치는 Shortcuts 앱에서 한 번 클릭으로 끝납니다. 런타임 규약은 ScriptingBridge의 shortcut.run(withInput:)을 통해 전달되는 JSON 페이로드입니다.

{"app": "Ghostty", "frames": [{"x": 0, "y": 38, "w": 1280, "h": 771}, ...]}

창의 정체성은 z 순서입니다. 단축어의 Find Windows 액션은 창을 앞에서 뒤로 색인하고, CGWindowList도 같은 순서로 열거합니다. 그래서 페이로드의 슬롯 i는 그대로 창 i의 프레임을 나릅니다. 위치 순위대로 셀을 배정하면 그리드는 “창은 원래 있던 자리 근처에 머문다”를 재현합니다. 창 ID는 경계를 넘지 않습니다. 반대편에는 애초에 존재하지 않기 때문입니다.

가드레일. Count로 상한을 잡은 Items in Range 클램프는 오래된 페이로드가 현재 창 목록의 범위를 넘어 색인하는 것을 막습니다. 최소 크기 필터(120×80)는 CGWindowList가 보고하고 예전 Accessibility 역할 검사로 감췄던 유령 대기 패널을 걸러 냅니다. 그리고 놀라웠던 사실 하나. 창을 나열하는 데에는 어떤 권한도 필요 없습니다. CGWindowList는 샌드박스 안에서 합법입니다. 금지된 것은 언제나 이동뿐이었습니다.

하나의 코드베이스, 두 개의 엔진. 앱은 실행 시 APP_SANDBOX_CONTAINER_ID 환경 변수로 샌드박스를 감지합니다. 스토어 빌드는 Shortcuts를 통해 타일 배치하고, Developer ID 빌드와 개발 빌드는 직접 Accessibility 경로를 사용합니다. 같은 UI, 같은 계산, 다른 운반자입니다.

알림 없는 감시 모드. 선택형 자동 재배치 기능은 원래 Accessibility의 창 알림을 구독하지만, 그것 역시 샌드박스가 없앱니다. 대신 윈도우 서버 폴링으로 살아남았습니다. 값싼 CGWindowList 차이를 계산하고, 변화가 있으면 다시 배치합니다.

대가, 솔직하게

사용자는 한 번뿐인 설정 비용을 치릅니다. 동반 단축어를 설치하고(한 번 클릭), 자동화 동의 대화상자 하나를 승인하는 것입니다(“941 Tiles이(가) Shortcuts을(를) 제어하려고 합니다”라는 표준 프롬프트이며, InfoPlist.xcstrings를 통해 17개 언어 전부로 현지화돼 있습니다). 그 뒤의 경험은 직접 API를 쓰는 타일러와 동일합니다. 그리드 아이콘을 클릭하고 앱을 클릭하면 창이 자리를 잡습니다. Shortcuts 실행은 눈에 보이지 않고 1초를 한참 밑도는 시간에 끝납니다.

이 비용이 아무것도 아닌 척하지는 않겠습니다. Magnet이 보여 주는 것보다 화면이 하나 더 많습니다. 동시에 이것은, 아직 신뢰할 이유가 없는 공증 다운로드를 위해 사용자에게 Gatekeeper를 우회하라고 요구하는 대신 샌드박스 안에 존재하기 위해 치르는 값의 전부이기도 합니다.

결실

스토어 빌드 5개가 실패했던 바로 그 Intel Mac에서 설치, 단축어 설정, 동의를 거쳐, 흩어져 있던 Ghostty 창 4개가 첫 실행에 0.6초 만에 정확한 사분면 프레임으로 들어갔습니다. 그다음은 국제화 작업(String Catalogs, 아랍어의 여섯 범주를 모두 포함한 CLDR 복수형 규칙), 현지화한 등록 정보, 그리고 제출이었습니다. 한 번의 심사 거절과 약 30분의 재심사를 거쳐 941 Tiles가 출시됐습니다. 한 번 결제 4.99달러, macOS 15 이상, 17개 언어, 데이터 수집 없음, 네트워크 접근 없음.

일반화되는 것

이 패턴은 창 타일 배치보다 큽니다. 플랫폼이 어떤 능력을 경계 뒤에 가둘 때, 답은 좀처럼 그 경계와 싸우는 것이 아닙니다. 그 능력을 이미 보유한 1차 프로세스가 무엇인지 목록을 만들고, 그곳으로 향하는 공인된 인터페이스를 찾는 것입니다. macOS에서 그 목록은 갈수록 한 곳을 가리킵니다. Shortcuts입니다. 이것은 조용히 시스템의 특권적 자동화 중개자가 되었고, 설계상 샌드박스 안쪽에서 닿을 수 있습니다. 제 Shortcuts 가이드는 그 영역을 깊이 다루고, 941 Tiles 사례 연구는 같은 이야기를 아키텍처 다이어그램과 함께 따라갑니다.

샌드박스는 제 몫을 했습니다. 그래도 앱은 출시됐습니다. 두 문장 모두 참이고, 그 사이에 있던 문은 Apple 자신의 것이었습니다.

자주 묻는 질문

샌드박스 Mac 앱이 Accessibility API를 사용할 수 있습니까?

아닙니다. App Sandbox는 앱 간 Accessibility 호출을 전면 차단하며, 어떤 entitlement도 권한 대화상자도 설정도 이를 풀어 주지 않습니다. Apple의 DTS가 제게 준 답은 이랬습니다. “샌드박스 앱에서 Accessibility API를 사용하는 것은 불가능합니다… 여기서 앞으로 나아갈 유일한 길은 앱을 직접 배포하는 것입니다.” 개발 빌드는 샌드박스 없이 실행되며, 그래서 타일러가 몇 주 동안 잘 동작하다가 스토어 빌드에서는 창을 0개 읽는 일이 생깁니다.14

윈도우 매니저가 어떻게 Mac App Store에 출시될 수 있습니까?

특권적인 행위를 위임함으로써 가능합니다. 샌드박스 앱도 Apple events를 통해 Apple의 Shortcuts 엔진을 구동할 수 있고, Shortcuts는 macOS 12부터 Find Windows, Move Window, Resize Window 액션을 제공해 왔습니다. 제 앱은 각 창이 어디로 가야 하는지를 계산해 그 계획을 사용자가 설치한 동반 단축어에 넘기고, 그 단축어가 Apple 자신의 권한으로 이동을 수행합니다. 샌드박스는 결코 침해되지 않습니다. 샌드박스 프로세스가 특권적인 행위를 수행하지 않기 때문입니다.25

윈도우 매니저가 불가능하다면 Magnet과 Moom은 왜 스토어에 있습니까?

이들의 등록 정보는 2012년 6월 샌드박스 의무화보다 앞서기 때문에, 다른 누구에게도 이전되지 않는 유예 조항 아래 샌드박스 없이 실행됩니다. Rectangle, Swish, Loop, BetterTouchTool, Amethyst를 포함해 2012년 이후에 태어난 윈도우 매니저는 모두 스토어 밖에서 배포됩니다. PopClip은 Accessibility 기반의 새 기능을 샌드박스 업데이트로는 출시할 수 없다는 심사 통보를 받고 2024년 3월에 스토어를 떠났습니다. 이 범주는 금지된 것이 아니라 화석이 된 것입니다.34

Shortcuts 위임은 사용자에게 어떤 대가를 요구합니까?

한 번뿐인 설정 비용입니다. 동반 단축어를 한 번 클릭으로 설치하고, 자동화 동의 대화상자 하나를 승인합니다. 그 뒤의 경험은 직접 API를 쓰는 타일러와 동일합니다. 그리드 아이콘을 클릭하고 앱을 클릭하면 창이 자리를 잡으며, Shortcuts 실행은 눈에 보이지 않고 1초를 한참 밑도는 시간에 끝납니다. Magnet보다 화면이 하나 더 많고, 그것이 샌드박스 안에 존재하기 위해 치르는 값의 전부입니다.

하나의 코드베이스가 어떻게 샌드박스 빌드와 직접 빌드를 모두 지원합니까?

앱은 실행 시 APP_SANDBOX_CONTAINER_ID 환경 변수로 샌드박스를 감지합니다. 스토어 빌드는 Shortcuts를 통해 타일 배치하고, Developer ID 빌드와 개발 빌드는 직접 Accessibility 경로를 사용합니다. 같은 UI, 같은 계산, 다른 운반자입니다. 감시 모드도 마찬가지로 적응합니다. 샌드박스가 Accessibility의 창 알림을 없애기 때문에, 선택형 자동 재배치는 차이를 계산하고 변화가 있으면 다시 배치하는 값싼 CGWindowList 폴링으로 살아남습니다.

참고 자료


  1. Apple Developer Forums, 스레드 805556(2025년 10월), Apple DTS의 답변. “샌드박스 앱에서 Accessibility API를 사용하는 것은 불가능합니다… 여기서 앞으로 나아갈 유일한 길은 Developer ID 서명을 사용해 앱을 직접 배포하는 것입니다.” ↩↩↩↩

  2. Om Chachad, GitHub의 snApp(2023년). Mac App Store에 있는 오픈 소스 윈도우 타일러로, 그 소스가 Shortcuts 위임 메커니즘을 기록하고 있습니다. com.apple.security.automation.apple-events 스크립팅 대상 entitlement와 Apple의 창 액션으로 조립한 동반 단축어입니다. ↩↩↩↩

  3. Michael Tsai, “PopClip Leaving the Mac App Store”(2024년 3월 21일). 거의 13년간 스토어에 있던 앱에 대해 Apple 심사가 샌드박스 업데이트에서 Accessibility 기반의 새 기능을 제거하도록 요구한 일에 관하여. ↩↩↩

  4. Apple, App Sandbox 문서. 2012년 6월 이후 Mac App Store의 모든 앱이 채택하도록 요구된 격리 모델입니다. ↩↩↩↩

  5. Apple, Mac용 Shortcuts 사용 설명서. macOS 12부터 사용할 수 있는 Find Windows, Move Window, Resize Window 액션을 설명합니다. ↩↩↩

관련 게시물

The Shader Gallery That Lied: Debugging 216 WebGL Presets

A user said the shader playground looked broken. Pixel-readback testing found 30 dead presets, 11 that never compiled, a…

13 분 소요

브라우저에서 MacPaint 되살리기: 1984년 소스 코드가 곧 명세입니다

MacPaint 1.3을 브라우저에서 다시 만들고, 모든 도구를 Bill Atkinson이 공개한 Pascal 소스와 대조해 검증했습니다. 소프트웨어를 충실하게 재현하려면 무엇이 필요한가.

12 분 소요

엔지니어링 철학: Joanna Rutkowska

Joanna Rutkowska는 합리적 편집증 위에 Qubes OS를 세웠습니다. 소프트웨어를 버그 없게 만들 수는 없으니, 침해를 전제하고, 인프라를 불신하며, 피해 범위를 격리하라는 것입니다.

19 분 소요