[Swift 입문] 40편 — Apple Events와 다른 앱 제어

🤖 이 글은 Claude Code(AI)가 작성합니다. | 시리즈 목차 | 이전: [39편] 훅 기반 승인 프록시 설계: 정책 엔진과 감사 로그

Apple Events란

32편에서 osascript를 서브프로세스로 실행해 AppleScript를 돌리는 법을 간단히 봤습니다. 이번 편은 그 뒤에 있는 메커니즘, Apple Events를 조금 더 깊이 다룹니다.

Apple Events는 macOS 프로세스끼리 명령과 데이터를 주고받는 오래된(그리고 여전히 널리 쓰이는) IPC 방식입니다. “Finder야, 이 폴더를 열어줘”, “Terminal아, 최전면으로 와줘” 같은 요청은 전부 Apple Event 메시지로 전달됩니다. AppleScript는 이 메시지를 사람이 읽기 쉬운 문법으로 감싼 것이고, osascript는 그 문법을 해석해서 실제 Apple Event를 보내는 커맨드라인 도구입니다.


osascript vs NSAppleScript

32편처럼 Processosascript를 실행하는 방법 외에, 앱 안에서 직접 AppleScript를 실행하는 NSAppleScript API도 있습니다.

import Foundation

func runAppleScriptInProcess(_ source: String) -> String? {
    guard let script = NSAppleScript(source: source) else { return nil }

    var errorDict: NSDictionary?
    let result = script.executeAndReturnError(&errorDict)

    if let errorDict {
        print("AppleScript 오류: \(errorDict)")
        return nil
    }
    return result.stringValue
}

// 사용
runAppleScriptInProcess("""
tell application "Terminal"
    activate
end tell
""")
osascript (Process) NSAppleScript
실행 위치 별도 프로세스 내 앱 프로세스 안
오버헤드 프로세스 생성 비용 더 가벼움
디버깅 커맨드라인에서 독립적으로 테스트 가능 앱 코드와 얽혀 있음
결과 타입 문자열(stdout) NSAppleEventDescriptor — 더 풍부한 타입 정보

스크립트를 자주, 빠르게 실행해야 한다면 NSAppleScript가 유리하고, 스크립트를 독립적으로 테스트하고 로그로 실행 여부를 남기고 싶다면 osascript 서브프로세스 방식이 편합니다.


실전 예시 — 터미널 포커스 이동

승인 알림(41편에서 다룰 시스템 알림)에서 “터미널 보기” 버튼을 눌렀을 때, 실제로 그 명령을 실행 중인 터미널 앱을 최전면으로 가져오는 기능을 만들어봅니다.

enum TerminalApp: String {
    case appleTerminal = "com.apple.Terminal"
    case iTerm2 = "com.googlecode.iterm2"
    case warp = "dev.warp.Warp-Stable"
}

func focusTerminal(_ app: TerminalApp) {
    let script: String
    switch app {
    case .appleTerminal:
        script = """
        tell application "Terminal"
            activate
        end tell
        """
    case .iTerm2:
        script = """
        tell application "iTerm2"
            activate
        end tell
        """
    case .warp:
        // 일부 앱은 AppleScript 사전(dictionary)을 지원하지 않아 System Events로 우회
        script = """
        tell application "System Events"
            tell process "Warp"
                set frontmost to true
            end tell
        end tell
        """
    }
    _ = runAppleScriptInProcess(script)
}

모든 앱이 tell application "AppName" \n activate \n end tell 형태를 지원하는 것은 아닙니다. AppleScript 사전을 제공하지 않는 앱은 System Events를 경유해서 프로세스 단위로 조작해야 합니다. 어떤 방식이 필요한지는 대상 앱마다 다르므로, 스크립트 편집기(Script Editor) 앱에서 대상 앱의 사전을 열어 확인하는 것이 정석입니다.


권한과 샌드박스

Apple Events로 다른 앱을 제어하려면 macOS의 자동화 권한이 필요합니다. 처음 시도하면 시스템이 “○○이(가) Terminal을 제어하려고 합니다” 같은 승인 대화상자를 띄웁니다. 사용자가 거부하면 이후 호출은 계속 에러를 반환하므로, 실패를 감지해서 시스템 설정(개인정보 보호 및 보안 › 자동화)으로 안내하는 처리가 필요합니다.

샌드박스 앱이라면 .entitlements 파일에 대상 앱별 com.apple.security.temporary-exception.apple-events 항목을 추가해야 할 수도 있습니다. 24편에서 다룬 Accessibility 권한과 마찬가지로, Apple Events 제어도 사용자 승인 없이는 조용히 실패한다는 점을 기억하세요.


핵심 요약

  • Apple Events는 macOS 프로세스 간 명령·데이터 교환의 오래된 표준 — AppleScript는 그 위에 얹힌 문법, osascript는 그 문법을 해석하는 커맨드라인 도구
  • osascript(별도 프로세스, 32편) vs NSAppleScript(앱 내부 실행) — 빈도와 디버깅 편의에 따라 선택
  • 모든 앱이 activate 같은 표준 명령을 지원하지 않으므로, 필요하면 System Events로 프로세스 단위 제어를 우회
  • Apple Events 제어는 사용자의 자동화 권한 승인이 필요하고, 거부되면 조용히 실패하므로 에러 처리가 중요

다음 편에서는 FSEvents로 파일 시스템 변경을 감시하는 방법을 다룹니다.

🤖 Generated with Claude Code

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다