macOS 고성능 화면 공유는 어떻게 동작할까

  • 소프트웨어
  • 네트워크
목차

macOS 고성능 화면 공유?

macOS 고성능 화면 공유는 Apple이 2023년 macOS Sonoma 14와 함께 내놓은 화면 공유 방식입니다. Apple의 macOS Sonoma 발표에 따르면 Apple silicon의 미디어 엔진을 써서, 지연이 짧은 소리와 높은 프레임레이트, 가상 디스플레이를 지원합니다.

이 글은 Apple이 공개하지 않은 고성능 화면 공유 프로토콜이 어떤 원리로 동작하는지 공유하려고 썼습니다. 다 읽으면 일반 VNC와 무엇이 다른지, Mac의 화면이 어떤 길을 거쳐 다른 기기에 그려지는지 알게 됩니다.

저는 이 프로토콜로 동작하는 클라이언트를 직접 구현했고, 이 글은 2026년 10월 15일에 본 동작을 기준으로 썼습니다. 이때 Mac은 macOS 27.0.1을, 클라이언트 기기인 iPad Pro 13형(M4)은 iPadOS 27.0.1을 실행했습니다. 프로토콜은 macOS 빌드마다 조금씩 달라서, macOS가 바뀌면 이 글의 내용도 달라질 수 있습니다.

VNC와 RFB

VNC는 다른 컴퓨터의 화면을 보면서 조작하는 원격 화면 방식이고, 이때 주고받는 규칙이 RFC 6143에 표준으로 정리된 RFB 프로토콜입니다. macOS의 화면 공유도 VNC 서버라서, 다른 회사의 VNC 앱으로도 Mac에 붙을 수 있습니다.

RFB는 TCP 연결 하나로 모든 것을 주고받습니다.

  • 서버 → 클라이언트: 클라이언트가 요청하면, 화면에서 바뀐 사각형 영역의 픽셀을 압축해서 보냅니다.
  • 클라이언트 → 서버: 키보드와 마우스 입력을 보냅니다.

이 방식은 문서나 메뉴처럼 가만히 있는 화면에는 잘 맞지만, 영상처럼 계속 바뀌는 화면에서는 두 가지가 걸립니다.

  • 보낼 양: 영상 코덱은 앞 프레임에서 움직인 부분을 찾아 그 차이만 보내지만, RFB는 바뀐 영역의 픽셀 자체를 압축해서 보냅니다.
  • 기다림: TCP는 보낸 순서대로 빠짐없이 도착하게 해 주는 대신, 패킷 하나를 잃으면 그 패킷을 다시 받을 때까지 뒤에 온 데이터도 기다리게 합니다.

Mac의 화면 공유 서버는 연결하자마자 자신을 RFB 003.889 버전이라고 소개합니다. 표준 RFB의 마지막 버전은 3.8(003.008)이어서, 클라이언트는 표준에 없는 003.889를 보고 상대가 Apple만의 기능을 더한 Mac이라는 것을 압니다. 고성능 화면 공유도 이 TCP 연결에서 시작합니다.

연결을 두 갈래로 나누기

고성능 화면 공유는 RFB 연결을 버리지 않고 역할을 줄입니다. TCP 연결은 제어 채널로 남아 인증과 입력, 각종 제어를 맡고, 화면과 소리는 잃은 패킷을 기다리지 않는 UDP를 타고 따로 옵니다.

Mac과 클라이언트 사이의 TCP 5900, UDP 5901, UDP 5900 연결이 각각 무엇을 나르는지 보여 주는 그림
TCP 5900은 제어를, UDP 5901은 영상을, UDP 5900은 소리와 클라이언트의 요청을 맡습니다
일반 VNC (RFB) 고성능 화면 공유
연결 TCP 하나 TCP 하나와 UDP 둘
화면 바뀐 사각형의 픽셀 영상 코덱(HEVC)으로 압축한 영상
패킷을 잃으면 TCP가 다시 보냄 온전한 화면을 새로 받아 회복
소리 표준에 없음 AAC-ELD 스테레오
화면 크기 Mac의 실제 화면 실제 화면 또는 클라이언트에 맞춘 가상 디스플레이

연결은 이 순서로 열리고, 그림과 표에 나온 이름은 아래 절에서 이 순서대로 설명합니다.

  1. TCP로 붙어서 macOS 계정으로 인증합니다.
  2. TCP 제어 채널을 암호화합니다.
  3. 영상과 소리를 어떻게 보낼지, 어떤 키로 암호화할지 정합니다.
  4. UDP로 영상과 소리가 오기 시작합니다.

계정으로 인증하기

Mac은 번호를 붙인 인증 방식 네 가지를 제시하고, 제가 구현한 클라이언트는 그중 30번인 ARD 인증(Apple Remote Desktop 인증)을 씁니다. VNC 전용 비밀번호 대신 Mac의 사용자 이름과 비밀번호로 로그인하는 방식입니다.

ARD 인증은 Diffie-Hellman 키 교환으로 시작합니다. 서버와 클라이언트가 공개해도 되는 값만 주고받고도 둘만 아는 같은 비밀 값을 얻는 방법입니다. 클라이언트는 이 비밀 값의 MD5 해시를 AES 키로 삼아, 사용자 이름과 비밀번호를 암호화해서 보냅니다.

이 방식은 엿듣기만 하는 상대에게서는 비밀번호를 가립니다. 그러나 클라이언트가 상대가 진짜 그 Mac인지 확인하지 않아서, 중간에서 Mac인 척 끼어드는 공격은 막지 못합니다. 끼어든 상대는 Mac과 클라이언트 각각과 키를 나눠 가져서, 뒤에서 이 키로 감싸 주고받는 키까지 풀어 화면과 입력을 볼 수 있습니다.

Mac은 SRP라는 방식으로 양쪽이 같은 비밀번호를 아는지 서로 확인하는 33번과 36번도 함께 제시합니다.

제어 채널 암호화하기

인증이 끝나면 클라이언트는 자신을 Apple의 화면 공유 앱처럼 소개하고, 암호화를 시작하자고 요청합니다. 소개 메시지에는 앱 버전, OS 버전, 지원하는 기능 목록이 들어갑니다.

서버는 이 요청에 새 AES 키와, 첫 블록을 암호화할 때 섞는 초기값(IV)으로 답합니다. 이 두 값은 인증 때 만든 키로 암호화되어 있어서 엿듣기만 하는 상대는 풀 수 없습니다. 이 답을 받은 뒤로는 입력, 커서 모양, 화면 크기 변경, 클립보드까지 모든 메시지를 새 키로 암호화합니다.

암호화한 메시지 하나를 레코드라고 부르고, 레코드는 다음 순서로 만듭니다.

  1. 메시지 뒤에, 레코드 순번과 메시지 내용으로 계산한 SHA-1 해시를 붙입니다.
  2. 이것을 AES-128-CBC로 암호화합니다. CBC는 블록마다 앞 블록의 암호문을 섞는 방식이고, 이 채널은 레코드가 바뀌어도 앞 레코드의 암호문을 이어서 섞습니다.

순번이 든 해시까지 함께 암호화해서, 키가 없는 상대가 중간에서 레코드를 빼거나 순서를 바꾸면 바로 드러납니다.

영상과 소리 정하기

제어 채널이 암호화되면 클라이언트는 영상과 소리를 어떻게 받을지 제안합니다. 제안 하나에는 이런 내용이 들어갑니다.

  • 암호 키 4개: 영상과 소리 각각에, 서버 → 클라이언트 방향과 클라이언트 → 서버 방향 키를 하나씩 둡니다. 연결할 때마다 클라이언트가 무작위로 새로 만듭니다.
  • 영상 조건: 코덱 후보, 뒤에서 소개할 띠의 수와 장기 참조 프레임 사용 여부
  • 소리 조건: 원하는 소리 비트레이트(1초에 보내는 데이터 양)
  • 클라이언트 정보: 기기 모델, OS 빌드, 클라이언트 이름

서버는 RFB 표준의 화면 갱신 메시지에 영상의 크기와 띠 수를 실어 답합니다.

키를 암호화된 제어 채널로 미리 넘겼기 때문에, UDP 쪽에서는 키를 따로 주고받지 않습니다. 서버는 답을 보내자마자 이 키로 암호화한 영상을 UDP로 보내기 시작합니다.

클라이언트 정보는 프레임레이트를 바꿉니다. iPad 모델(iPad16,6)과 iPadOS 빌드로 알리면 서버가 30fps로 묶고, Mac 모델(Mac16,5)과 macOS 빌드로 알리면 60fps로 보냅니다. 모델과 빌드 중 어느 쪽이 기준인지는 아직 모릅니다.

UDP로 영상과 소리 받기

UDP와 SRTP

UDP는 TCP와 달리 잃어버린 패킷을 다시 보내지 않고, 도착 순서도 보장하지 않습니다. 대신 패킷 하나를 잃어도 뒤의 영상과 소리가 멈춰 기다리지 않고, 언제 어떻게 회복할지는 클라이언트와 서버가 정합니다.

영상은 UDP 5901번, 소리는 UDP 5900번 포트로 옵니다. Apple Remote Desktop 사용 설명서는 UDP 5900~5902번으로 통신할 수 있어야 한다고 적었습니다. 그러나 클라이언트가 두 번째 영상 스트림을 요청하지 않으면 5900번과 5901번만으로 동작합니다.

UDP 위에서는 표준 RTP(RFC 3550)가 패킷마다 순번, 시각, 스트림 번호(SSRC)를 붙입니다. 받는 쪽은 순번으로 빠진 패킷을 알아채고, SSRC로 어느 스트림의 패킷인지 가립니다.

고성능 화면 공유는 여기에 표준 암호화 방식인 SRTP(RFC 3711)를 씌웁니다.

  • 암호화: RTP 헤더는 그대로 두고, 그 뒤의 내용만 AES-256으로 암호화합니다(RFC 6188).
  • 위조 방지: HMAC-SHA1 값을 붙여, 키 없이 만든 패킷을 걸러냅니다.

HEVC 4:4:4

영상 코덱은 기본으로 HEVC(H.265)이고, 그중에서도 색을 줄이지 않는 4:4:4 방식을 씁니다.

영상은 보통 밝기와 색을 나눠 저장하고, 색은 가로세로 절반 해상도로만 저장하는 4:2:0을 씁니다. 사람 눈이 밝기보다 색의 변화에 둔해서 영화에서는 티가 잘 나지 않지만, 화면 공유에서는 색이 있는 글자와 얇은 선의 가장자리가 번져 보입니다. 4:4:4는 색도 밝기와 같은 해상도로 저장해서 작은 글자도 선명하게 남깁니다.

그러나 PC에서는 GPU와 디코딩 API에 따라 HEVC 4:4:4를 하드웨어로 풀지 못하는 경우가 많습니다. 그런 클라이언트는 제안의 코덱 후보를 바꿔 H.264 4:2:0 영상을 받을 수 있습니다.

HEVC 영상을 RTP 패킷에 나눠 담는 방식은 표준(RFC 7798)을 조금 바꾼 것이라서, 표준대로 읽으면 영상이 깨집니다.

띠 4개로 나눈 화면

서버는 화면을 가로 띠 4개로 나누고, 띠마다 RTP 스트림을 따로 만듭니다. 네 스트림은 연속된 SSRC를 받고, 가장 작은 번호가 맨 위 띠입니다. 띠 수는 보통 클라이언트가 제안에 적은 값을 따릅니다.

Mac 화면의 띠 4개가 각자 다른 SSRC 스트림으로 보내져 디코더 하나로 들어가고, 클라이언트 화면에서 다시 쌓이는 그림
띠는 스트림 4개로 나뉘어 오지만 디코더는 하나만 씁니다

다른 프레임을 참조하지 않는 키프레임은 맨 위 띠에만 오고, 나머지 띠는 다른 띠의 프레임을 참조해서 만들어집니다. 네 띠를 따로 디코딩할 수 없어서 클라이언트는 네 스트림을 모두 디코더 하나에 넣습니다. 디코딩한 띠는 위에서부터 차례로 쌓아 화면 하나를 만듭니다.

소리

소리는 통화처럼 지연이 짧아야 하는 곳에 쓰는 48kHz 스테레오 AAC-ELD로 옵니다. 패킷 하나에 10ms 분량이 담겨 초당 100개씩 오고, 소리가 없을 때도 무음 패킷이 계속 옵니다.

잃어버린 패킷 복구하기

잃은 패킷을 되찾으려면 클라이언트가 서버에 요청해야 합니다. 요청은 RTP와 짝을 이루는 RTCP로 UDP 5900번에 보내고, RTCP도 SRTP와 같은 방식(SRTCP)으로 암호화합니다.

클라이언트가 서버에 보내는 RTCP는 주로 세 가지입니다.

  • 수신 보고: 얼마나 잃었는지, 도착 간격이 얼마나 흔들리는지 서버에 알립니다.
  • 손실 알림: 잃은 패킷의 순번을 알려, 서버가 다시 보낼 수 있게 합니다.
  • 키프레임 요청: 키프레임을 새로 보내 달라고 합니다.

영상 코덱은 대부분의 프레임을 앞 프레임과의 차이로 만들어서, 프레임 하나를 잃으면 다음 키프레임이 올 때까지 뒤 프레임이 제대로 풀리지 않습니다. Apple의 영상 디코딩 프레임워크(VideoToolbox)도 참조할 프레임이 빠지면 다음 키프레임까지 오는 프레임을 모두 거부합니다.

HEVC 영상에서는 손실 알림을 보내도 서버가 패킷을 다시 보내지 않아서, 클라이언트는 순번이 빈 것을 보는 즉시 키프레임을 요청합니다.

키프레임 요청은 Apple의 화면 공유 앱이 쓰는 옛 형식(RFC 2032)과 새 형식을 한 번에 묶어 보냅니다. 서버가 새 형식에도 늘 답하는지는 확실하지 않기 때문입니다.

VPN을 거쳐 초당 2,600패킷쯤 받는 경로에서는 손실이 잦았고, 키프레임으로 복구하는 데 걸린 시간은 이랬습니다.

  • 키프레임은 요청하고 25~35ms 안에 왔습니다.
  • 손실에서 회복하기까지 중앙값 0.07초, 최대 0.6초가 걸렸습니다.

제안에는 키프레임 말고 장기 참조 프레임(LTR)이라는 회복 방법도 들어 있습니다. 클라이언트가 "이 프레임은 잘 받았다"고 알려 두면, 손실 뒤 서버가 키프레임 대신 그 프레임을 참조해 다음 프레임을 만드는 방식입니다.

그러나 HEVC 영상에서는 손실 뒤 300ms를 기다려도 서버가 장기 참조 프레임으로 회복시켜 주지 않았습니다. 제안의 코덱 후보 중 장기 참조 프레임 표시가 붙은 것은 H.264뿐이어서, HEVC에서는 이 방법을 쓰지 않는 것일 수도 있습니다.

입력과 커서

키보드와 마우스

입력은 모두 TCP 제어 채널로 보냅니다.

  • 키보드: RFB 표준의 키 이벤트를 그대로 씁니다. 누른 키는 X11에서 정한 키 번호(keysym)로 보내고, 한글 같은 유니코드 글자는 0x01000000에 그 글자의 유니코드 번호를 더해 보내면 입력됩니다.
  • 마우스: Apple이 따로 만든 메시지를 쓰고, 좌표와 버튼을 담은 부분은 레코드로 감싸기 전에 한 번 더 암호화합니다. 오른쪽 버튼과 가운데 버튼의 번호도 RFB 표준과 반대입니다.
  • 스크롤: 스크롤 이벤트 하나에 Mac 화면이 1픽셀만 움직여서, 휠 한 칸에 이벤트를 12개쯤 묶어 보냅니다.

레티나 디스플레이의 Mac은 화면을 논리 크기로 배치하고, 실제로는 가로세로 2배 픽셀로 그립니다(예: 1512×982로 보이는 화면을 3024×1964 픽셀로). 마우스 좌표는 이 논리 크기가 아니라 실제 픽셀을 기준으로 합니다.

커서

서버는 커서 모양을 영상과 따로 TCP로 보내고, 클라이언트는 이 모양을 자기 포인터 위치에 그립니다. 영상이 늦게 와도 커서는 바로 움직입니다. 제안에는 커서를 영상에 그리지 말아 달라는 표시도 넣습니다.

커서 모양은 번호를 붙여 한 번만 보내고, 같은 모양이 다시 필요하면 번호만 보냅니다.

클라이언트에 맞춘 가상 디스플레이

클라이언트가 연결할 때 원하는 화면의 논리 크기와 픽셀 크기를 알리면, Mac은 그 크기의 가상 디스플레이를 만들어 영상으로 보냅니다. 알리지 않으면 실제 화면을 그대로 보냅니다. Apple 문서에 따르면 Mac에 로그인해 있는 사용자로 붙을 때는 Mac에 연결된 화면이 꺼집니다.

연결한 뒤에 크기를 바꾸는 순서는 이렇습니다.

  1. 클라이언트가 새 크기를 보냅니다.
  2. 서버가 바뀐 화면 배치를 알려 옵니다.
  3. 클라이언트가 영상과 소리 제안을 다시 보냅니다. 이 단계를 빼면 서버가 인코딩을 멈춰서 화면이 검게 멈춥니다.
  4. 서버가 새 SSRC와 새 키프레임으로 영상을 다시 보내기 시작합니다.

요청한 크기에 따라 디스플레이의 배율과 프레임레이트가 달라집니다.

  • 픽셀 크기가 논리 크기의 2배일 때만 레티나 디스플레이가 되고, 1.72배처럼 2배가 아니면 논리 크기 그대로인 1배 디스플레이가 됩니다.
  • Apple 문서는 가상 디스플레이의 최대 크기를 4K(3840×2160)로 적었습니다. 그러나 클라이언트가 최대 크기를 5120×2880으로 적어 보내면 3840×2880 같은 더 큰 크기도 받아들입니다.
  • 대신 Mac 모델로 알려 60fps를 받을 때도, 크기가 크면 30fps로 떨어집니다. 픽셀 수가 4K보다 적은 3200×2400은 60fps, 4K보다 많은 3840×2880은 30fps입니다.
  • 120Hz를 요청해도 60fps를 넘지 않습니다.

연결 끊기

연결을 끝낼 때 보내는 작별 메시지는 없어서, 클라이언트는 TCP 연결을 닫는 것으로 세션을 끝냅니다. TCP 연결은 "다 보냈다"고 알리며 닫거나(FIN) 바로 끊어 버릴 수(RST) 있는데, 고성능 화면 공유에서는 둘의 결과가 다릅니다.

FIN으로 닫으면 끝낸 세션의 영상이 계속 오고, RST로 닫으면 바로 멈춥니다. 서버가 FIN을 받고 인코더를 정리하기까지 약 75초가 걸리기 때문입니다.

글을 마치며

macOS 고성능 화면 공유는 두 갈래로 나뉩니다. TCP 쪽은 RFB에 Apple만의 인증, 암호화, 메시지를 더한 제어 채널입니다. 화면과 소리는 RTP, SRTP, RTCP 같은 표준을 타고 UDP로 옵니다.

영상은 Apple식으로 패킷에 담은 HEVC이고, 띠 4개로 나뉘어 옵니다. 클라이언트는 네 띠를 디코더 하나로 풀고, 잃은 패킷은 재전송 대신 키프레임으로 회복합니다.

여러분은 원격으로 Mac을 쓸 때 무엇이 가장 아쉬웠나요?

See Also

댓글