졸업 프로젝트로 쇼핑몰 환경에서 여러 대의 로봇이 자율주행으로 물건을 배송하는 시스템을 만들었다. ROS2 기반 네비게이션 스택 위에 FastAPI 백엔드, React 프론트엔드까지 붙인 꽤 규모 있는 프로젝트였다.
나는 FMS를 구현하는 역할을 맡았다. FMS 특성상 여러 대의 로봇을 중앙에서 관리해야 했고, 자연스럽게 멀티로봇 환경 구축이 내 작업의 전제 조건이 됐다.
팀원들은 각자 맡은 기능을 ROS2 노드로 구현하는 데 집중했다. 네비게이션, 장애물 감지, 서버 통신 등 기능 단위로 나눠서 개발했고, 각자의 환경에서는 잘 돌아갔다.
문제는 그게 전부 단일 로봇 환경 기준이었다는 것. 멀티로봇으로 확장하려면 누군가 전체를 묶는 작업을 해야 했는데, FMS를 맡은 나로서는 선택의 여지가 없었다.
로봇의 동작을 state 단위로 나눴다. 각 state에 따라 어떤 노드를 실행하고, 어떤 토픽을 구독할지를 중앙에서 제어하는 구조를 만들었다:

일부 state는 시간 관계상 launch 파일을 실행해 노드가 생성되고 토픽을 계속 발행하도록 구현했다. 단순히 코드를 합치는 게 아니라, 언제 무엇을 켜고 끌지를 설계하는 작업이었다.
팀원들의 코드를 전부 멀티로봇 환경에 맞게 수정하는 건 현실적으로 불가능했다. 코드 양도 많았고, 각자 개발한 로직을 건드리다가 오히려 더 많은 문제가 생길 수 있었다.
그래서 선택한 방식이 bridge node 삽입이었다.

팀원 코드는 손대지 않으면서, 외부에서 봤을 때는 멀티로봇 시스템처럼 동작하도록 인터페이스를 분리한 것이다.
구조 자체는 깔끔하게 될 거라 생각했다. 근데 막상 실행하니 충돌이 났다.
원인은 팀원들의 코드 곳곳에 namespace나 포트 번호가 디폴트값 또는 ~/.bashrc의 환경변수로 하드코딩되어 있었던 것. 로봇이 한 대일 때는 문제가 없었지만, 여러 대를 동시에 띄우니 namespace가 겹치고 포트가 충돌했다.
일단 전체 코드를 뒤지며 어디서 어떤 값이 고정되어 있는지 파악하는 작업부터 다시 시작했다. 특히 디폴트값 디버깅이 오래 걸렸다. 에러가 안 뜨고 바로 실행되다 보니 파악 자체가 느렸다.
bridge node에서 namespace와 포트를 런타임 파라미터로 주입받도록 구조를 바꿨다. 로봇마다 고유한 값을 launch 시점에 넘겨주는 방식으로, 코드 자체를 수정하지 않고도 충돌 없이 여러 대를 동시에 띄울 수 있게 됐다.
팀원 코드를 최대한 건드리지 않는다는 원칙을 지키면서 문제를 해결했다.
기능 개발과 통합은 완전히 다른 종류의 일이라는 걸 느꼈다. 기능 개발은 내가 정한 환경 안에서 동작하면 되지만, 통합은 내가 통제하지 못한 것들 사이에서 동작해야 한다.
팀원 코드를 건드리지 않으면서 시스템 전체가 돌아가도록 만드는 과정에서, 아이러니하게도 전체 시스템을 가장 깊이 이해하는 사람이 됐다. 토픽 흐름, 노드 간 인터페이스, 환경 의존성까지 전부 머릿속에 그려지는 상태가 됐고, 그게 이후 디버깅에서도 큰 무기가 됐다.