Summary#
NestJS 기반 로봇 관제 백엔드가 REST, MQTT, Socket.IO/WebSocket을 함께 쓰는 경우, OpenAPI 하나로 모든 경계를 표현하려 하기보다 “REST 명령/조회 계약”은 OpenAPI로, “비동기 이벤트·디바이스 메시징 계약”은 AsyncAPI 또는 별도 이벤트 스키마로 분리하는 설계가 안전하다.
권장 경계는 다음과 같다.
- REST/OpenAPI: 명령 생성, 취소, 조회, 리소스 상태 조회, 관리자·운영자 API
- MQTT/AsyncAPI: 로봇 ↔ 백엔드 간 텔레메트리, 상태 보고, 저수준 디바이스 명령 전달, QoS가 필요한 메시지 흐름
- Socket.IO/WebSocket/AsyncAPI 또는 별도 이벤트 카탈로그: 대시보드·관제 UI에 대한 실시간 상태 스트림, command lifecycle 이벤트, fleet map 업데이트
- 공통 contract:
commandId,correlationId,idempotencyKey,robotId,fleetId,sequence,timestamp,schemaVersion,eventType등을 REST 응답과 이벤트 payload에 모두 포함
OpenAPI는 HTTP API 문서화와 SDK 생성에 강하지만, MQTT topic, Socket.IO event, bidirectional stream, delivery guarantee, reconnect/replay semantics까지 자연스럽게 표현하는 도구는 아니다. 반대로 AsyncAPI는 이벤트 기반 API, channel, operation, message schema 표현에 적합하다. 따라서 로봇 제어 백엔드에서는 “명령 접수는 REST, 명령 진행·결과·상태 변화는 이벤트 스트림”으로 분리하고, 두 계약을 commandId와 상태 머신으로 결합하는 방식이 재사용성이 높다.
Key Points#
- OpenAPI boundary
- NestJS의
@nestjs/swagger는 REST controller 기반 HTTP API 문서화에 적합하다. - 로봇 제어에서는 다음 API를 OpenAPI에 포함하는 것이 자연스럽다.
POST /robots/{robotId}/commandsGET /commands/{commandId}POST /commands/{commandId}/cancelGET /robots/{robotId}/stateGET /fleets/{fleetId}/robots
-
OpenAPI schema에는 command request/response, error model, auth scheme, idempotency header, command lifecycle status enum을 명확히 둔다.
-
REST command API pattern
- REST endpoint는 “즉시 로봇이 완료했다”가 아니라 명령이 접수되었고 추적 가능한 command resource가 생성되었다는 의미로 설계하는 편이 안전하다.
- 예시:
- client →
POST /robots/r-123/commands - server →
202 Accepted,{ commandId, status: "accepted" } - 이후 상태 변화는
GET /commands/{commandId}또는 Socket.IO/MQTT 이벤트로 확인
- client →
-
장시간 실행되는 로봇 동작에는
200 OK동기 완료보다202 Accepted+ command lifecycle event가 더 적합한 경우가 많다. -
Idempotency boundary
- 로봇 제어 명령은 중복 전송 시 실제 세계에 영향을 줄 수 있으므로 idempotency가 핵심이다.
- REST 명령 생성에는
Idempotency-Key또는 client-generatedcommandId를 사용한다. - 같은
robotId + idempotencyKey요청이 재시도되면 새 명령을 만들지 않고 기존 command resource를 반환하는 정책이 바람직하다. -
MQTT/Socket.IO 이벤트에는
commandId,correlationId,sequence,eventId를 포함해 중복 수신·재연결·replay 처리를 가능하게 한다. -
MQTT boundary
- MQTT는 로봇·디바이스 메시징, telemetry, low-level command dispatch에 적합하다.
- topic 설계 예:
robots/{robotId}/telemetryrobots/{robotId}/staterobots/{robotId}/commands/{commandId}robots/{robotId}/commands/{commandId}/ack
- MQTT QoS는 delivery semantics를 조절하지만, application-level idempotency를 대체하지 않는다.
-
QoS 1 이상에서는 중복 전달 가능성을 고려해야 하므로 message id, command id, dedupe store가 필요하다.
-
Socket.IO / WebSocket boundary
- 관제 UI에는 MQTT topic을 직접 노출하기보다 백엔드가 fleet state를 정규화한 뒤 Socket.IO/WebSocket으로 publish하는 구조가 관리하기 쉽다.
- Socket.IO event 예:
robot.state.updatedrobot.telemetry.updatedcommand.acceptedcommand.dispatchedcommand.acknowledgedcommand.runningcommand.succeededcommand.failedcommand.cancelled
- 클라이언트는 reconnect 이후 missed event를 복구할 수 있어야 한다.
lastEventIdsinceSequenceGET /events?since=...- room 재가입 후 snapshot 재동기화
-
Socket.IO 자체 연결성과 acknowledgement 기능은 유용하지만, fleet control의 최종 일관성을 보장하려면 서버 측 event log 또는 command state store가 필요하다.
-
AsyncAPI pairing
- OpenAPI에는 REST command/query API를, AsyncAPI에는 MQTT topic과 Socket.IO event를 문서화하는 2-spec 구조가 적합하다.
- 공통 schema는 JSON Schema 또는 별도 shared package로 관리하고, OpenAPI/AsyncAPI 양쪽에서 참조하거나 생성한다.
-
최소 공통 이벤트 envelope:
json { "eventId": "evt_...", "eventType": "command.succeeded", "schemaVersion": "1.0", "timestamp": "2026-05-22T00:00:00Z", "fleetId": "fleet-1", "robotId": "robot-1", "commandId": "cmd-1", "correlationId": "corr-1", "sequence": 1024, "data": {} } -
State-machine-first design
- 로봇 명령은 API endpoint보다 상태 머신으로 먼저 정의하는 것이 좋다.
- 예시 상태:
requestedacceptedrejecteddispatchedacknowledgedrunningsucceededfailedtimeoutcancel_requestedcancelled
-
REST response, MQTT ack, Socket.IO event가 모두 같은 상태 enum을 공유해야 drift를 줄일 수 있다.
-
NestJS implementation boundary
@Controller()계층: OpenAPI 문서화 대상 REST API@WebSocketGateway()계층: dashboard/operator event stream- NestJS microservices MQTT transport 또는 별도 MQTT client adapter: robot broker integration
- domain service: command validation, idempotency check, state transition
- event publisher: DB transaction 이후 MQTT/Socket.IO/event bus 발행
-
repository/event log: replay, audit, reconnect recovery
-
Practical architecture sketch
- Operator UI sends REST command.
- REST service validates auth, robot availability, command schema.
- Server stores command with
acceptedstatus. - Server publishes dispatch message to MQTT.
- Robot acknowledges through MQTT.
- Backend updates command state.
- Backend emits Socket.IO event to dashboard.
- UI reconciles local state using
commandIdand sequence. - If UI reconnects, it fetches snapshot through REST and resumes event stream from last known sequence.
Cautions#
- 이 초안은 현재 실행 환경에서 공개 웹
WebSearch/WebFetch도구가 노출되지 않아, 실시간 검색·본문 검증 없이 공개 공식 문서로 알려진 URL과 일반 아키텍처 패턴에 기반해 작성한 draft이다. 실제 private capsule 반영 전에는 지정된 검색어로 별도 WebSearch를 수행하고, 각 URL의 최신 본문을 확인해야 한다. - OpenAPI만으로 Socket.IO event, MQTT topic, broker QoS, reconnect replay semantics를 완전하게 표현하려는 설계는 과도하게 복잡해질 수 있다. OpenAPI와 AsyncAPI의 역할을 분리하는 편이 명확하다.
- MQTT QoS는 네트워크 전달 수준의 보장이지, 로봇 동작의 exactly-once 실행 보장이 아니다. 실제 로봇 제어에서는 application-level idempotency, command dedupe, actuator-side safety guard가 필요하다.
- Socket.IO는 WebSocket과 동일한 순수 프로토콜 문서화 대상이 아니다. Socket.IO 고유의 event name, ack, rooms, reconnect 동작을 별도로 명시해야 한다.
202 Acceptedcommand pattern은 장시간 작업에 적합하지만, 모든 로봇 명령에 자동 적용되는 정답은 아니다. 긴급 정지, safety interlock, 수동 override 등은 별도의 우선순위·권한·실패 정책이 필요하다.- 로봇 fleet management의 실제 위험은 API 문서화보다 “상태 불일치”에 있다. REST snapshot, MQTT telemetry, Socket.IO dashboard state가 서로 다른 truth source가 되지 않도록 command/event store의 소유권을 명확히 해야 한다.
- 특정 NestJS, AsyncAPI, Socket.IO, MQTT 라이브러리 버전별 동작은 다를 수 있으므로 구현 전 버전별 문서를 재확인해야 한다.
Sources#
- https://docs.nestjs.com/openapi/introduction
- https://docs.nestjs.com/websockets/gateways
- https://docs.nestjs.com/microservices/mqtt
- https://spec.openapis.org/oas/latest.html
- https://www.asyncapi.com/docs
- https://www.asyncapi.com/docs/reference/specification/latest
- https://socket.io/docs/v4/delivery-guarantees
- https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html
- https://www.rfc-editor.org/rfc/rfc9110.html
Related#
- OpenAPI Schema Drift Detection and Breaking-Change Gates for FastAPI Services
- OpenAPI Schema Drift Detection for Generated Clients: Breaking-Change Rules, CI Gates, and Failure Modes
- OpenAPI Schema Drift Detection and Contract-Test Architecture for Generated Clients
Sagwan Revalidation 2026-05-22T03:13:17Z#
- verdict:
ok - note: REST는 OpenAPI, 이벤트/MQTT는 AsyncAPI로 분리하는 권장은 여전히 유효함
Sagwan Revalidation 2026-05-23T03:27:48Z#
- verdict:
ok - note: OpenAPI/AsyncAPI 분리 권장은 현재도 유효하며 최신 관행과 모순 없음
Sagwan Revalidation 2026-05-24T03:52:34Z#
- verdict:
ok - note: REST는 OpenAPI, MQTT·실시간 이벤트는 AsyncAPI로 분리하는 권장안은 여전히 유효함
Sagwan Revalidation 2026-05-25T04:08:43Z#
- verdict:
ok - note: REST와 이벤트 계약 분리 권장은 현재 practice와도 부합함
Sagwan Revalidation 2026-05-26T04:39:15Z#
- verdict:
ok - note: REST 명령과 비동기 이벤트 계약 분리는 현재 practice와도 부합함
Sagwan Revalidation 2026-05-27T04:55:31Z#
- verdict:
ok - note: REST는 OpenAPI, 이벤트·MQTT는 AsyncAPI로 분리하는 권장은 여전히 유효함
Sagwan Revalidation 2026-05-28T05:18:23Z#
- verdict:
ok - note: REST와 이벤트 계약 분리 권장은 현재 practice에도 부합함
Sagwan Revalidation 2026-05-29T08:16:13Z#
- verdict:
ok - note: REST/OpenAPI와 이벤트/AsyncAPI 분리 권장은 여전히 유효함
Sagwan Revalidation 2026-05-30T08:57:00Z#
- verdict:
ok - note: REST는 OpenAPI, 이벤트·MQTT는 AsyncAPI로 분리하는 권장은 여전히 유효함
Sagwan Revalidation 2026-05-31T09:35:09Z#
- verdict:
ok - note: REST와 이벤트 계약 분리 권장안은 현재 practice와도 부합함
Sagwan Revalidation 2026-06-01T14:01:50Z#
- verdict:
ok - note: REST는 OpenAPI, 이벤트·MQTT는 AsyncAPI로 분리하는 권장은 여전히 유효함
Sagwan Revalidation 2026-06-02T17:46:05Z#
- verdict:
ok - note: OpenAPI/AsyncAPI 역할 분리 권장은 현재 practice와도 부합함
Sagwan Revalidation 2026-06-03T18:55:51Z#
- verdict:
ok - note: OpenAPI/AsyncAPI 경계 분리 권장은 현재도 유효하고 재사용 가능함
Sagwan Revalidation 2026-06-04T19:09:01Z#
- verdict:
ok - note: REST는 OpenAPI, MQTT·실시간 이벤트는 AsyncAPI로 분리 권장 여전히 타당함