구현 상세
이 플랫폼은 다종 로봇 SDK를 공통 API로 묶는 백엔드와, 그 API를 코드 없이 다루는 프론트엔드 두 축으로 이루어집니다. 백엔드에서는 브랜드별 차이를 어디까지 어댑터에 숨길지·상태와 동작 데이터를 어떻게 분리할지가, 프론트엔드에서는 노드 그래프를 어떻게 안전하게 실행하고 그 흐름을 운영자에게 어떻게 통제시킬지가 핵심이었습니다. 아래는 백엔드(공통 인터페이스·포즈 저장 구조·시퀀스 실행 안정화)와 프론트엔드(워크플로우 실행 엔진·3D 제어)를 나눠 정리했습니다.
백엔드 — 통합 제어 API
1. 공통 인터페이스 + 브랜드 어댑터 구조
브랜드별 SDK 차이로 상위 서비스 개발이 복잡해지는 문제를, 공통 제어 계약을 추상 계층으로 분리해 해결했습니다.
- •
core_robot 추상 클래스에 공통 메서드 계약 정의 (연결 · 전원 · 서보 · 모션 · 상태) - •브랜드별 어댑터(JAKA / Doosan / UR / Fairino / UFACTORY / AUBO)가 각 SDK에 맞춰 구현 제공
- •컨트롤러는
ROBOT_SUBCLASSES 매핑으로 robot_type에 따라 구현체를 선택
신규 브랜드 추가 시 컨트롤러 수정 없이 어댑터 클래스와 매핑만 추가하면 되는 플러그인 방식의 확장성을 확보했습니다.
2. 동작(포즈) CRUD + 순서 제어 — Hash + Sorted Set
오퍼레이터가 로봇 모션을 저장·편집·재배열·재사용할 수 있도록, 도메인 요구사항(데이터 + 순서)을 Redis 자료구조에 그대로 매핑했습니다.
- •Redis Hash: 각 모션의 좌표 데이터 저장
- •Redis Sorted Set: 실행 순서를
score로 관리 - •삭제·변경 후
_reorder_scores()로 score를 재정렬해 "중간 모션 삭제 시 순서가 비는" 상태 방지
CRUD 전 구간(save / update / copy / delete / reorder / get)에서 동일한 로봇 식별자 기반 키 스키마를 일관 적용해, 로봇 컨텍스트가 바뀌어도 데이터가 섞이지 않습니다.
3. 저장 시퀀스 일괄 반복 실행 + Doosan 안정화
/run_all_moves로 저장된 시퀀스를 times만큼 자동 반복합니다. 처음에는 모든 브랜드를 동일 방식으로 처리했는데, Doosan에서 반복 실행 시 타임아웃·중간 정지·제어권 반환 불안정이 반복됐습니다.
원인: 반복 루프마다 모션 명령과 대기(mwait)를 처리하는 방식이 SDK 동기화 타이밍과 충돌. 단일 포인트와 다중 포인트를 똑같이 처리해 모션 완료 판정 일관성이 떨어짐.
해결: 실행 구조를 시퀀스 중심으로 바꾸고, 단일 포인트는 `movej`, 다중 포인트는 `movesj` (joint_move_sequence_spline)로 분기. Doosan 전용 실행 경로를 robot_type 분기로 분리해, 복잡한 다중 동작에서도 타임아웃·멈춤 빈도를 줄였습니다.
그 외 구현
- •JWT + Redis 세션 관리: 연결 시 식별 정보를 Redis에 저장하고 JWT 발급, 요청마다
get_robot_from_request()로 인스턴스 복원. "인증 토큰"과 "운영 상태 저장소"를 분리해 토큰은 짧게, 세션 복원은 빠르게. - •운영 관측성:
RotatingFileHandler로 로그 로테이션(디스크 점유 제어), Accept-Language 헤더 기반 국/영 응답 메시지 분기로 상위 앱의 다국어 부담 경감.
트러블슈팅
- •로봇 간 동작 데이터 분리: 다른 로봇에서 저장한 move가 섞일 가능성 → JWT에 로봇 식별 정보를 포함하고
extract_user_key()로 로봇 단위 접두를 생성, 전 구간 일관 적용. - •SDK 호출 차이로 공통 API 불일치: 같은 엔드포인트라도 브랜드별 호출 순서·함수가 달라 일부 실패 → 어댑터 안에 SDK 차이를 격리하고 핵심 엔드포인트에 로봇 타입별 분기 적용.
- •Redis 저장/접속 운영 이슈: 일부 환경(특히 Windows Redis) 동작 편차 →
hset 호출 방식 정비 + 접속 설정 표준화, 실패 로그 강화.
프론트엔드 — 노코드 워크플로우 플랫폼
통합 API를 코드 없이 다룰 수 있도록, 노드 그래프로 로봇 작업을 설계하고 실행하는 클라이언트를 추가 개발하고 있습니다. (Next.js(App Router) · React · TypeScript · Zustand · TanStack Query · React Flow · three.js / urdf-loader · axios · Monaco · next-intl) — R&D 과제 기반으로 점진 개발 중인 프로젝트입니다.
1. 비주얼 플로우 에디터 — 노드 그래프 = 로봇 작업
운영자가 코드 없이 로봇 작업을 구성할 수 있도록, 작업 단위를 노드로 모델링하고 React Flow(@xyflow/react)로 시각화했습니다.
- •노드 타입을
basic / control / sensor / advanced 카테고리로 분류 — 전원·서보·이동·정지·대기·조건·반복·거리·카메라·코드 - •노드별 속성 스키마(
PropertyDefinition: position·angle·slider·select 등)를 정의해, 같은 속성 패널 컴포넌트가 노드 종류에 따라 입력 UI를 자동 구성 (코드 노드는 Monaco 에디터로 편집) - •작성한 그래프는 프로젝트 단위로 저장·재사용하고 썸네일로 식별
작업의 "정의"를 데이터(노드+엣지+속성)로 분리해, 새 동작 유형을 추가할 때 스키마만 확장하면 에디터·실행이 함께 따라오도록 했습니다.
2. 실행 엔진 — 그래프 인터프리터 + 일시정지 / 재개 / 중지
저장된 노드 그래프를 실제 로봇 동작으로 옮기면서, 실행 순서를 명확히 정하고 실행 도중 운영자가 흐름을 통제할 수 있어야 했습니다.
- •시작 노드(진입 엣지가 없는 노드)가 정확히 하나인지 검증한 뒤, 엣지를 따라 노드를 순회하는 인터프리터 방식으로 실행 — 조건 노드는 true/false 분기 엣지를 선택하고, 반복은 loopStart/End 짝을 사전 검증하고 런타임 스택으로 처리(중첩 지원)
- •시작 노드 유일성 · 루프 짝 · 스텝 상한(무한 순환 방지) 검증으로 잘못된 그래프를 실행 전/중에 차단
- •노드를 순차 실행하되 매 노드 경계에서 중지 / 일시정지 제어 객체를 확인해 다음 노드 진입 여부를 결정
- •실행 중 노드별 상태(
idle / executing / success / error)를 실시간 갱신해 에디터에 표시, 실행 로그를 별도 패널에 누적 - •실행 로직을 동적 import(
@/lib/workflow/executor)로 분리해 에디터 초기 번들에서 제외
명령을 한꺼번에 던지지 않고 노드 경계마다 제어권을 회수하는 구조라, 현장에서 즉시 멈추고 이어서 재개하는 운영 흐름을 안전하게 지원합니다.
3. 로봇이 실제로 움직이는 방식 — 논블로킹 명령 + 위치 폴링
이 플랫폼에서 가장 까다로웠던 부분은 "로봇이 움직이는 동안 3D 뷰가 끊김 없이 따라 움직이게" 만드는 것이었습니다.
- •이동 명령을
is_block: false(논블로킹)로 보내 즉시 반환받고, 프론트가 약 30ms 간격으로 관절 각도(`getJointPosition`)를 폴링 - •폴링한 각도로 (1) 3D 팔을 실시간 갱신하고 (2) 목표 도달·정지(stall)를 감지해 다음 노드로 진행
- •
is_block: true로 보내면 백엔드가 로봇 요청을 직렬화해 위치 폴링까지 막혀, 3D 팔이 멈췄다가 완료 시 순간이동하는 문제가 생김 → 논블로킹 + 폴링으로 중간 위치를 받아 부드럽게 표현 - •폴링 비용 관리: 이동 중에는 관절 각도만 호출(매 틱 TCP 좌표까지 부르면 백엔드 직렬화로 틱당 ~800ms 지연), TCP 좌표는 모션 완료 후 1회만 갱신
- •완료 판정을 관절 각도 기반으로 다중화 — 목표 도달(0.5° 이내) · 정지 감지(연속 무변화) · "한 번 움직인 뒤"에만 정지를 인정하는 가드 · 끝내 안 움직이면 포기 · 안전 타임아웃(무한 대기 방지)
- •단위·속도 변환을 클라이언트에서 처리 — 관절은 deg 그대로, 속도 0~100% → 0.1~180 deg/s / 직선은 mm → m, 속도 0~100% → 0.001~1.0 m/s
"명령을 보내고 끝"이 아니라 실제 로봇 상태를 되읽어 화면에 반영하는 구조라, 운영자가 동작 진행을 눈으로 확인하며 통제할 수 있습니다.
4. 로봇 연동 — REST 게이트웨이 + 자동 연결
- •axios 기반으로 Robot Core 게이트웨이의 공통 엔드포인트를 호출 —
connect / power / enable / move(joint·linear) / abort / position(joint·tcp) - •모든 제어 명령 전에
ensureConnected로 연결을 보장 — 스토어에 연결 상태가 없으면 /connect를 시도하고, 그래도 실패하면 위치 조회로 "이미 연결된 상태"를 판별하는 폴백을 둬 운영 중 끊김·중복 연결을 흡수 - •연결 상태는 Zustand 스토어에 로봇 단위로 캐시해 불필요한 재연결 호출을 줄임
5. 로봇 3D 뷰 & 제어 패널
워크플로우를 실행하기 전후로 로봇 상태를 눈으로 확인하고 직접 조작할 수 있어야 했습니다.
- •
urdf-loader + three.js로 URDF 모델을 로드해 관절 자세를 3D로 렌더링 - •연결·재연결·전원·서보·joint/linear 모션·정지(abort)를 한 패널에서 제어, 좌표값은 모션 후 패널에 즉시 반영
- •화면 폭이 좁아지면 제어 패널을 플로팅 뷰로 전환해 에디터 영역을 가리지 않도록 처리
그 외 구현 (프론트엔드)
- •httpOnly 쿠키 인증 + 401 자동 갱신: 토큰을 JS에서 직접 다루지 않고, 응답 인터셉터에서 401 감지 시 토큰 갱신을 한 번만 수행하도록 동시 요청을 큐로 묶어 중복 갱신을 방지.
- •다국어·테마: next-intl로
[locale] 라우팅 기반 국/영 분기, next-themes로 다크/라이트 + ReactFlow 전용 다크 스타일 적용. - •편집 진입 시 자동 연결: 프로젝트 편집 페이지 진입 시 로봇 연결을 시도하고, 캐시된 연결 상태를 초기화한 뒤 명시적으로 connect를 한 번 호출하는 재연결 버튼 제공.