Cua Driver: Windows와 Linux 데스크톱 백그라운드 컴퓨터 사용 프로젝트 (feat. Claude Code, MCP)

Cua Driver 소개

코딩 에이전트나 범용 에이전트에게 "컴퓨터를 직접 쓰게" 하면 작업의 폭이 크게 넓어집니다. 코드를 고치고, 앱을 실행하고, 실제 창을 읽고, 버튼을 누르고, 자기가 만든 결과를 스스로 검증하는 컴퓨터 유즈(Computer-Use) 의 순환이 만들어지기 때문입니다. 그런데 이 순환에는 오래된 걸림돌이 하나 있었습니다. 데스크톱의 입력은 본질적으로 동기적입니다. 하나의 커서와 하나의 키보드가 하나의 포커스된 창을 향합니다. 에이전트가 무언가를 클릭하는 순간 사용자의 마우스가 그쪽으로 끌려가고, 작업 중이던 창이 뒤로 밀리며, macOS라면 화면(Space)까지 따라 이동합니다. 사용자는 자기 컴퓨터를 빼앗기는 셈입니다.

Cua가 만든 Cua Driver는 바로 이 문제를 정면으로 다룹니다. 핵심 아이디어는 백그라운드 컴퓨터 유즈(Background Computer-Use) 입니다. 에이전트가 실제 데스크톱 앱을 조작하되, 사용자의 커서를 움직이지 않고, 포커스를 빼앗지 않으며, 화면을 강제로 전환하지 않습니다. 에이전트는 자기만의 합성 커서(Synthetic Cursor)를 가지고 클릭하고, 사용자는 그 옆에서 하던 일을 계속합니다. Claude Code, Codex, 혹은 직접 만든 에이전트 루프 어디에든 MCP(Model Context Protocol)나 CLI로 끼워 넣을 수 있고, 특정 모델이나 특정 제품에 묶이지 않은 범용 드라이버를 지향합니다.

이 글은 Cua 팀이 공개한 기술 해설 시리즈를 한국어로 정리한 것입니다. 시리즈는 세 편으로, 같은 계약(에이전트는 백그라운드에서 앱을 조작하고, 정직하게 실패를 보고한다)을 macOS, Windows, Linux라는 서로 다른 운영체제 위에서 어떻게 구현했는지를 다룹니다. macOS는 SkyLight라는 비공개 프레임워크 하나와 라우팅 트릭 하나의 이야기였고, Windows는 십수 가지 UI 툴킷을 가로지르는 라우터(Router) 의 이야기였으며, Linux는 AT-SPI 2XTEST를 둘러싼 또 다른 종류의 어려움이었습니다. 이 글에서는 이번에 공개된 Windows와 Linux 편을 중심에 두되, 시리즈 전체의 토대가 되는 macOS 편을 함께 엮어 설명합니다.

백그라운드 컴퓨터 유즈란 무엇인가: 커서를 빼앗지 않는 에이전트

기존의 컴퓨터 유즈 도구들은 대부분 "타깃 창을 앞으로 가져오고(activate), 클릭하고, 다시 원래대로 돌려놓는" 방식으로 동작했습니다. 이 방식은 동작은 하지만, 클릭할 때마다 사용자의 포커스가 흔들리고 스크롤 위치가 바뀝니다. 에이전트가 일하는 동안 사용자는 사실상 화면을 양보해야 합니다. 그래서 Cua는 그동안 데스크톱 호스트에 직접 컴퓨터 유즈 컴포넌트를 설치하기보다는, 격리된 VM이나 GUI 컨테이너를 에이전트의 행동 공간으로 권장해 왔습니다.

백그라운드 컴퓨터 유즈는 이 전제를 바꿉니다. 비슷한 방향으로, OpenAI의 Codexbackground computer-use 라는 이름으로 같은 모양의 기능을 제시했는데, Cua는 이것이 특정 에이전트 제품의 기능이 아니라 누구나 끼워 쓸 수 있는 공용 인프라(commodity) 가 되어야 한다고 봤습니다. 그래서 Cua Driver는 어떤 모델이 반대편에 있든 상관하지 않는 드롭인 드라이버 형태로, 허용적인 라이선스로 공개되었습니다.

핵심 장치는 합성 커서(Synthetic Cursor) 입니다. Cua Driver는 에이전트를 위한 커서를 별도의 오버레이로 직접 그립니다. 이 커서는 사용자의 물리적 포인터와 분리되어 있고, 에이전트마다 고유한 cursor_id를 가집니다. 덕분에 여러 에이전트가 하나의 물리 포인터를 두고 다투지 않고, 각자의 커서로 동시에 작업할 수 있습니다. 화면에 보이는 그 커서는 에이전트가 어디를 클릭하려 하는지, 어디를 클릭했는지 를 보여주는 시각적 흔적(trace)이며, 그 아래에서 실제 입력이 어떤 경로로 전달되는지는 타깃 앱에 따라 달라집니다. 이 분리가 바로 데모 영상이 읽기 쉬운 이유이자, 멀티 에이전트와 백그라운드 워크플로를 추론 가능하게 만드는 핵심입니다.

또 하나의 축은 요소 기반 자동화(Element Automation) 입니다. 픽셀 자동화가 "좌표 (842, 513) 근처를 클릭하라"고 말한다면, 요소 자동화는 "이 창에서 이름이 Save인 버튼을 클릭하라"고 말합니다. 후자는 레이아웃이 바뀌어도 다시 찾을 수 있고, 에이전트가 추론할 수 있는 대상입니다. 세 플랫폼 모두 운영체제의 접근성 트리(Accessibility Tree)를 1순위 주소 체계로 삼고, 그것이 통하지 않는 캔버스나 WebGL 표면에서만 픽셀 좌표로 떨어지는 구조를 공유합니다.

macOS가 닦아놓은 길: SkyLight와 포커스 없는 활성화

시리즈의 출발점인 macOS 편은, 사용자의 커서를 움직이지 않고 특정 앱에만 입력을 전달하는 경로를 찾는 이야기였습니다. 가장 쉬운 방법인 CGEventPost는 클릭 좌표로 커서를 워프시켜 버립니다. HID 이벤트 스트림에 이벤트를 떨어뜨리기 때문입니다. 다음 후보인 CGEvent.postToPid는 특정 프로세스로 이벤트를 보내 커서 워프는 막지만, Chromium 렌더러가 "신뢰할 수 없는 합성 이벤트"로 보고 조용히 무시해 버립니다.

해법은 두 조각이었습니다. 첫째, 타일링 윈도우 매니저 yabai가 수년간 써온 포커스 후 끌어올리지 않기(focus-without-raise) 패턴입니다. AppKit 활성화는 "이 앱이 입력 라우팅의 대상이다"라는 내부 상태 전환과 "창을 앞으로 끌어올려라"라는 두 단계로 나뉘는데, 앞 단계만 수행하면 창은 z 순서 그대로 둔 채 이벤트만 올바른 이벤트 루프로 보낼 수 있습니다. 둘째, 비공개 프레임워크 SkyLight의 SLEventPostToPid입니다. CGEvent.postToPid와 호출 시그니처는 같지만, 이벤트가 IOHIDPostEvent를 우회하는 인증 서명된 SkyLight 채널을 통해 전달되어 Chromium의 필터를 통과합니다.

여기에 더해, Chromium의 사용자 활성화 게이트(User-Activation Gate) 라는 마지막 관문이 있습니다. 신뢰된 채널로 이벤트가 와도, 최근에 "신뢰된 사용자 제스처"를 보지 못하면 동영상 재생이나 window.open 같은 동작을 막습니다. Cua Driver는 이를 화면 밖 좌표 (-1, -1)에 디코이(decoy) 클릭을 한 번 날리는 프라이머 클릭(Primer Click)으로 우회합니다. 이 클릭은 어떤 창에도 닿지 않아 폐기되지만, 활성화 게이트는 한 칸 전진하고, 몇 밀리초 뒤의 진짜 클릭이 그 제스처의 신뢰된 연속으로 받아들여집니다.

macOS 드라이버는 클라이언트가 무엇을 보고 클릭을 결정할지에 따라 세 가지 캡처 모드를 제공합니다. ax는 접근성 트리만 Markdown 아웃라인으로 반환하며 화면 녹화 권한이 필요 없습니다. vision은 타깃 창의 PNG만 반환해 픽셀에 기반하는 비전 모델에 적합합니다. 그리고 기본값인 somSet-of-Mark 방식으로 트리와 스크린샷을 함께 반환하여, 요소 인덱스 기반 클릭을 첫 스냅샷부터 동작하게 하면서 시각적 확인까지 무료로 얻습니다.

macOS 백그라운드 컴퓨터 유즈 더 알아보기

Inside macOS window internals: how SkyLight enables multi-cursor background agents - Francesco Bonacci (Cua Blog)

yabai - macOS 타일링 윈도우 매니저, focus-without-raise 패턴의 원본

Windows에는 수많은 Windows가 들어 있다: 라우터가 된 드라이버

Windows 편의 첫 문장은 시리즈 전체를 관통하는 통찰을 담고 있습니다. "Windows 안에는 수많은 Windows가 들어 있다" 는 것입니다. macOS의 어려움이 단 하나의 WindowServer 경로를 찾는 데 있었다면, Windows의 어려움은 "Windows 앱"이라고 부를 단일한 형태가 존재하지 않는다는 데 있었습니다. 오래된 메시지 루프를 쓰는 Win32 앱, 라우티드 이벤트를 쓰는 WPF 앱, XAML과 AppContainer 경계를 가진 WinUIUWP/WinRT 앱, 일부 합성 메시지만 받아들이는 Electron과 Chromium 앱, LibreOffice 계열의 VCL/SAL 앱, GTK 앱, 커스텀 캔버스, 그리고 수년째 아무도 손대지 않은 사내 업무용(line-of-business) 앱까지. 하나의 에이전트 드라이버가 이 모두에서 동작해야 했습니다.

커밋 히스토리는 Windows가 가르쳐 준 교훈의 목록처럼 읽힙니다. PrintWindow는 고전적 Win32 표면을 잘 캡처하지만 DirectComposition 기반 창에서는 검은 화면을 반환합니다. UI Automation은 대부분의 앱에 맞는 트리 API이지만 SAL/VCL에서는 서브트리 캐시 호출에 멈춰버립니다. PostMessage는 사용자 데스크톱을 건드리지 않지만 Chromium 콘텐츠는 그 클릭을 무시합니다. SendInput은 어려운 경우에 통하지만 시스템 입력 큐를 건드려 포어그라운드 전환을 요구할 수 있습니다. 이런 이유로 Windows 드라이버는 하나의 트릭이 아니라 라우터(Router) 가 되었습니다.

픽셀, 트리, 액션의 세 갈래 라우팅

쉬운 버전은 명확합니다. 타깃 HWND를 찾고, PrintWindow로 스크린샷을 찍고, IUIAutomation으로 트리를 걷고, PostMessage로 클릭을 보냅니다. 문제는 이것이 계산기, 설정, WinUI3 앱, Chromium 콘텐츠, WPF 슬라이더, LibreOffice 대화상자, GTK 버튼, 캔버스 앞에서 줄줄이 깨진다는 점입니다. 그래서 실제 드라이버는 타깃과 액션마다 올바른 백엔드를 골라야 합니다.

픽셀(스크린샷) 의 경우, 고전적 창은 PrintWindow와 GDI로 시작합니다. UWP나 WinUI, DirectComposition 기반 표면에서 검은 프레임이 돌아오면 Windows.Graphics.Capture(WGC)로 폴백합니다. WGC는 다른 창이 위에 겹쳐 있어도 DWM(Desktop Window Manager)으로부터 그 창 자신의 합성된 프레임을 읽어옵니다. WGC조차 불가능하면 화면 영역 BitBlt로 떨어지지만, 타깃이 다른 창에 가려져 있었다면 그 결과에 표시를 남깁니다. 가려진 창이 조용히 찍힌 스크린샷은 스크린샷이 없는 것보다 나쁘기 때문입니다.

트리(접근성) 의 경우, UI Automation을 캐시 요청과 함께 걸어, 거대한 Chrome이나 Electron 트리가 수천 번의 프로세스 간 속성 호출로 번지지 않게 합니다. 일부 CoreWindow/UWP 앱에서 ElementFromHandle이 빈 래퍼를 반환하면, 데스크톱 루트에서부터 걸으며 프로세스 ID로 필터링하는데, 이는 inspect.exe가 쓰는 것과 같은 방식입니다. VCL/SAL 앱은 UIA 제공자가 멈추거나 분할 드롭다운 버튼의 역할 정보를 잃어버리므로, 이때는 oleacc.dll을 통한 MSAA(Microsoft Active Accessibility)로 우회합니다. 이 오래된 경로는 ROLE_SYSTEM_BUTTONDROPDOWN 같은 역할을 보존해, 분할 버튼의 드롭다운 절반을 정확히 누를 수 있게 해줍니다.

액션(입력) 의 경우, 드라이버는 항상 가장 고수준 경로를 먼저 시도합니다. 어떤 요소가 UIA의 InvokePattern, TogglePattern, ValuePattern, RangeValuePattern, ExpandCollapsePattern을 가지고 있으면 그 패턴을 직접 호출합니다. 요소 인덱스 기반 클릭이 가장 깔끔한데, 좌표가 아니라 컨트롤 자체를 주소로 삼기 때문입니다. 픽셀 클릭은 계층적 경로를 거칩니다. 먼저 해당 픽셀에서 UIA 히트 테스트를 하고, 그 지점의 가장 깊은 컨트롤에 호출 가능한 액션이 있으면 접근성을 통해 호출합니다. 캔버스, 비디오, WebGL 표면처럼 접근성으로 잡히지 않는 곳에 떨어지면 그제서야 Win32 입력으로 내려갑니다.

정직한 디스패치: background와 foreground의 구분

기본 디스패치 모드는 background입니다. 즉 Cua Driver는 UIA와 PostMessage를 사용하고, 사용자의 포어그라운드를 조용히 빼앗기를 거부합니다. 만약 타깃이 PostMessage가 무시되는 알려진 경우라면, 도구는 background_unavailable을 반환합니다. 그러면 호출자는 그 한 번의 액션에 한해 dispatch:"foreground"를 선택할 수 있습니다. Chromium 좌표 클릭, GTK 버튼, VCL 가속 키, WPF 드래그가 그런 예입니다. 이 정직함이 Windows에서 특히 중요합니다. 모델이 포어그라운드 비용을 치르는 줄 알고 그 경로를 택하는 것과, 실수로 사용자 데스크톱을 통째로 가져가는 것은 전혀 다른 일이기 때문입니다.

화면에 보이는 커서 역시 사용자의 마우스가 아닙니다. Windows에서 Cua Driver는 투명하고 클릭이 통과되는(click-through) 레이어드 창에 에이전트 커서를 그립니다. 이 오버레이는 여러 모니터에 걸친 가상 화면을 덮고, 자신은 활성화되지 않으며, 타깃 창 근처의 z 순서에 머뭅니다. 각 커서는 고유한 cursor_id를 가지므로, 서로 다른 에이전트가 하나의 물리 포인터를 두고 다투는 대신 각자의 시각적 커서를 가집니다.

Session 0 문제: 데몬이 필요한 이유

진짜 에이전트 하네스를 돌려야만 드러나는 Windows 고유의 문제가 하나 있습니다. 에이전트가 SSH를 통해, 서비스로부터, 혹은 잘못된 부모 프로세스에서 실행되면 Session 0 에 떨어질 수 있습니다. Session 0에는 대화형 데스크톱이 없습니다. EnumWindows, GetForegroundWindow, PrintWindow, UIA, 화면 BitBlt가 모두 호출 프로세스의 WindowStation과 Desktop을 대상으로 동작하므로 비어 있거나 쓸모없는 결과를 돌려줍니다.

Cua Driver는 사용자의 대화형 세션 안에서 도는 데몬으로 이를 해결합니다. CLI나 MCP 서버는 에이전트가 시작되는 어디서든 돌 수 있고, 실제로 데스크톱이 붙어 있는 데몬에게 네임드 파이프(named pipe)로 도구 호출을 프록시합니다. 배관 작업처럼 들리지만, 이것이 "앱에 창이 없다""에이전트가 지금 당신이 보고 있는 Windows 세션을 조작할 수 있다" 의 차이를 만듭니다.

무엇을 녹화했나

Cua는 이번 출시를 위해 두 개의 Windows 데모를 녹화했습니다. 첫 번째는 코딩 에이전트의 QA 루프입니다. 프롬프트는 "아름다운 WPF 앱을 만들고 제대로 동작하는지 확인하라" 였습니다. Claude Code가 앱을 작성하고, 실행하고, 실제 Windows UI를 들여다보며 거친 부분을 찾아 고치고, 결과를 QA한 뒤, Cua Driver에게 최종 데모를 녹화하게 했습니다. 데스크톱 앱에서는 diff만으로는 증거가 부족합니다. 에이전트는 앱을 실행해 무슨 일이 일어났는지 보여줘야 합니다.

두 번째는 API가 전혀 없는 레거시 메일 서비스 데스크톱 앱을 조작하는 데모입니다. 에이전트는 사람이 쓰는 것과 똑같은 UI, 즉 창과 컨트롤과 필드와 버튼을 사용했습니다. Microsoft에 따르면 Windows는 월 14억 대 이상의 활성 기기에서 동작하며, 여전히 많은 업무가 Windows 전용 사내 소프트웨어 뒤에 갇혀 있습니다. 디스패치 도구, 청구 시스템, 백오피스 포털, 금융과 의료, 물류 시스템 같은 것들입니다. 그중 일부는 API를 갖게 되겠지만, 많은 경우 그렇지 못할 것입니다. 그런 앱에게는 UI가 존재하는 전부입니다.

Windows 백그라운드 컴퓨터 유즈 더 알아보기

Inside Windows computer-use: synthetic cursors for background agents - Francesco Bonacci (Cua Blog)

UI Automation 공식 문서 - Microsoft Learn

Cua Driver 공식 문서

Linux는 또 다른 방식으로 어렵다: AT-SPI 2와 XTEST

macOS와 Windows를 거친 뒤의 다음 질문은 자연히 Linux였습니다. 그리고 Linux는 예상과는 다른 방식으로 어려웠습니다. 이 어려움은 앱 자체와는 거의 관계가 없고, 앱 아래에 깔린 부품의 가짓수에서 옵니다. 단일한 Linux 데스크톱 API가 없습니다. 세션, 컴포지터, 디스플레이 서버, 접근성 서비스, D-Bus 버스, 툴킷별로 다른 포커스 동작, 그리고 "이 요소를 클릭한다"의 의미에 부분적으로만 동의하는 앱들이 있습니다. GTK3과 GTK4가 다르게 동작하고, Qt5는 또 자기만의 형태를 가지며, Tk는 더 오래되고 직접적입니다. Electron, Chrome, VS Code, Slack, Discord, Obsidian은 모두 Chromium 위에 올라가 자기만의 접근성 모델을 끌고 들어옵니다. 그래서 Linux 백엔드도 Windows가 강요했던 것과 같은 모양, 즉 단일 레시피가 아닌 라우터 가 되었습니다.

가장 저항이 적은 길은 X11을 직접 조작하는 것입니다. 창 목록을 읽고, 제목을 보고, 마우스를 옮기고, 키 이벤트를 보내고, 구조가 없으면 이미지 매칭으로 떨어지는 방식입니다. 이것은 데모에서는 통하지만 금세 무너집니다. X11은 창이 존재한다는 것은 알려주지만, 사이드바에 "Run"이라는 이름의 버튼이 있다거나, 텍스트 필드에 플레이스홀더가 있다거나, 대화상자에 파괴적인 동작이 숨어 있다는 것은 알려주지 못합니다. 결국 픽셀 매칭으로 되돌아가게 되는데, 그것이야말로 컴퓨터 유즈가 넘어서려던 바로 그 취약함입니다.

AT-SPI 2: Linux의 접근성 계층

그래서 등장하는 것이 AT-SPI 2입니다. D-Bus 위에 올라간 Linux 접근성 계층으로, macOS의 Accessibility와 Windows의 UI Automation에 가장 가까운 대응물이며, 스크린 리더 같은 보조 기술이 이미 사용하는 바로 그 인터페이스입니다. 이를 통해 드라이버는 애플리케이션, 창, 패널, 버튼, 입력 필드, 텍스트 영역, 메뉴 항목, 탭으로 이루어진 접근성 객체의 트리를 봅니다. 트리가 항상 완전하지는 않지만, 데스크톱을 비트맵으로 취급하는 것보다는 훨씬 낫습니다. 이것이 픽셀 자동화와 컴퓨터 유즈의 진짜 차이입니다. 픽셀 자동화가 "좌표 842, 513 근처를 클릭" 이라고 말한다면, 요소 자동화는 "이 창에서 Save라는 이름의 버튼을 클릭" 이라고 말하고, 후자는 레이아웃이 바뀐 뒤에도 다시 찾을 수 있는 대상입니다.

키보드 입력은 XSendEvent에서 XTEST 주입으로 옮겨갔습니다. 더 많은 앱이 XTEST를 "진짜에 충분히 가까운" 입력으로 받아들이기 때문입니다. 물론 XTEST가 최선이 아닐 때를 위한 XSendEvent와 X11 폴백도 남아 있습니다. 이 모든 것을 관통하는 원칙은 다시 한번 정직함 입니다. 앱이 보이지 않을 때, AT-SPI가 꺼져 있을 때, 세션이 네이티브 Wayland 전용일 때, 드라이버는 동작한 척하는 대신 명시적인 오류를 반환합니다. 에이전트는 읽을 수 있는 오류로부터는 복구할 수 있지만, 조용히 아무것도 하지 않고 성공을 보고하는 드라이버로부터는 복구할 수 없습니다.

Chromium 접근성 스위치: 오후 하나를 잡아먹은 디테일

Chromium 기반 앱에는 특히 날카로운 모서리가 하나 있습니다. Chrome, VS Code, Slack, Discord, Obsidian, 그리고 대부분의 Electron 또는 CEF 앱은 기본적으로 자신의 전체 AT-SPI 트리를 만들어두지 않습니다. Chromium은 보조 기술이 존재한다고 감지하기 전까지 트리 구축을 미룹니다. 성능 면에서는 합리적이지만, 드라이버를 작성하는 입장에서는 앱이 열려 있고 창이 바로 거기 있는데도 트리가 그냥 비어 있는, 진심으로 혼란스러운 상황을 만듭니다.

Linux에서 그 신호는 세션 접근성 상태 아래에 있습니다. cua-driver serve가 시작되면 데몬은 세션의 org.a11y.Status 접근성 플래그를 켜는데, 이는 스크린 리더가 설정하는 바로 그 신호입니다. 유용한 점은 Chromium이 소급적으로 반응한다는 것입니다. 이미 열려 있던 Chromium과 Electron 앱이 재실행이나 앱별 플래그 없이 AT-SPI 트리를 구축하고, GTK와 Qt 앱도 같은 신호를 읽어 접근성 경로를 예열합니다. GNOME에서는 추가로 다음 설정이 필요할 수 있습니다.

gsettings set org.gnome.desktop.interface toolkit-accessibility true

cua-driver doctor가 이 경로를 점검하고 무엇이 보이는지 보고합니다.

포커스를 빼앗지 않고 글쓰기

클릭은 문제의 절반에 불과합니다. 백그라운드 자동화가 무너지는 지점은 보통 텍스트 입력입니다. 에이전트가 글을 쓸 때마다 타깃 앱에 포커스를 줘야 한다면 사용자가 방해받기 때문입니다. 포어그라운드 커서가 튀고, 활성 창이 바뀝니다. 그래서 Linux 백엔드는 주요 툴킷 계열별로 포커스 없는 쓰기(focus-free write) 경로를 갖췄고, 여기에 도달하려면 GTK3, GTK4, Qt5, Tk를 각각 다르게 다뤄야 했습니다. 어떤 위젯은 접근성 액션으로 텍스트를 받고, 어떤 위젯은 앱 전체를 끌어올리지 않는 GrabFocus가 필요하며, 어떤 위젯은 합성 포커스 이벤트를, 어떤 위젯은 위젯 클릭 폴백을, 또 어떤 위젯은 가능한 경우 툴킷 IPC를 통해 다뤄야 합니다. 깔끔한 추상화 하나가 그 아래 있었다면 좋았겠지만, 실제로는 느린 방법으로 하나씩 찾아낸 툴킷별 특수 케이스의 더미였습니다.

합성 커서와 더 작아진 데몬 문제

Linux 백엔드는 macOS, Windows와 같은 커서 모델을 씁니다. 에이전트 커서는 사용자의 물리 포인터가 아니며, 드라이버가 오버레이 커서를 그리고, 각 에이전트가 자기 cursor_id를 가지며, 물리 포인터는 손이 둔 자리에 그대로 머뭅니다. 아래 데모는 XFCE 데스크톱에서 에이전트 커서가 "Cua"라는 단어를 드래그하는 동안 물리 포인터는 전혀 움찔하지 않는 모습을 보여줍니다. 백엔드는 백그라운드 드래그와 버튼 홀드 도구도 제공하여, 항목을 옮기거나 슬라이더를 테스트하거나 접근성 액션이 없는 커스텀 위젯을 조작할 수 있습니다.

XFCE에서 에이전트 커서가 단어를 드래그하는 멀티 커서 데모

데몬 문제는 Linux에서 훨씬 작습니다. Windows가 Session 0 격리 때문에 데몬 프록시를 필요로 했던 것과 달리, Linux에서는 SSH로 접속해도 세션에 디스플레이 서버가 있으면 sshdDISPLAY를 깔끔하게 상속받습니다. 세션 버스와 디스플레이에 닿을 수 있는 한 cua-driver serve는 Session 0 춤 없이 SSH 세션에서 곧장 데스크톱에 연결합니다. 지속적인 세션이 필요하면 systemd--user 유닛을 쓰고, 헤드리스나 CI 형태의 머신에서는 로그아웃 후에도 서비스가 살아남도록 lingering을 켭니다.

loginctl enable-linger "$USER"

무엇에 써왔나, 그리고 무엇이 아직 깨지나

첫 데모는 일부러 작게 잡았습니다. 더 많은 추상화를 쌓기 전에 단단해야 할 원시적인 것들을 보여주기 위해서입니다. 하나는 위에서 본 XFCE 멀티 커서 드래그이고, 다른 하나는 코딩 에이전트가 계산기 데스크톱 앱을 만들고 실행한 뒤 Cua Driver로 그 앱의 Linux UI를 직접 QA하는 빌드, 실행, 조작, 검증 의 루프입니다. Windows의 WPF 데모를 볼 만하게 만들었던 것과 같은 루프로, 에이전트가 코드 생성에서 멈추지 않고 실제로 실행해 확인합니다.

아직 깨지는 부분도 분명합니다. 네이티브 Wayland는 여전히 프리뷰 상태이며, 현재 지원되는 경로는 X11 또는 XWayland입니다. XWayland를 통해 도는 앱에는 동작하지만, 그것을 우회하는 일부 최신 Firefox와 GTK4 빌드 같은 네이티브 Wayland 전용 앱은 백엔드에 아예 보이지 않을 수 있습니다. CUA_DRIVER_RS_ENABLE_WAYLAND=1 플래그 뒤에 네이티브 Wayland 경로가 있지만 기본은 꺼져 있고, 화면 캡처와 완전한 AT-SPI 동등성은 아직 추가되는 중입니다. 또한 AT-SPI가 세션 버스에서 켜지고 닿을 수 있어야 하며, 헤드리스 이야기도 아직 초기 단계입니다. cua-driver doctor는 바로 이런 간극들을 추측이 아닌 사실로 보고하기 위해 존재합니다.

Linux 백그라운드 컴퓨터 유즈 더 알아보기

Inside Linux computer-use: AT-SPI, XTEST, and background agents - Robert Wendt (Cua Blog)

AT-SPI 2 / freedesktop Accessibility

cua GitHub 저장소 (Issues로 토킷 버그 제보 환영)

세 플랫폼을 관통하는 하나의 계약

세 편의 글을 나란히 놓으면, 운영체제마다 구현은 전혀 다르지만 같은 설계 계약을 공유한다는 것이 드러납니다. 입력은 사용자의 물리 포인터와 분리된 합성 커서를 통해 전달되고, 주소 체계는 픽셀이 아니라 접근성 트리의 요소를 1순위로 삼으며, 그것이 불가능할 때만 픽셀로 떨어집니다. 그리고 무엇보다 정직하게 실패합니다. 백그라운드 전달이 불가능하면 조용히 포어그라운드를 빼앗는 대신 명시적 오류나 background_unavailable 신호를 돌려주어, 에이전트가 그로부터 복구하거나 비용을 알고 선택하게 합니다.

항목 macOS Windows Linux
입력 전달 SkyLight SLEventPostToPid, CGEvent.postToPid UIA 패턴, PostMessage, SendInput AT-SPI 액션, XTEST, XSendEvent
접근성 트리 AX SPI (private) UI Automation, MSAA(oleacc) AT-SPI 2 (D-Bus)
화면 캡처 윈도우 PNG 캡처 PrintWindow, WGC, BitBlt X11 / XWayland 캡처
포커스 회피 yabai식 focus-without-raise background 디스패치 모드 툴킷별 focus-free write
핵심 난관 Chromium 신뢰 게이트, 프라이머 클릭 십수 가지 툴킷 라우팅, Session 0 Chromium 접근성 스위치, Wayland
원격/데몬 로컬 앱 데몬 + 네임드 파이프 (Session 0) SSH가 DISPLAY 상속, systemd --user

이 표가 말해주는 것은, 백그라운드 컴퓨터 유즈가 어느 한 운영체제의 특수한 트릭이 아니라 재현 가능한 설계 패턴 이라는 점입니다. macOS는 비공개 프레임워크 하나로, Windows는 백엔드 라우터로, Linux는 D-Bus 접근성 계층으로 같은 사용자 경험에 도달했습니다. 이는 OpenAI의 OperatorHugging Face의 Open Computer Agent처럼 격리된 환경이나 브라우저에서 동작하던 컴퓨터 유즈 에이전트들이, 이제 사용자의 실제 데스크톱 위에서 화면을 빼앗지 않고 동작하는 방향으로 한 걸음 더 나아갈 수 있음을 시사합니다.

설치와 사용: Claude Code에 붙이기

세 플랫폼 모두 같은 설치 경로를 공유합니다. macOS와 Linux는 동일한 설치 스크립트가 운영체제를 감지해 알맞은 백엔드로 라우팅하고, Windows는 PowerShell 한 줄로 설치합니다.

# macOS / Linux
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/trycua/cua/main/libs/cua-driver/scripts/install.sh)"
# Windows (PowerShell)
irm https://raw.githubusercontent.com/trycua/cua/main/libs/cua-driver/scripts/install.ps1 | iex

Linux의 경우 접근성 패키지가 필요하며, 세션과 데몬 상태는 doctorstatus로 점검합니다.

# Debian / Ubuntu
sudo apt-get install -y at-spi2-core
# Fedora / Rocky / RHEL
sudo dnf install -y at-spi2-core

cua-driver doctor
cua-driver status
cua-driver serve

Claude Code에 MCP 서버로 연결하면, 에이전트가 곧바로 데스크톱을 백그라운드로 조작할 수 있습니다. Cursor나 다른 MCP 클라이언트도 같은 방식으로 붙일 수 있습니다.

claude mcp add --transport stdio cua-driver -- cua-driver mcp
# 다른 MCP 클라이언트용 설정 출력
cua-driver mcp-config

모든 MCP 도구는 최상위 CLI 서브커맨드로도 노출되어, 같은 동작을 스크립트로 직접 호출할 수 있습니다.

cua-driver list_apps
cua-driver doctor
cua-driver status

Cua Driver는 trycua/cua 저장소의 일부이며, 이 저장소에는 드라이버 외에도 어떤 OS든 에이전트용 샌드박스를 제공하는 cua-agent, OSWorld와 ScreenSpot 등에서 컴퓨터 유즈 에이전트를 평가하는 Cua Bench, Apple Silicon에서 macOS/Linux VM을 관리하는 Lume가 함께 들어 있습니다.

마치며

백그라운드 컴퓨터 유즈 시리즈가 보여주는 것은, "에이전트에게 컴퓨터를 쓰게 한다"는 오래된 목표의 병목이 모델의 지능이 아니라 운영체제의 입력 모델에 있었다는 사실입니다. 하나의 커서, 하나의 키보드, 하나의 포커스라는 데스크톱의 동기적 제약이 그동안 수많은 GUI 에이전트 제품을 실패하게 만들었습니다. Cua Driver는 그 제약을 우회하는 방법이 운영체제마다 다르지만 분명히 존재하며, 그것을 특정 벤더의 기능이 아니라 누구나 끼워 쓸 수 있는 공용 드라이버로 만들 수 있음을 보여줍니다.

특히 정직한 실패 라는 설계 원칙은 곱씹어 볼 가치가 있습니다. 자동화 도구가 빠지기 쉬운 함정은, 어딘가에서 입력이 사라졌는데도 성공을 보고하는 것입니다. 에이전트는 읽을 수 있는 오류로부터는 다음 행동을 추론할 수 있지만, 조용한 실패 앞에서는 무력합니다. 픽셀이 아닌 요소를 1순위로 삼는 주소 체계, 사용자와 분리된 합성 커서, 그리고 실패를 숨기지 않는 계약. 이 세 가지가 모여 멀티 에이전트와 백그라운드 워크플로를 비로소 추론 가능한 것으로 만듭니다.

:scroll: Inside Windows computer-use 소개 블로그

:scroll: Inside Linux computer-use 소개 블로그

:github: trycua/cua GitHub 저장소

라이선스

Cua는 MIT License로 배포되고 있어, 연구 목적은 물론 상업적 용도로도 자유롭게 사용 및 수정이 가능합니다. 다만 저장소에 포함된 일부 서드파티 컴포넌트는 자체 라이선스를 따르며(예: OmniParser는 CC-BY-4.0, 선택적 cua-agent[omni]에 포함되는 ultralytics는 AGPL-3.0), Apple, macOS, Ubuntu, Microsoft 등은 각 권리자의 상표입니다. 이 프로젝트는 해당 기업들과 제휴하거나 보증받은 관계가 아닙니다.

더 읽어보기




이 글은 GPT 모델로 정리한 글을 바탕으로 한 것으로, 원문의 내용 또는 의도와 다르게 정리된 내용이 있을 수 있습니다. 관심있는 내용이시라면 원문도 함께 참고해주세요! 읽으시면서 어색하거나 잘못된 내용을 발견하시면 덧글로 알려주시기를 부탁드립니다. :hugs:

:pytorch:파이토치 한국 사용자 모임:south_korea:이 정리한 이 글이 유용하셨나요? 회원으로 가입하시면 주요 글들을 이메일:love_letter:로 보내드립니다!
텔레그램(Telegram)이나 Slack/Discord/Teams/Dooray/GoogleChat 등으로도 새 글 알림을 받으실 수 있습니다. :smiley:

:wrapped_gift: 아래:down_right_arrow:쪽에 좋아요:+1:를 눌러주시면 새로운 소식들을 정리하고 공유하는데 힘이 됩니다~ :star_struck: