
Mac 앱이 마이크 권한을 받고도 실행할 때마다 잊어버리는 이유
GeekBye가 마이크 권한을 요청했고, 당신은 허용했고, 잘 작동했습니다. 그런데 다음 실행: 사라졌습니다. 게다가 앱은 시스템 설정 → 마이크 목록에 아예 나타나지도 않았습니다. 범인은 사라지는 경로에서 앱을 조용히 실행하던 macOS 보안 기능이었습니다 — 여기 그 진단과 한 번의 프롬프트로 끝나는 해결책이 있습니다.
당신 자신의 눈을 의심하게 만드는 버그가 여기 있습니다. GeekBye를 설치하면 마이크 권한을 요청하고, 당신은 허용을 클릭하고, 받아쓰기가 작동합니다. 좋습니다. 앱을 종료하고 다음 날 아침 다시 열면 — 마이크 권한을 또 요청합니다. 수동으로 고치려고 시스템 설정 → 개인정보 보호 및 보안 → 마이크를 확인하러 가면, GeekBye는 목록에 아예 없습니다. 거부된 것도 아닙니다. 허용된 것도 아닙니다. 그냥 없습니다, 마치 애초에 요청한 적도 없는 것처럼.
각각의 조각은 하나하나 다 올바르게 보였습니다. 프롬프트는 진짜였습니다. 권한은 그 순간에 작동했습니다. 앱은 올바른 사용 목적 문자열과 함께 제대로 서명되고 공증되었습니다. 그런데도 권한 부여는 실행할 때마다 증발했습니다. GeekBye v2.0.6이 이를 고쳤습니다 — 그리고 근본 원인은 macOS가 당신을 보호하기 위해 하는 가장 은밀한 일 중 하나입니다.
앱을 앱 자신으로부터 숨기고 있던 기능
범인은 Gatekeeper 보안 기능인 macOS App Translocation입니다. 앱을 다운로드하고 DMG나 ~/Downloads 폴더에서 곧바로 실행하면 — 아직 "격리(quarantine)"된 어디에서든 — macOS는 실제로 당신이 보는 그 위치에서 앱을 실행하지 않습니다. macOS는 그것을 /private/var/folders/.../AppTranslocation/… 깊숙한 곳의 무작위로 지정된 읽기 전용 경로로 투명하게 복사한 뒤, 그 복사본을 실행합니다. 좋은 방어책입니다: 악성 다운로드가 자기 옆에 있는 파일을 조작하는 것을 막아 줍니다.
하지만 여기서 충돌이 일어납니다. macOS의 권한 시스템(TCC — 누가 당신의 마이크, 카메라, 화면을 쓸 수 있는지 추적하는 그것)은 앱을 경로와 코드 신원으로 식별합니다. 앱이 이동(translocate)되면 그 경로는 무작위이고 일시적입니다. 그래서 당신이 마이크 권한을 부여하면, macOS는 성실하게 그 권한 부여를 기록합니다 — 다음 실행에는 존재하지 않을 경로를 향해서 말입니다. 앱을 다시 열면, macOS는 그것을 다른 무작위 경로로 이동시키고, 기록에 없는 앱을 보고, 다시 요청합니다. 그리고 그 유령 같은 경로는 결코 안정적인 위치가 아니기 때문에, 앱은 시스템 설정 → 마이크에 영구적인 항목을 결코 얻지 못합니다.
앱은 유령에게 권한을 부여하고 있었던 것입니다.
이것이 바로 일부 사람들만 이 문제를 겪은 이유이기도 합니다. 당신의 GeekBye 복사본이 이미 /Applications에 있었다면 — 직접 그리로 드래그했든, 자동 업데이트를 통해 그리로 갔든 — 격리도 없고, 이동도 없고, 안정적인 경로가 있으며, 모든 것이 완벽하게 유지됩니다. 이 버그는 우리에게도, 첫 설치를 지난 누구에게도 보이지 않았습니다 — 그리고 이것이야말로 가장 오래 살아남는 종류의 버그입니다.
해결책: 앱에게 진짜 집을 주기
문제 전체가 불안정한 경로이므로, 해결책은 앱을 안정적인 경로로 옮기는 것입니다. v2.0.6은 GeekBye가 이동된 상태로 실행 중일 때(또는 그냥 /Applications 밖에서 실행 중일 때)를 감지하고, 한 번의 클릭으로 **"Move to Applications"**를 제공합니다 — 번들을 /Applications로 복사하고 거기서 다시 실행하는 macOS 재배치 호출을 사용해서 말입니다. 그 시점부터 앱은 고정된 신원을 갖습니다: 마이크 권한이 유지되고, 화면 녹화 권한이 유지되며, GeekBye는 드디어 당신이 예상하는 곳인 시스템 설정에 나타납니다.
프롬프트는 그 점에서 정중합니다. Move to Applications, Not Now, Don't Ask Again을 제공하며 — 마지막 선택을 기억하므로, 다른 곳에서 실행할 의도적인 이유가 있는 사람을 앱이 결코 성가시게 하지 않습니다. 프롬프트를 아예 보여줄지 말지 를 결정하는 로직은 작고 순수한 함수들로 분리되어 있어(이 빌드가 이동됐는가? /Applications 밖에 있는가? 사용자가 억제했는가?), 실제 격리된 볼륨에서 실제 공증된 빌드를 띄우지 않고도 단위 테스트할 수 있습니다.
같은 릴리스는 권한 경험 자체도 단순화했습니다. GeekBye는 예전에 커스텀으로 만든 앱 내 권한 창을 띄웠습니다 — OS가 이미 잘하는 일을 재현하려는 수백 줄의 UI였죠. v2.0.6은 그것을 삭제하고 네이티브 macOS 권한 프롬프트에 기댔으며, 필요한 권한이 실제로 없을 때만 나타나는 조용하고 비차단적인 배너로 뒷받침했습니다. 코드는 줄고, 다른 모든 Mac 앱이 똑같이 동작하기 때문에 사용자가 이미 알아보는 동작입니다.
내가 가장 자랑스러운 부분: 우리는 믿기 전에 증명했다
이동을 추측 하고 해결책을 출시하기는 쉬웠을 것입니다. 대신, 이 릴리스는 시작 시 진단 기능을 먼저 출시했습니다: 실행 시 GeekBye는 이제 자신의 실행 파일 경로와, 이동된 상태인지, /Applications 안에 있는지, 그리고 현재 마이크 및 화면 권한 상태를 보고합니다. 그 텔레메트리는 그럴듯한 이론을 확인된 이론으로 바꿔 놓았습니다 — 프로덕션 데이터는 영향받은 세션이 실제로 /Applications 밖의 이동된 경로에서 실행 중이었음을, 예측한 그대로 보여 주었습니다.
그 순서가 중요합니다. "프롬프트는 뜨는데 권한이 유지되지 않는다"에는 여러 가능한 설명이 있습니다 — 서명 문제, 누락된 사용 목적 문자열, 자격(entitlement) 문제, TCC 데이터베이스의 특이 동작. 우리는 API 수준의 원인을 배제했고(요청 경로는 명백히 올바랐습니다), 그런 뒤 희망 섞인 해결책을 출시하고 지원 티켓이 멈추기만을 바라는 대신, 실세계 데이터가 신원/경로 계층을 가리키도록 두었습니다.
이 버그가 가르쳐 주는 세 가지
- 프롬프트는 뜨는데 유지되지 않는 권한은 API 문제가 아니라 신원 문제다. 요청 코드가 올바른데도 권한이 계속 사라진다면, 요청을 다시 짜는 것을 멈추세요. OS가 권한을 어떤 경로와 코드 신원에 묶고 있는지 — 그리고 그 신원이 실행 간에 안정적인지 — 를 물으세요.
- 진단 기능을 해결책과 함께(또는 먼저) 출시하라. 몇 개의 필드 — 나는 어디서 실행 중인가, 내 권한 상태는 무엇인가 — 가 근거 있는 추측을 검증된 근본 원인으로 바꿔 주었고, 어떤 사용자가 영향을 받았는지 정확히 알려 주었습니다. 패치하기 전에, 의심 가는 경계에 계측을 하세요.
- 개발자로부터 숨는 버그는 "잘못된" 환경에서 실행되는 버그다. 우리 버그는 모든 개발 머신의
/Applications에 살고 있었고, 그래서 그 버그는 우리에게는 구조적으로 보이지 않으면서 처음 쓰는 사용자를 때렸습니다. 보고된 문제가 재현되지 않을 때, 첫 번째 질문은 사용자가 틀렸는가 가 아니라 어디서 실행되는지가 무엇이 다른가 입니다.
GeekBye v2.0.6은 재배치 프롬프트와 진단 기능을 함께 출시했습니다. 이것이 자리한 더 넓은 신뢰성의 궤적에 대해서는, 버전 2에 실제로 무엇이 필요한가(v2.0.0)와 화면 녹화가 엉뚱한 모니터를 잡는 이유(v2.0.10) — 특정 환경에서만 드러난 또 하나의 버그 — 를 보세요. 바로 옆의 디테일 릴리스에 대해서는, 차분한 소프트웨어: 깜빡임 수정과 답변 모드 칩(v2.0.3 + v2.0.5)을 보세요.