포스트

MyKVM (1) 키보드 하나로 맥과 윈도우를 오가는 앱의 전체 구조

이 시리즈는 내가 직접 쓴 개발기가 아니다. 무엇을 만들지 정해 Claude Code 에게 앱을 만들게 했고, 만든 과정과 쓰인 기술은 Claude Code 가 정리했다. 나는 방향을 정하고, 실기기에서 써 보며 고칠 곳을 찾아 다시 맡겼다. 그래도 글로 남기는 건 공부 때문이다. 아직 내가 아는 프레임워크가 몇 개 없어서, 이 앱에 쓰인 여러 프레임워크를 정리해 두고 나중에 하나씩 찾아보며 익히려고 한다.

책상에 맥북과 윈도우 PC가 같이 있고, 키보드와 마우스는 하드웨어 KVM 장비의 버튼으로 오간다. 그런데 버튼을 누를 때 USB 키보드가 가끔 인식되지 않는다. 케이블을 뽑았다 꽂아도 안 되면 CalDigit 독을 맥북에 다시 꽂아야 겨우 돌아온다. Synergy도 써 봤지만 연결이 불안정하다는 느낌이 들었다. 로지텍 Flow처럼 마우스를 화면 끝으로 밀면 옆 컴퓨터로 매끄럽게 넘어가는 걸 직접 만들어 보기로 했다.

이 시리즈는 그렇게 만든 MyKVM을 주제별로 나눠 정리한다. 이번 글은 전체 구조와 저장소 구성, 그리고 윈도우 앱을 맥에서 어떻게 시험했는지까지다.


선을 옮기지 않는 KVM

KVM은 Keyboard, Video, Mouse의 첫 글자다. 한 벌의 키보드, 모니터, 마우스로 컴퓨터 여러 대를 쓰게 해 주는 장치를 말한다.


소프트웨어 KVM이란?

하드웨어 KVM은 키보드와 마우스 USB 선을 실제로 이쪽 컴퓨터에서 저쪽 컴퓨터로 옮겨 꽂아 주는 상자다. 선을 옮기는 순간 컴퓨터는 USB 장치를 새로 인식해야 하고, 내 경우 거기서 가끔 키보드를 못 찾았다.

소프트웨어 KVM은 선을 옮기지 않는다. 키보드는 늘 한 컴퓨터에 꽂혀 있고, 그 컴퓨터의 앱이 누른 키를 네트워크로 상대 컴퓨터에 보낸다. 상대 컴퓨터의 앱은 받은 키를 “방금 이 키가 눌렸다”고 자기 컴퓨터에 알려 준다. 전화로 불러 주면 받아 적는 것과 비슷하다. USB를 다시 인식하는 일 자체가 없어서 처음 문제였던 키보드 인식 불량이 생길 자리가 없다.

모니터는 옮기지 않는다. 모니터 입력은 지금처럼 모니터 버튼으로 바꾼다. 맥에서 모니터에 “입력을 HDMI로 바꿔” 명령을 보내는 방식(DDC)도 시험해 봤지만, 내 LG 모니터는 표준 방식과 LG 전용 방식 모두 반영하지 않았다.


맥이 판단하고 윈도우는 따르는 구조

맥에는 메뉴 막대 앱이, 윈도우에는 트레이 아이콘으로 떠 있는 앱이 하나씩 있다. 둘은 같은 공유기에 랜선으로 붙어 있다.

맥이 키보드와 마우스 입력을 먼저 받아 커서가 어느 화면에 있는지 판단하고, 윈도우 화면이면 위치와 키를 암호화해서 윈도우로 보낸다. 윈도우는 받은 대로 입력을 넣고 화면 목록과 잠김 여부만 알려 준다

어느 컴퓨터가 입력을 받을지는 전부 맥이 정한다. 커서가 지금 어느 화면에 있는지, 화면 끝에 닿았는지, 어느 모니터가 지금 맥 화면이고 어느 모니터가 윈도우 화면인지를 맥만 계산한다. 윈도우 앱은 받은 대로 키와 마우스를 넣고, 자기 화면 목록과 잠김 여부만 알려 준다.

판단을 한쪽으로 몰아 둔 이유는 윈도우 쪽 코드를 바로 돌려 볼 수 없어서다. 윈도우 앱은 맥에서 빌드해서 NAS로 옮겨 실행한다. 윈도우 쪽이 단순할수록 옮기고 확인하는 횟수가 줄어든다.

나중에 반대 방향(키보드를 윈도우에 꽂고 맥 조작)을 넣을 때도 이 구조는 그대로 뒀다. 그 이야기는 11편과 12편에 있다.


맥 쪽과 윈도우 쪽 저장소 구성

저장소 하나에 맥 코드와 윈도우 코드를 같이 둔다.

1
2
3
4
5
6
7
8
9
10
11
MyKVM/
  mac/
    Packages/KVMCore/      계산 로직 (Swift 패키지, 화면과 OS를 모름)
    MyKVMMac/              메뉴 막대 앱 (이벤트 탭, 커서, 화면 감시)
  windows/
    src/MyKVM.Core/        계산 로직과 연결 (.NET 라이브러리, 윈도우 기능 안 씀)
    src/MyKVM.Win/         트레이 앱 (입력 넣기, 훅, 화면 정보)
    src/MyKVM.Cli/         맥에서 윈도우 클라이언트를 시험하는 콘솔
    tests/MyKVM.Core.Tests/
  protocol/vectors/        맥과 윈도우가 같은 값을 내는지 확인하는 고정 값
  scripts/e2e.sh           맥에서 양쪽을 실제로 붙여 보는 시험

양쪽 다 “계산 로직”과 “OS에 붙는 부분”을 나눴다. 화면 배치 계산, 키 변환, 암호화, 메시지 형식, 연결 상태 같은 판단은 OS 기능을 전혀 쓰지 않는 코드로 모았다. OS에 붙는 부분은 그 판단을 받아 실제로 커서를 옮기거나 키를 넣는 얇은 층이다.


Swift 패키지란?

Swift 코드를 앱과 따로 묶어 두는 단위다. Package.swift 파일 하나에 이름, 지원하는 OS, 안에 든 라이브러리와 테스트를 적는다. 앱 없이 터미널에서 swift test 한 줄로 테스트를 돌릴 수 있어서, 화면이 필요 없는 계산 로직을 두기 좋다. MyKVM의 KVMCore가 이 패키지이고, 메뉴 막대 앱은 이 패키지를 가져다 쓴다.


.NET 솔루션과 프로젝트란?

C# 쪽에서는 프로젝트(.csproj)가 하나의 결과물(라이브러리나 실행 파일)이고, 솔루션(.sln)이 여러 프로젝트를 묶는다. 프로젝트마다 어떤 .NET 버전과 어떤 OS를 대상으로 하는지 적는다.

프로젝트대상하는 일
MyKVM.Corenet10.0판단과 연결. 윈도우 기능을 안 써서 맥에서도 돈다
MyKVM.Winnet10.0-windows트레이 앱. 윈도우 전용 기능을 쓴다
MyKVM.Clinet10.0맥에서 Core를 실제로 돌려 보는 콘솔
MyKVM.Core.Testsnet10.0Core 테스트. 맥에서 dotnet test로 돈다

대상이 net10.0인 프로젝트는 맥, 윈도우, 리눅스 어디서나 돈다. 그래서 윈도우 앱의 연결 코드와 판단 코드를 Core에 두고 맥에서 테스트했다.

1
2
3
4
5
6
7
8
9
10
<!-- MyKVM.Win.csproj -->
<PropertyGroup>
  <OutputType>WinExe</OutputType>
  <TargetFramework>net10.0-windows</TargetFramework>
  <UseWindowsForms>true</UseWindowsForms>
  <EnableWindowsTargeting>true</EnableWindowsTargeting>
  <ApplicationManifest>app.manifest</ApplicationManifest>
  <ApplicationIcon>app.ico</ApplicationIcon>
  <ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
</PropertyGroup>

EnableWindowsTargeting으로 맥에서 윈도우 앱 빌드하기

속성하는 일
OutputTypeWinExe는 콘솔 창 없이 뜨는 윈도우 프로그램이라는 뜻이다
TargetFrameworknet10.0-windows는 .NET 10에 윈도우 전용 기능까지 쓰겠다는 뜻이다
UseWindowsForms트레이 아이콘과 작은 창을 만드는 WinForms를 쓴다
EnableWindowsTargeting윈도우가 아닌 컴퓨터(맥)에서도 이 프로젝트를 빌드하게 허락한다
ApplicationManifest관리자 권한과 화면 배율 처리를 적은 파일(9편)
ApplicationIconexe 파일과 작업 표시줄에 보일 아이콘
ApplicationHighDpiMode모니터마다 배율이 달라도 좌표를 실제 픽셀로 다룬다(10편)

EnableWindowsTargeting이 없으면 맥에서 net10.0-windows 프로젝트를 빌드하려 할 때 “윈도우에서만 빌드할 수 있다”는 오류가 난다. 빌드는 맥에서 하고, 실행만 윈도우에서 하는 이 프로젝트에 꼭 필요했다. .NET 프로젝트 속성 Docs에 전체 목록이 있다.


윈도우 코드를 맥에서 시험하는 방법

윈도우 앱은 실제 윈도우에서 돌려 보기 전까지 동작을 알 수 없다. 그렇다고 고칠 때마다 NAS로 옮기면 한 번 확인하는 데 시간이 오래 걸린다. 그래서 시험을 세 겹으로 나눴다.

무엇을어디서어떻게
화면 배치, 키 변환, 원격 조작 판단맥Swift 테스트(swift test)
윈도우 쪽 판단, 암호화, 메시지 형식맥C# 테스트(dotnet test)
맥 서버와 윈도우 클라이언트 실제 연결맥두 프로그램을 띄워 붙여 보는 스크립트
입력 넣기, 훅, 트레이 아이콘윈도우점검 목록을 들고 직접 확인

세 번째 줄이 쓸모가 컸다. 윈도우 클라이언트의 연결 코드는 윈도우 기능을 안 쓰니 맥에서도 그대로 돈다. 그래서 맥 서버와 윈도우 클라이언트를 이 맥 안에서 실제 소켓으로 붙여 페어링, 끊겼다 다시 붙기, 서버 재시작 같은 장면을 스크립트로 확인했다. 8편에서 자세히 다룬다.

마지막 줄은 맥에서 확인할 수 없어서, 점검 항목을 목록으로 만들어 윈도우에서 하나씩 확인했다. 실제로 맞닥뜨린 문제(커서가 튀거나 포인터가 사라지는 것 등)는 대부분 이 단계에서 나왔고, 각 편에서 그 원인과 함께 정리한다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.