macOS 클립보드 관리자, Swift·Tauri·Electron 중 무엇으로 만들까?
클립보드 관리자를 만들겠다고 하면 첫 질문은 대개 "UI를 뭘로 짤까"입니다. React가 익숙하면 Electron이나 Tauri, 네이티브가 좋으면 SwiftUI를 떠올립니다. 그런데 이 앱의 화면은 검색창 하나와 목록 하나가 거의 전부입니다. 나머지는 운영체제와 주고받는 일입니다.
흔히 데스크톱 프레임워크 선택을 "화면을 어떤 기술로 그릴 것인가"의 문제로 이해합니다. 클립보드 관리자에서는 이 질문이 거의 아무것도 정하지 않습니다. 실제로 갈리는 것은 어느 프레임워크를 고르든 결국 네이티브 코드로 짜야 하는 부분이 얼마나 되는가입니다. 아직 만들기 전 단계이고, 이 글은 고르기 전에 확인한 사실과 그에 대한 제 판단입니다.
복사했다고 알려 주는 이벤트가 없는 구조
macOS의 NSPasteboard에는 내용이 바뀌었다는 알림이 없습니다. 대신 changeCount라는 정수가 있고, Apple 문서는 이 값이 pasteboard 소유권이 바뀔 때마다 증가한다고만 적습니다. 알림이 없으니 클립보드 관리자는 이 숫자를 주기적으로 읽어 달라졌는지 봅니다. 폴링 간격이 곧 "복사했는데 목록에 늦게 뜬다"는 체감 지연이 됩니다.
macOS 15.4부터는 고려할 것이 하나 더 붙습니다. NSPasteboard.AccessBehavior의 기본값은 앱이 일반 pasteboard를 프로그램으로 읽을 때 사용자에게 묻는 것입니다. 사용자는 시스템 설정에서 항상 허용, 묻기, 항상 거부 중 하나를 고릅니다. 클립보드를 읽는 것이 본업인 앱이라면 첫 실행 안내에서 반드시 다뤄야 할 주제입니다. changeCount만 읽어도 이 확인이 뜨는지는 아직 확인하지 못했습니다. 만들기 시작하면 가장 먼저 확인할 부분입니다.
Electron과 Tauri도 사정은 같습니다. 두 쪽 모두 클립보드 변경 이벤트가 없어서 폴링해야 합니다.
비밀번호를 기록하지 않겠다는 약속, nspasteboard.org 표식
클립보드 관리자가 가장 조심해야 하는 것은 비밀번호 관리자에서 복사한 값입니다. 이를 위한 관례가 nspasteboard.org에 정리돼 있습니다. 복사하는 앱이 내용 옆에 표식 타입을 함께 올려 두면, 기록하는 앱이 그것을 보고 건너뛰는 방식입니다.
| 표식 | 뜻 |
|---|---|
org.nspasteboard.ConcealedType |
기밀로 다뤄야 하는 내용 |
org.nspasteboard.TransientType |
잠깐만 올라가 있을 내용 |
org.nspasteboard.AutoGeneratedType |
사용자가 복사할 의도 없이 앱이 만든 내용 |
org.nspasteboard.source |
내용을 올린 앱의 번들 ID |
같은 페이지에는 1Password의 com.agilebits.onepassword, Handoff의 com.apple.is-remote-clipboard 같은 예전 고유 타입도 나옵니다. 이 표식을 확인하려면 pasteboard에 올라온 임의의 타입 이름을 조회할 수 있어야 합니다. 프레임워크를 비교할 때 이 조건이 생각보다 큰 차이를 만듭니다.
포커스를 뺏지 않는 창과 ⌘V 전송
목록에서 항목을 고르면 원래 쓰던 앱에 바로 붙여 넣어져야 합니다. 여기에는 두 가지가 필요합니다.
먼저 목록 창이 떠도 원래 앱이 활성 상태로 남아 있어야 합니다. AppKit에서는 NSPanel에 .nonactivatingPanel 스타일을 주면 소유한 앱을 활성화하지 않는 패널이 됩니다. 이게 안 되면 창을 띄우는 순간 붙여 넣을 대상이 바뀌어 버립니다.
다음으로 대상 앱에 ⌘V 키 이벤트를 보내야 합니다. CGEvent로 키 이벤트를 만들어 보내고, 권한은 macOS 10.15부터 있는 CGRequestPostEventAccess로 요청합니다. 시스템 설정에서는 "손쉬운 사용" 항목에 나타나지만, Apple DTS의 설명에 따르면 이벤트를 보내는 데만 한정된 별도 권한입니다.
App Store 배포가 갈리는 조건
자동 붙여넣기 때문에 클립보드 관리자는 Mac App Store에 낼 수 없다는 말이 종종 보입니다. 반만 맞는 말입니다.
DTS는 이벤트를 보내는 권한이 App Sandbox와 함께 쓸 수 있는 권한이라고 답했습니다. 반면 포커스된 입력란을 읽는 식의 일반 Accessibility API는 샌드박스 앱에서 쓸 수 없습니다. 그리고 이 권한을 쓰는 샌드박스 앱이 심사를 통과할지는 DTS가 아니라 App Review가 정한다고 선을 그었습니다.
Paste와 Maccy는 둘 다 App Store에서 팝니다. Paste의 도움말은 바로 붙여넣기에 손쉬운 사용 권한이 필요하다고 적지만, App Store판과 직접 배포판을 나눠 설명하지는 않습니다. Maccy의 App Store 설명에는 자동 붙여넣기 언급이 없습니다. 그러니 "App Store판에서도 된다"는 것은 문서로 확인된 사실이 아니라 추정입니다.
App Store 밖으로 내면 Developer ID 인증서로 서명하고 공증을 받아야 Gatekeeper를 통과합니다. 어느 쪽이든 배포 쪽 일이라 프레임워크와는 무관합니다. 저라면 직접 배포로 먼저 내고 App Store는 나중에 따로 따지겠습니다.
프레임워크마다 네이티브로 남는 코드
위의 조건을 프레임워크별로 놓으면 이렇습니다.
| 필요한 것 | Swift (AppKit + SwiftUI) | Tauri 2 | Electron |
|---|---|---|---|
| 변경 감지 | changeCount 폴링 | Rust에서 폴링 | 폴링 |
| 임의 타입 읽기 (표식 포함) | NSPasteboard 그대로 | 공식 플러그인으로는 불가, Rust로 직접 | 44부터 application/osclipboard 형식으로 가능 |
| 포커스 안 뺏는 패널 | NSPanel | Rust/Objective-C로 직접 | type: 'panel' (스타일 마스크를 런타임에 덧붙임) |
| ⌘V 전송 | CGEvent | Rust로 직접 | 네이티브 모듈 필요 |
| 웹뷰 | 없음 | 시스템 WKWebView | Chromium 내장 |
Tauri 쪽에서 눈여겨볼 것은 공식 clipboard-manager 플러그인의 범위입니다. 함수가 readText, writeText, readImage, writeImage, writeHtml, clear 여섯 개뿐이고, HTML은 쓰기만 됩니다. 문서 주석에도 HTML은 문자열로만 읽을 수 있어서 readHtml이 없다고 적혀 있습니다. 임의의 타입을 읽는 길이 없으니 ConcealedType 표식도 이 플러그인으로는 볼 수 없습니다. 결국 클립보드 앱의 핵심은 Rust에서 macOS API를 직접 부르는 코드가 됩니다. 전역 단축키는 공식 플러그인이, 메뉴바 아이콘은 코어의 트레이 기능이 맡아서 이 부분은 편합니다.
Electron은 최근에 달라졌습니다. Electron 44에서 clipboard 모듈이 W3C Clipboard API 모양으로 다시 짜이면서 readBuffer와 availableFormats가 빠졌습니다. 운영체제 원본 형식은 이제 electron application/osclipboard;format="<이름>"이라는 사용자 정의 형식으로 읽습니다. 렌더러 프로세스에서는 더 이상 쓸 수 없고 메인 프로세스 전용입니다. 예전 글의 예제 코드는 44 이상에서 그대로 돌지 않습니다.
크기에 대한 공식 숫자는 Tauri 쪽에만 있습니다. Tauri는 최소 앱이 600KB 미만일 수 있다고 적고, Electron 문서는 Chromium과 Node.js를 바이너리에 넣는다고만 적습니다. 항상 떠 있는 앱이라 유휴 메모리가 중요한데, 이건 각자 빌드해서 재야 할 숫자입니다.
화면 기술이 정하지 못하는 것
표에서 "직접"이라는 말이 없는 열은 Swift뿐입니다. Tauri를 고르면 React로 목록을 그리는 대신, 같은 macOS API를 Rust에서 부르는 코드를 씁니다. 네이티브 코드를 피하려고 고른 프레임워크에서 가장 어려운 부분이 네이티브로 남는 셈입니다.
그래서 제 기준은 하나입니다. Windows판을 낼 계획이 있는가. macOS만이라면 Swift가 맞다고 봅니다. 운영체제 쪽 일이 전부 1급 API이고, React를 쓸 수 있다는 이점은 검색창과 목록 정도의 화면에서는 크지 않습니다. Windows까지 낸다면 Tauri를 고르겠습니다. 화면과 히스토리 저장·검색은 한 벌로 두고, 감지·패널·붙여넣기만 운영체제별로 짜는 구조가 됩니다. Electron은 44의 clipboard 개편으로 임의 형식을 다시 읽을 수 있게 됐지만, 항상 떠 있는 작은 앱에 Chromium을 싣는 비용을 정당화할 이유를 저는 아직 찾지 못했습니다.
확실하지 않은 것은 두 가지입니다. macOS 15.4의 pasteboard 접근 확인이 changeCount 폴링에도 걸리는지, 그리고 App Store판의 자동 붙여넣기가 심사를 통과하는지입니다. 둘 다 프레임워크와는 무관하게 첫 주에 확인해야 할 일입니다.
함께 읽기
- GitHub 개인 계정과 조직: 어떤 리포를 조직으로 옮길까?기능 모듈을 패키지로 내려다가 리포 하나를 개인 계정에서 조직으로 옮겼습니다. 옮기기 직전까지 저는 이 일이 귀찮을 거라고 생각했습니다. 러너를 다시 붙이고, 시크릿을 다시 넣고, 문서마다 주소를 고쳐야 할 것 같았습니다. 실제로는 API 호출 한 번과 원격 주소 한 줄로 끝났습니다. 그 과정에서 어떤 리포를 조직에 두…
- 새 맥북 개발 환경 세팅: git clone이 가져오지 않는 것들맥북을 새로 받고 하던 프로젝트를 이어서 하려 했습니다. 리포를 클론하고 npm install 을 돌린 다음 개발 서버를 띄웠는데, 브라우저에 이게 떴습니다.
- xcode-select와 DEVELOPER_DIR: 어느 쪽이 정석일까?Expo 앱을 iOS 시뮬레이터에 띄우려고 도구부터 점검했는데, 결과가 앞뒤가 안 맞았습니다.
- Sentry 란 무엇인가: 에러 수집 도구가 하는 일과 대안 비교헬스체크는 200 을 돌려주는데 화면은 500 입니다. 컨테이너는 떠 있고, 워치독은 조용하고, 서버 로그에는 같은 스택 트레이스가 수백 줄 흩어져 있습니다. 어느 배포부터 시작됐는지, 몇 명이 겪었는지, 지금도 나고 있는지는 로그를 아무리 봐도 안 나옵니다.
- cmux와 Orca 비교: 터미널을 늘릴 것인가, 시도를 늘릴 것인가두 앱의 소개 문구는 거의 같습니다. 코딩 에이전트를 여러 개 동시에 돌린다는 것입니다. 그런데 cmux는 Swift와 AppKit으로 짠 macOS 전용 앱이고, Orca는 Electron으로 짜서 Windows와 Linux, 심지어 휴대폰까지 갑니다. 라이선스도 GPL-3.0과 MIT로 갈립니다.