현재 NestJS 서버는 Zenoh 네트워크와 통신하기 위해 zenoh-plugin-remote-api를 사용한다.
여기서 중요한 점은 NestJS가 Zenoh Router의 Native WebSocket Listener에 직접 연결되는 것이 아니라, Remote API가 제공하는 WebSocket Endpoint에 연결한다는 것이다.
전체적인 구조는 다음과 같다.
NestJS
│
│ WebSocket
▼
zenoh-plugin-remote-api
│
▼
Zenoh Router
│
▼
Robot / Edge Device
NestJS에서는 @eclipse-zenoh/zenoh-ts 패키지를 사용해 Zenoh Session을 생성한다.
코드상으로는 NestJS가 Zenoh Session을 직접 사용하는 것처럼 보이지만, 내부적으로는 Remote API Endpoint와 WebSocket 연결을 생성하고 그 연결을 통해 Zenoh 요청과 메시지를 주고받는다.
@eclipse-zenoh/zenoh-ts는 Node.js와 TypeScript 환경에서 Zenoh를 사용하기 위한 클라이언트 라이브러리다.
현재 프로젝트에서는 이 클라이언트가 zenoh-plugin-remote-api의 WebSocket Endpoint에 연결하는 방식으로 Zenoh를 사용한다.
따라서 NestJS 코드에서 Session.open()을 호출하면 내부적으로 다음과 같은 과정이 진행된다.
Session.open()
│
▼
Remote API WebSocket 연결 생성
│
▼
Zenoh Session 생성
│
▼
Zenoh 기능 사용 가능
Session이 생성된 이후에는 해당 Session을 통해 다음과 같은 Zenoh 기능을 사용할 수 있다.
즉, 애플리케이션에서는 Zenoh API를 사용하지만 실제 통신은 Remote API와 연결된 WebSocket 위에서 이루어진다.
NestJS에서는 RobotZenohService가 Zenoh 연결을 담당한다.
이 서비스는 @eclipse-zenoh/zenoh-ts를 불러오고, 설정된 Remote API 주소를 기반으로 Zenoh Session을 생성한다.
전체적인 초기화 흐름은 다음과 같다.
RobotZenohService 초기화
│
▼
zenoh-ts 로드
│
▼
Remote API WebSocket Endpoint 설정
│
▼
Session.open()
│
▼
Subscriber 등록
│
▼
Robot 메시지 수신 시작
Session.open()이 성공하면 NestJS는 Zenoh Session을 사용할 수 있는 상태가 된다.
이후 해당 Session을 이용해 Robot이 보내는 데이터를 구독하거나 Robot으로 명령을 발행한다.
예를 들어 Robot의 상태 정보를 수신하기 위해 다음과 같은 Key Expression을 구독할 수 있다.
robot/{robotId}/state
robot/{robotId}/navigation/state
서버에서 Robot으로 명령을 전달할 때도 같은 Session을 사용한다.
robot/{robotId}/command/move
robot/{robotId}/command/path
이 토픽들은 실제 프로젝트 토픽이 아니라 통신 구조를 설명하기 위한 예시다.
NestJS 코드에서는 일반적인 Zenoh API를 사용하는 것처럼 보이지만, 실제 메시지는 Remote API와 연결된 WebSocket을 통해 전달된다.
Remote API 연결은 WebSocket을 기반으로 하기 때문에 네트워크 문제나 Remote API 재시작 등으로 인해 연결이 끊길 수 있다.
일반적으로는 Zenoh Session 상태를 통해 연결 종료를 감지할 수 있다. 하지만 일부 비정상적인 단절 상황에서는 Session 상태만으로 연결 문제를 즉시 확인하기 어려울 수 있다.
이를 보완하기 위해 현재 구조에서는 zenoh-ts 내부에서 사용하는 WebSocket의 close 및 error 이벤트도 함께 감지한다.
Zenoh Session 상태 확인
│
├─ Session 오류 감지
│
└─ WebSocket close/error 감지
│
▼
연결 종료 처리
이처럼 Session과 내부 WebSocket 상태를 함께 확인하면 Remote API 연결이 비정상적으로 종료된 경우에도 NestJS가 빠르게 문제를 감지할 수 있다.
Remote API와의 WebSocket 연결이 끊기면 기존 Zenoh 리소스를 정리하고 새로운 연결을 생성한다.
재연결 과정은 다음과 같다.
WebSocket close/error 감지
│
▼
기존 Zenoh Session 종료
│
▼
등록된 Subscriber 정리
│
▼
일정 시간 대기
│
▼
Remote API Endpoint로 재연결
│
▼
Subscriber 재등록
기존 Session과 Subscriber를 먼저 정리하는 이유는 재연결 과정에서 동일한 Subscriber가 중복으로 등록되는 것을 방지하기 위해서다.
Subscriber가 중복으로 등록되면 하나의 메시지가 여러 번 처리되거나 동일한 이벤트가 반복해서 발생할 수 있다.
따라서 재연결 전에는 기존 연결과 관련된 리소스를 확실하게 정리해야 한다.
연결이 실패했다고 해서 즉시 무한 반복으로 재연결을 시도하지는 않는다.
Remote API나 Zenoh Router가 재시작 중이라면 짧은 시간 동안 연결할 수 없는 상태가 계속될 수 있다. 이때 반복적으로 연결을 시도하면 불필요한 CPU 및 네트워크 사용이 발생한다.
이를 방지하기 위해 재연결 시도 사이에 대기 시간을 두는 Backoff 방식을 사용한다.
첫 번째 실패
↓
짧은 시간 대기
연속 실패
↓
대기 시간 증가
연결 성공
↓
대기 시간 초기화
처음에는 짧은 간격으로 재연결을 시도하고, 실패가 반복되면 점차 대기 시간을 늘린다.
연결에 성공하면 재연결 횟수와 대기 시간을 초기화하고 정상적인 메시지 구독을 다시 시작한다.
WebSocket의 close와 error 이벤트만으로 모든 연결 장애를 감지할 수 있는 것은 아니다.
TCP 또는 WebSocket 연결이 형식적으로는 남아 있지만, 실제로 메시지를 주고받을 수 없는 비정상 상태가 발생할 수 있기 때문이다.
이를 확인하기 위해 NestJS에서는 주기적으로 가벼운 Zenoh 요청을 보내 연결 상태를 검사한다.
주기적으로 Health Check 요청
│
├─ 성공 → 연결 유지
│
└─ 실패 → 연결 이상으로 판단
│
▼
Session 정리 및 재연결
Health Check는 WebSocket 이벤트가 발생하지 않은 채 연결이 비정상적으로 멈춰 있는 상황을 감지하기 위한 보조 수단이다.
따라서 현재 구조에서는 다음과 같은 신호를 함께 사용해 연결 상태를 판단한다.
close 이벤트error 이벤트여러 감지 방식을 함께 사용하면 네트워크 장애나 Remote API 재시작 상황에 더 안정적으로 대응할 수 있다.
NestJS와 Zenoh 사이의 전체 연결 흐름을 정리하면 다음과 같다.
┌──────────────────────────┐
│ NestJS │
│ │
│ RobotZenohService │
│ Session / Subscriber │
└────────────┬─────────────┘
│
│ WebSocket
▼
┌──────────────────────────┐
│ zenoh-plugin-remote-api │
└────────────┬─────────────┘
│
│ Zenoh
▼
┌──────────────────────────┐
│ Zenoh Router │
└────────────┬─────────────┘
│
▼
┌──────────────────────────┐
│ Robot / Edge Device │
└──────────────────────────┘
NestJS가 직접 Zenoh Router의 Native Transport에 연결되는 것은 아니다.
NestJS는 Remote API가 제공하는 WebSocket Endpoint에 연결하고, Remote API가 NestJS의 요청을 Zenoh 네트워크로 전달한다.
현재 NestJS 서버는 Zenoh와 통신하기 위해 zenoh-plugin-remote-api의 WebSocket Endpoint를 사용한다.
NestJS에서는 @eclipse-zenoh/zenoh-ts를 통해 Session을 생성하며, 생성된 Session으로 Publish, Subscribe, Query 등의 Zenoh 기능을 사용한다.
코드에서는 일반적인 Zenoh API를 사용하는 것처럼 보이지만, 실제 통신은 다음 경로를 거친다.
NestJS
↓
Remote API WebSocket
↓
Zenoh Router
↓
Robot / Edge Device
또한 WebSocket 연결은 네트워크 문제나 Remote API 재시작으로 끊길 수 있기 때문에 다음과 같은 연결 감지 및 복구 기능을 함께 사용한다.
close 및 error 이벤트 감지이 구조의 핵심은 NestJS가 Zenoh Router에 직접 연결되는 것이 아니라, Remote API의 WebSocket 연결을 통해 Zenoh 네트워크에 접근한다는 점이다.
zenoh remote api websocket연결 이벤트 핸들러 등록...
zenoh.session() 형태로 사용되기 때문에 우리는 zenoh session값을 받을뿐 websocket에 대한 내용을 가져올 순 없다.