로봇이나 IoT 시스템을 개발하다 보면 여러 장치와 서비스 사이에서 데이터를 실시간으로 전달해야 하는 상황이 자주 발생한다.
예를 들어 자율주행 로봇 시스템에서는 다음과 같은 데이터가 끊임없이 오간다.
처음에는 HTTP나 WebSocket만으로도 시스템을 구성할 수 있다. 하지만 연결되는 로봇과 서비스가 많아지면 서버가 각 연결과 메시지 전달 경로를 직접 관리해야 하고, 요청·응답과 연결 상태 확인을 위한 별도의 규칙도 필요해진다.
이러한 분산 환경에서 사용할 수 있는 통신 프로토콜 중 하나가 Zenoh다.
Zenoh는 데이터를 발행하고 구독하는 Pub/Sub 방식과 필요한 데이터를 요청하는 Query 방식을 하나의 프로토콜에서 제공한다.
Zenoh 공식 문서에서는 Zenoh를 다음 요소를 통합하는 Pub/Sub/Query 프로토콜로 설명한다.
즉, 단순히 메시지를 전달하는 통신 채널을 넘어 실시간 데이터 전달, 데이터 조회, 저장소 연동을 하나의 데이터 공간에서 다루도록 설계된 프로토콜이다.
Zenoh라는 이름은 Zero Overhead Network Protocol에서 유래했다. 여기서 Zero Overhead는 실제 오버헤드가 완전히 0이라는 의미보다는, 다양한 네트워크와 장치 환경에서 통신 비용을 작게 유지하겠다는 설계 방향을 나타낸다고 이해하는 편이 적절하다.
Zenoh는 Eclipse Foundation에서 관리되는 오픈소스 프로젝트이며, 데이터센터와 클라우드뿐만 아니라 엣지 장치, 로봇, IoT 장비와 같은 제한된 환경까지 연결하는 것을 목표로 한다. 실제 활용 분야로는 로보틱스, 자동차, IoT, 엣지 컴퓨팅 등이 소개되고 있다.
Zenoh에서는 데이터를 경로 형태의 키로 구분한다.
예를 들어 로봇의 상태를 다음과 같은 키로 표현할 수 있다.
robot/A001/status
robot/A001/battery
robot/A001/position
robot/A001/move/result
Zenoh는 이러한 키와 값을 기반으로 데이터를 전달한다. 값에는 문자열, JSON, 바이너리 데이터 등 다양한 형식을 사용할 수 있다.
특정 로봇의 배터리 상태를 받고 싶다면 다음 키를 구독할 수 있다.
robot/A001/battery
모든 로봇의 상태를 받고 싶다면 와일드카드가 포함된 Key Expression을 사용할 수 있다.
robot/*/status
또는 로봇과 관련된 모든 데이터를 구독할 수도 있다.
robot/**
Zenoh Router는 메시지의 Key Expression을 기준으로 데이터를 필요한 Subscriber에게 전달한다. 이 덕분에 Publisher는 데이터를 누가 받는지 직접 알 필요가 없다.
Publisher가 특정 키로 데이터를 발행하면 해당 키를 구독하고 있는 Subscriber가 데이터를 전달받는다.
AMR
└─ robot/A001/status 발행
├─ 관제 서버
├─ 상태 관리 서비스
├─ 로그 수집 서비스
└─ 모니터링 UI
로봇은 자신의 데이터를 누가 사용하는지 알 필요가 없다. 새로운 서비스가 추가되더라도 로봇 프로그램을 수정하지 않고 동일한 키를 구독하면 된다.
이러한 구조를 사용하면 데이터 생산자와 소비자 사이의 의존성을 낮출 수 있다.
Pub/Sub는 실시간으로 발생하는 데이터를 전달하는 데 적합하다. 하지만 특정 시점의 정보가 필요할 때는 Query를 사용할 수 있다.
예를 들어 서버가 특정 로봇의 현재 네트워크 설정을 조회한다고 가정해 보자.
Query:
robot/A001/network/current
로봇이나 Agent는 해당 키에 Queryable을 등록하고 요청을 받았을 때 결과를 반환한다.
WebSocket으로 같은 기능을 구현하려면 일반적으로 요청 ID, 응답 매칭, 타임아웃, 오류 응답 규칙을 직접 설계해야 한다. Zenoh에서는 Query와 Queryable이라는 추상화를 통해 요청·응답 통신을 제공한다.
Zenoh Storage는 특정 Key Expression을 구독하여 데이터를 저장하고, Query 요청이 들어왔을 때 저장된 최신 값을 반환할 수 있다.
예를 들어 로봇이 주기적으로 상태를 발행한다고 가정해 보자.
robot/A001/status
이 데이터를 Storage에 저장하면 새롭게 접속한 서비스가 Query를 통해 최근 상태를 조회할 수 있다. 이후에는 Subscriber를 통해 실시간 변경을 계속 전달받을 수 있다.
Zenoh의 Storage는 Subscriber와 Queryable을 결합한 구조로 설명할 수 있다. 다만 Zenoh Router를 실행한다고 해서 데이터가 자동으로 영구 저장되는 것은 아니며, Storage Plugin이나 별도의 저장소 구성이 필요하다.
WebSocket 기반 시스템에서는 서버가 연결된 클라이언트를 직접 관리하는 경우가 많다.
robotId → WebSocket 연결
특정 로봇의 데이터를 여러 서비스에 전달하려면 서버가 수신자를 찾아 각각 메시지를 보내야 한다.
Zenoh에서는 Publisher가 Key Expression을 기준으로 데이터를 발행하고 Subscriber가 필요한 데이터를 선택해서 구독한다.
Publisher → Zenoh Network → Subscriber
Publisher는 Subscriber의 주소, 개수 또는 위치를 몰라도 된다. 새로운 모니터링 서비스나 분석 서비스가 추가되어도 기존 Publisher를 수정할 필요가 줄어든다.
분산 시스템에서는 실시간 데이터뿐 아니라 현재 상태를 조회해야 하는 경우도 많다.
일반적인 시스템에서는 역할에 따라 여러 기술을 조합한다.
실시간 상태 전달 → WebSocket
상태 조회 → HTTP API
메시지 전달 → Message Broker
최근 값 조회 → Redis 또는 Database
Zenoh에서는 Pub/Sub, Query/Reply, Storage를 동일한 Key/Value 데이터 공간에서 사용할 수 있다.
물론 Zenoh가 데이터베이스나 HTTP API를 모두 대체한다는 의미는 아니다. 다만 장치와 서비스 간 통신에서 사용해야 하는 프로토콜과 데이터 모델을 줄일 수 있다는 장점이 있다.
Zenoh 애플리케이션은 시스템 환경에 따라 Client, Peer, Router 중심 구조로 배치할 수 있다.
Peer ↔ Peer
Client → Router ← Client
Robot → Edge Router → Central Router → Server
Peer 모드에서는 다른 Peer 또는 Router를 탐색하여 연결할 수 있다. Client 모드에서는 하나 이상의 Router Endpoint를 지정할 수 있다.
이러한 구조는 로봇 내부 네트워크, 공장 내부 네트워크, 원격 서버가 함께 존재하는 시스템에서 유용하다.
다만 Zenoh를 사용한다고 해서 자동으로 장애에 강한 시스템이 되는 것은 아니다. 하나의 Router에 모든 Client가 의존한다면 그 Router는 여전히 단일 장애 지점이 될 수 있다. 고가용성이 필요하다면 여러 Router 연결과 재접속 정책을 별도로 설계해야 한다.
Zenoh에서는 데이터를 단순한 문자열 Topic이 아니라 계층적인 Key Expression으로 관리할 수 있다.
factory/line1/robot/A001/status
factory/line1/robot/A002/status
factory/line2/robot/A003/status
이 구조를 사용하면 서비스마다 필요한 범위만 구독할 수 있다.
factory/line1/robot/*/status
factory/**/robot/A001/**
factory/line2/**
로봇 수와 서비스 수가 증가해도 데이터의 의미와 범위를 비교적 일관된 규칙으로 관리할 수 있다.
Zenoh는 Liveliness 기능을 제공한다. 애플리케이션이 특정 Key Expression에 Liveliness Token을 선언하면 다른 서비스가 해당 Token의 생성과 삭제를 관찰할 수 있다.
예를 들어 다음과 같은 키를 사용할 수 있다.
robot/A001/liveliness
이를 통해 로봇이나 Agent가 현재 Zenoh 네트워크에 참여하고 있는지 추적할 수 있다. Zenoh의 접근 제어 문서에서도 Liveliness Token이 별도의 메시지 유형으로 다뤄지고 있다.
다만 실제 운영 환경에서는 Liveliness 하나만으로 모든 장애를 판별하기보다 다음 방법을 함께 사용하는 것이 안전하다.
Liveliness
+ 마지막 메시지 수신 시간
+ Heartbeat
+ 애플리케이션 Watchdog
전원 강제 종료나 네트워크 단절에서는 장애 감지까지 시간이 걸릴 수 있기 때문이다.
Zenoh는 Rust, Python, C, C++, Java, Kotlin 등의 API를 제공하며, 제한된 장치를 위한 C 기반 Zenoh-Pico도 제공한다.
따라서 중앙 서버뿐 아니라 로봇 제어 PC, 임베디드 장치, 센서 노드 등 다양한 환경에서 같은 데이터 모델을 사용할 수 있다.
WebSocket은 하나의 TCP 연결에서 클라이언트와 서버가 양방향 메시지를 주고받기 위한 프로토콜이다. HTTP 기반 핸드셰이크 후 기본적인 메시지 프레이밍을 제공한다.
반면 Zenoh는 메시지를 주고받는 연결뿐 아니라 다음 기능을 함께 제공한다.
따라서 둘은 완전히 같은 계층의 기술이라고 보기 어렵다.
WebSocket
= 연결된 두 프로그램 사이의 양방향 통신 채널
Zenoh
= 다수의 장치와 서비스를 연결하기 위한 분산 데이터 통신 체계
WebSocket에서도 Pub/Sub나 요청·응답 기능을 구현할 수 있다. 하지만 연결 관리, Topic 규칙, 요청 ID, 타임아웃, 재연결, 메시지 라우팅 등을 애플리케이션이나 서버에서 직접 설계해야 한다.
Zenoh는 이러한 분산 통신 기능을 프로토콜 수준의 추상화로 제공한다.
반대로 브라우저와 웹 서버 사이의 단순한 실시간 통신에서는 WebSocket이 더 간단하고 적합할 수 있다. 브라우저는 WebSocket을 기본적으로 지원하지만, Zenoh를 웹 환경에서 사용하려면 REST Plugin이나 별도의 Bridge가 필요할 수 있다. Zenoh는 REST Plugin을 통해 Key/Value 공간에 HTTP로 접근하는 방법도 제공한다.
따라서 모든 WebSocket을 Zenoh로 교체하기보다는 시스템의 역할에 따라 기술을 구분하는 것이 좋다.
로봇·Agent·백엔드 서비스 간 통신
→ Zenoh
브라우저 UI와 백엔드 간 실시간 통신
→ WebSocket 또는 Socket.IO
대용량 파일 다운로드
→ HTTP 또는 Object Storage
파일 배포 명령과 진행 상태 전달
→ Zenoh
Zenoh는 다음과 같은 환경에서 특히 유용하다.
반대로 서버 한 대와 브라우저 몇 개가 연결되는 단순한 시스템이라면 WebSocket만으로도 충분할 수 있다. Zenoh를 도입하면 Router 운영, Key Expression 설계, 네트워크 모드 설정 등 추가로 관리해야 할 요소가 생기기 때문이다.
Zenoh는 단순히 WebSocket보다 빠른 통신 기술이라고 설명하기에는 부족하다.
Zenoh의 핵심적인 장점은 다수의 장치와 서비스가 데이터를 발행하고, 구독하고, 조회할 수 있는 공통 데이터 공간을 제공한다는 점이다.
WebSocket이 연결된 두 프로그램 사이의 통신 채널이라면 Zenoh는 로봇, Agent, 서버, Storage와 같은 여러 구성 요소를 느슨하게 연결하는 데이터 통신 백본에 가깝다.
특히 로봇과 IoT 시스템처럼 장치 수가 많고 네트워크 구조가 복잡하며, 동일한 데이터를 여러 서비스에서 사용해야 하는 환경에서는 Zenoh의 Pub/Sub, Query, Key Expression, Liveliness 기능이 시스템 구조를 단순화하는 데 도움이 될 수 있다.
다만 Zenoh를 도입한다고 해서 연결 복구, 데이터 유실 처리, Router 이중화와 같은 운영 문제가 자동으로 해결되는 것은 아니다. Zenoh가 제공하는 기능을 이해하고 시스템 특성에 맞는 장애 처리와 데이터 정책을 함께 설계해야 한다.
결국 Zenoh의 가치는 단순한 메시지 전송 성능보다 다음 문장으로 정리할 수 있다.
Zenoh는 분산된 장치와 서비스가 데이터의 위치와 상대방의 연결 구조에 덜 의존하면서 통신할 수 있도록 만들어 주는 프로토콜이다.