ROS 2에서 노드 하나를 만들어 발행·구독을 돌려봤어요. 그런데 실제 로봇은 노드를 몇 개로 쪼갤지부터 정해야 하고, 여기에 트레이드오프가 있었어요.
하나로 뭉칠까, 여럿으로 쪼갤까
| 모놀리식 | 모듈러 | |
|---|---|---|
| 구조 | 큰 노드 하나가 센싱·처리·제어를 다 맡음 | 기능별로 잘게 나눠 조립 |
| 데이터 전달 | 함수 호출 — 통신 비용 없음 | 토픽 — 직렬화와 복사 |
| 재사용·교체 | 일부만 못 함 | 가능 |
| 부분 고장 | 전체가 같이 죽음 | 나머지는 살아 있음 |
이 통신 비용의 정체를 알려면 노드가 실제로 무엇인지 봐야 해요.
노드 하나가 프로세스 하나
ROS 2에서 노드는 기본적으로 각각 독립된 OS 프로세스로 떠요. 프로세스가 다르면 메모리를 공유하지 않으므로, 노드 A가 노드 B에게 데이터를 넘기려면 직렬화해서 보내고 받는 쪽이 다시 역직렬화해야 해요. 이 전송을 맡는 통신 계층이 DDS이고, 토픽 이름만 맞으면 중개 서버 없이 서로를 찾아 연결해요.
프로세스를 나눈 덕에 두 가지가 가능해져요. 하나는 격리예요. 카메라 노드가 죽어도 모터 제어 노드는 계속 돌아 부분 고장이 전체 정지로 번지지 않아요. 다른 하나는 분산이에요. 프로세스가 이미 나뉘어 있으니 그중 일부를 다른 컴퓨터에 두어도 같은 네트워크면 그대로 통신해요. 센서는 로봇에 붙은 라즈베리파이에서 읽고, 무거운 AI 추론은 데스크톱 GPU에서 돌리면서 하나의 시스템으로 묶을 수 있다는 뜻이에요.
쪼갤수록 드는 비용
분산과 격리는 공짜가 아니에요. 프로세스 경계를 넘을 때마다 직렬화와 복사가 일어나기 때문에, 작은 메시지는 괜찮지만 카메라 프레임처럼 큰 데이터를 초당 수십 장 나르면 이 복사가 병목이 돼요. “DDS가 느리다”는 말은 대개 이 오버헤드를 가리켜요.
문제가 도드라지는 건 데이터를 단계적으로 가공하는 파이프라인 구조예요. 카메라에서 전처리로, 전처리에서 검출로, 검출에서 추적으로 이어지는 노드 사슬은 재사용과 교체가 쉬운 대신 단계를 넘길 때마다 복사가 쌓여요.
다시 합치기
여기서 나오는 절충이 컴포지션이에요. 코드는 노드 단위로 쪼개 재사용성과 격리를 유지하되, 실행할 때 여러 노드를 한 프로세스에 함께 로드해요. 개발은 레고처럼 나눠서 하고, 배치할 때 무거운 데이터가 오가는 노드들만 한 프로세스로 묶어 복사 비용을 지우는 거예요.
핵심은 이 선택을 실행 시점에 할 수 있다는 점이에요. 노드를 컴포넌트로 작성해 두면 따로따로 띄우는 것과 한 프로세스로 합치는 것을 나중에 고를 수 있어요. 쪼개는 이유(재사용·격리·분산)와 합치는 이유(복사 비용 제거)가 충돌하지 않게, 코드의 모듈 경계와 실행의 프로세스 경계를 분리해 둔 설계예요.
여러 노드를 한 번에
노드가 다섯 개, 열 개가 되면 터미널마다 실행하는 건 비현실적이에요. 그래서 launch 파일에 “이 노드들을, 이 설정으로, 이렇게 연결해 띄워라”를 적어 두고 한 번에 실행해요. 어떤 노드들을 한 프로세스로 컴포지션할지도 이 launch 단계에서 지정하니, 앞의 나눔과 합침이 실제로 맞물리는 자리이기도 해요.
로봇 소프트웨어의 첫 설계 결정은 노드를 몇 개로, 어떤 입도로 나눌 것인가예요. 노드 하나를 만드는 것보다 이 나눔과 합침의 감각을 잡는 게 실제 로봇 시스템에 더 가까운 이야기였어요.
로봇이 하드웨어만 여러 컴퓨터로 나뉘는 게 아니라 소프트웨어 노드까지 분산된다는 점에서, SBC와 MCU 2계층 구조와 시리얼 경계 처리의 계층 분리가 소프트웨어 쪽으로 한 겹 더 이어지는 셈이에요.