"화살표 키는 매번 먹혔어. 메뉴 단축키로만 걸어둔 쉼표랑 마침표 조합은 없는 기능처럼 느껴졌고."
키가 자기 처리자를 찾아가는 길
Mac에서 키 입력은 정해진 길을 따라가. 먼저 앱이 설치한 NSEvent 로컬 모니터가 봐. 이어서 메뉴 막대가 메뉴 항목 단축키를 맞춰보기 전에, 창이 performKeyEquivalent(with:)로 뷰 트리를 내려가면서 뷰들에게 키 조합을 처리할 기회를 줘. 그 뒤에야 평범한 키가 keyDown(with:)으로 첫 번째 응답자에게 가. 처리되지 않은 액션은 뷰, 상위 뷰, 창, 창 컨트롤러, 앱, 앱 델리게이트 순으로 리스폰더 체인을 따라 올라가. 대상이 nil인 메뉴 항목은 첫 번째 응답자부터 그 체인을 따라 액션을 올려보내.
이 순서를 알면 가족의 키보드 버그가 풀려. 버그마다 키가 기대와 다른 단계에 도착했던 거거든.
고친 버그 셋, 걸린 단계도 셋
- 메뉴 단축키는 매일 쓰는 입력으로 믿을 게 못 돼. 비디오 플레이어가 프레임 넘기기랑 구간 반복 조합(수정 키 없는
,.[])을 SwiftUI 메뉴 키 조합으로만 걸어뒀어. 이 조합은 먹혔다 안 먹혔다 하고 눈에 보이는 반응도 없었어. 반면 로컬NSEvent모니터가 처리하는 화살표 키는 매번 먹혔지. 그래서 조합 처리를 모니터로 옮겼어. 메뉴 항목은 같은 키를 알려주는 안내용 라벨로 남기고, 모니터가 이벤트를 소비하게 해서 두 번 실행되는 일도 막았어. - 텍스트 필드에서 기본 편집 키가 안 먹힐 수 있어. 파일 작업대의
NSAlert보조 영역에 넣은 텍스트 필드에서 ⌘V, ⌘C, ⌘X, ⌘A가 안 먹혔어. 앱의 편집 메뉴가 그 키를 직접 만든 액션으로 보내고 있었거든. 창은 메뉴보다 먼저 뷰에게 키 조합을 주니까,performKeyEquivalent(with:)에서 그 키를 처리하는NSTextField서브클래스는 메뉴 상태와 상관없이 동작해. - 오버라이드 못 하는 라이브러리 클래스. 서드파티 라이브러리의 터미널 뷰가
keyDown(with:)을open이 아니라public으로 선언해서, 단축키를 추가하려고 서브클래싱하면 컴파일이 안 됐어. 대신 로컬 모니터가 그 뷰가 창의 첫 번째 응답자인지 확인하고, 뷰보다 먼저 조합을 가로채서 처리해.
조용한 함정 둘
셀렉터 이름은 모두가 같이 쓰는 이름 공간이야. NSResponder엔 이미 moveUp(_:), moveDown(_:) 같은 액션이 있어. 그래서 앱 액션에 같은 이름을 붙이면 의도치 않은 오버라이드가 되고, 텍스트 시스템과 화살표 키가 그 액션을 부르게 돼. 입력기는 "타이핑"의 뜻을 바꿔. 한국어 입력 소스가 켜져 있으면 합성한 ASCII 키 입력이 한글로 도착하고, 글자가 확정되기 전엔 조합 중인 글자로 먼저 들어와. 편집기나 터미널의 키보드 동작은 설치된 앱에서 입력기를 켜고 직접 눌러보기 전까진 확인된 게 아니야.