로컬 저장소에서의 패키지 관리

로컬 저장소 구조 개요

ROS2 Humble에서 패키지를 로컬 저장소로 관리할 때 가장 먼저 고려해야 할 부분은 저장소(Repository)의 디렉터리 구조이다. 일반적으로 ROS2 패키지들은 src 디렉터리 아래에 하위 패키지 폴더들을 나누어 배치하고, 그 상위 디렉터리에 CMakeLists.txt 또는 package.xml 등을 배치하여 워크스페이스(workspace)로 구성한다. 예를 들어, 다음과 같은 구조가 있을 수 있다.

my_ros2_ws/
└── src/
    ├── my_pkg1/
    │   ├── CMakeLists.txt
    │   └── package.xml
    └── my_pkg2/
        ├── CMakeLists.txt
        └── package.xml

이때 my_ros2_ws 디렉터리가 로컬 저장소의 루트가 되며, src 디렉터리 아래에 여러 ROS2 패키지가 존재한다. ROS2의 빌드 도구(colcon)는 이 구조를 바탕으로 각 패키지를 자동으로 인식하고 관리한다.

colcon을 활용한 로컬 빌드와 설치

ROS2 Humble에서는 colcon build 명령을 통해 로컬에 존재하는 모든 패키지를 빌드할 수 있다. 예를 들어 다음과 같이 실행한다.

colcon build --symlink-install
  • --symlink-install 옵션을 사용하면, 빌드된 결과물이 심볼릭 링크로 연결되어 패키지의 수정사항이 반영될 때마다 재빌드 과정을 빠르게 할 수 있다.

빌드 후에는 install 디렉터리가 생기며, ROS2 환경 설정을 위해 아래처럼 setup.bash를 source 한다.

source install/setup.bash

이 과정을 거치면 로컬 저장소 내에서 빌드된 패키지를 사용할 수 있는 환경이 설정된다.

패키지 의존성 관리

ROS2 패키지는 package.xml 파일에 의존성을 기술함으로써 다른 패키지나 라이브러리와의 관계를 정의한다. 예를 들어 my_pkg1rclcppstd_msgs를 사용한다면, package.xml에 아래와 같은 항목이 있어야 한다.

만약 로컬 저장소 안에 있는 다른 패키지, 예를 들어 my_pkg2가 의존성이 된다면,

와 같이 설정한다. colcon build는 이를 자동으로 확인하여 빌드 순서를 조정하므로, 패키지 간 의존 관계가 복잡해도 올바른 순서대로 빌드가 가능하다.

로컬 저장소 버전 관리

로컬 저장소에서 작업한 패키지를 다른 개발자와 공유하거나, 특정 시점의 버전을 보관하기 위해서는 Git과 같은 버전 관리 시스템을 적극 활용해야 한다. ROS2 패키지 구조와 달리 Git 저장소의 루트 위치와 관계없이, .git 디렉터리가 존재하는 경로에 따라 버전을 관리할 수 있다.

  • 단일 ROS2 패키지 단위로 Git 저장소를 구성: 각 패키지마다 .git을 두고 버전을 관리한다.

  • 워크스페이스 단위로 Git 저장소를 구성: 여러 패키지를 포함하는 상위 디렉터리에 .git을 두고 전체를 관리한다.

어느 방식을 택하든, 빌드를 위한 종속성과 ROS2 설정 파일(package.xml, CMakeLists.txt 등)을 함께 추적하면 협업 시에 편리하다.

로컬 저장소와 시스템 설치 패키지의 차이

ROS2 패키지는 APT 등 시스템 패키지 관리자를 통해 설치할 수도 있고, 로컬(소스)로 설치할 수도 있다. 예를 들어, Ubuntu 환경에서 apt install ros-humble-rclcpp와 같이 설치하면 시스템 전역에 ROS2 패키지가 설치된다. 반면 로컬 빌드를 통해 설치하면 특정 워크스페이스 폴더에만 패키지가 위치한다. 두 방식은 아래와 같은 차이가 있다.

  • 시스템 패키지: 설치가 간편하지만, 최신 기능이나 커스텀 패치를 적용하기 어렵다.

  • 로컬 패키지: 개발 중인 기능을 수시로 업데이트할 수 있지만, 별도의 빌드 과정이 필요하고, 의존성 충돌이 생길 수 있다.

일반적으로 로컬 개발이 필요한 경우 로컬 저장소 방식을 택하며, 안정적인 배포판의 기능만 사용하고자 하면 시스템 패키지를 활용한다.

colcon overlay와 underlay 개념

ROS2의 빌드와 패키지 관리는 계층화된(workspace overlay) 구조를 지원한다. 작업 중인 로컬 저장소를 overlay workspace라 하고, 이미 빌드되어 사용 가능한 상위 레벨의 ROS2 설치(예: 시스템 설치 패키지나 다른 워크스페이스)를 underlay workspace라 한다. 다음과 같은 구조를 예로 들 수 있다.

개발자가 새 패키지를 추가하거나 수정한 뒤 ~/my_ros2_ws를 빌드하면, 이미 설치되어 있는 /opt/ros/humble에 있는 패키지와 합쳐져 하나의 환경을 이룬다. 이를 달성하기 위해서는 overlay workspace를 빌드한 후 아래와 같이 source 단계를 거쳐야 한다.

이후 ROS2 환경에서 패키지들을 사용할 때, 우선 overlay workspace 내의 패키지를 탐색하고, 만일 동일한 패키지가 underlay workspace에도 존재한다면 overlay workspace의 패키지가 우선한다.

세부 빌드 옵션 활용

colcon build 명령에는 빌드 과정에서 상세 옵션을 지정할 수 있다. 예를 들어, 특정 패키지만 빌드하려면 --packages-select를 사용한다.

또한 빌드 모드를 지정할 때 --cmake-args 옵션을 사용하여 CMake 변수를 전달할 수 있다.

여기서 Release 모드로 빌드하면 최적화가 적용되므로 속도가 중요한 ROS2 노드의 경우 유용하다. 반면 디버깅을 위해서는 -DCMAKE_BUILD_TYPE=Debug로 빌드하여 디버그 기호를 포함할 수 있다.

로컬 저장소 패키지의 종속성 충돌 방지

로컬 저장소와 시스템 설치 패키지를 동시에 사용하는 환경에서, 특정 ROS2 패키지 버전이 서로 다르면 충돌을 일으킬 수 있다. 특히 의존성이 복잡한 경우, 아래와 같은 상황이 발생할 수 있다.

  • Underlay workspace의 메시지 타입(pkgA)과 Overlay workspace에서 수정한 메시지 타입(pkgA)이 다른 버전을 사용

  • 런타임 시에 어느 쪽 메시지 정의를 참조해야 하는지 애매해짐

이런 문제를 피하려면 다음과 같은 접근이 필요하다.

  1. 패키지 버전 관리: package.xml<version> 태그를 정확히 관리하고, 동시에 underlay에 설치된 패키지와 버전을 맞추거나, 완전히 분리된 namespace를 사용한다.

  2. Workspace 분할: 특정 기능을 개발하기 위한 workspace와, 안정된 공식 패키지를 사용하는 workspace를 분리하고 필요할 때만 합친다.

로컬 환경 전환 스크립트

ROS2 환경을 매번 source하는 과정을 자동화하기 위해서는, 터미널을 열 때마다 필요한 source 명령을 실행하는 스크립트를 작성할 수 있다. 예를 들어 ~/.bashrc~/.zshrc에 아래와 같이 추가한다.

이렇게 설정하면, 새 터미널에서 자동으로 underlay와 overlay 환경이 준비된다. 여러 개의 workspace를 전환해가며 작업할 때는, 별도의 alias나 함수로 분기해 관리하는 방법도 있다.

로컬 저장소 빌드 자동화

지속적인 통합(Continuous Integration)이나 자동화된 테스트 환경을 구축하려면, 로컬 저장소를 빌드하는 스크립트나 파이프라인을 설정해야 한다. 예를 들어 다음과 같은 간단한 셸 스크립트(build_local.sh)를 만들 수 있다.

위 스크립트를 CI/CD 파이프라인에서 실행하면, 원격 저장소(GitLab, GitHub 등)에 푸시할 때마다 테스트 빌드를 진행할 수 있다. 이를 통해 배포 전에 빌드 오류나 의존성 문제를 빠르게 발견할 수 있다.

Docker를 활용한 로컬 저장소 빌드

협업 환경에서 개발 PC의 OS나 ROS2 버전에 상관없이 동일한 환경을 구성하려면, Docker 컨테이너를 활용한 빌드를 고려할 수 있다. 예시로, 아래와 같은 Dockerfile을 작성한다.

이후 다음 명령으로 이미지를 빌드한다.

이렇게 만들어진 이미지는 컨테이너 내부에서 ROS2 Humble 환경 설정과 패키지 빌드를 모두 수행했으므로, 협업 시 같은 이미지를 사용하면 환경 차이에 의한 문제를 줄일 수 있다.

다이어그램 예시

로컬 저장소를 빌드하고 테스트하는 단계를 순서도로 표현하면 다음과 같다.

spinner

rosdep을 활용한 의존성 자동 설치

ROS2 패키지 개발 시, 패키지가 필요로 하는 시스템 의존성(예: 특정 라이브러리, 개발 헤더 등)을 자동으로 설치하고 싶다면 rosdep 도구를 활용한다. rosdeppackage.xml에 명시된 의존성 정보를 기반으로 OS별 패키지 매니저(예: apt, yum)를 통해 적절한 시스템 패키지를 찾아 설치한다. 간단히 다음과 같은 절차를 따른다.

rosdep 초기화:

로컬 저장소(워크스페이스)에서 의존성 설치:

  • --from-paths 옵션 뒤에 의존성을 찾을 소스 디렉터리를 지정한다.

  • --ignore-src는 소스 패키지는 무시하고, 시스템 패키지만 설치를 시도한다.

  • -y 옵션은 설치 과정에 대한 사용자 입력을 생략한다.

이를 통해 패키지 개발자들은 서로 다른 OS 환경에서도 같은 의존성 목록을 공유할 수 있으므로, 협업 시 설정 시간을 절약할 수 있다.

로컬 패키지 테스트와 test-result

ROS2 Humble에서 패키지 단위 테스트를 진행할 때는 다음 명령을 사용할 수 있다.

이 명령은 워크스페이스 내의 모든 패키지에 대해 CMake의 ctest 또는 Python의 pytest 등을 자동으로 호출한다. 테스트가 끝나면, 결과 확인을 위해 다음과 같이 명령을 입력한다.

위 결과에서 성공 패키지와 실패 패키지를 한눈에 파악할 수 있으며, 로그 파일 위치도 함께 출력된다. 이를 통해 로컬 저장소에 있는 패키지들의 품질 및 기능 동작 여부를 확인할 수 있다.

Python 기반 ROS2 패키지 빌드와 관리

C++ 패키지(CMake 기반)뿐 아니라, Python으로 작성한 ROS2 패키지도 로컬 저장소에서 관리할 수 있다. Python 패키지인 경우 CMakeLists.txt 대신 setup.pypackage.xml이 중심이 된다.

예를 들어, my_python_pkg 디렉터리 내에 setup.pypackage.xml을 두고 다음과 같이 작성한다.

setup.py 예시:

package.xml 예시:

이렇게 구성된 로컬 Python 패키지는 colcon build로 빌드하며, 빌드 후에 install 디렉터리 내부에 Python 모듈이 설치된다. 다음과 같이 실행할 수 있다.

로컬 저장소에서 사용자 정의 메시지 공유

여러 패키지에서 공통으로 사용하는 메시지 타입을 정의할 경우, 별도의 메시지 패키지(예: my_msgs)를 만들고 이를 로컬 저장소에서 함께 관리한다. 예시 구조는 다음과 같다.

MyCustomMsg.msg 정의 예:

package.xml에서 메시지 패키지는 build_dependexec_dependrosidl_default_generators, rcl_interfaces 등을 명시해야 하며, 다른 패키지에서 my_msgs를 의존하도록 설정한다.

이렇게 하면 my_pkg1, my_pkg2 등에서 MyCustomMsg를 자유롭게 import하여 사용할 수 있다. 로컬 저장소에서 이 메시지 패키지를 수정하면 의존 패키지들이 colcon 빌드 시 자동으로 재빌드되어 최신 메시지 정의를 반영한다.

C++ 라이브러리와 헤더 관리

C++ 기반 ROS2 패키지에서 사용자 정의 라이브러리(.so)나 헤더 파일(.hpp)을 로컬로 빌드해 공유할 수도 있다. 예를 들어, my_library_pkg라는 패키지에서 C++ 라이브러리를 제공하고, my_application_pkg에서 이를 링크한다면 다음과 같은 단계를 거친다.

  1. my_library_pkg

    • add_library(my_library src/my_library.cpp)

    • install(TARGETS my_library DESTINATION lib)

    • install(DIRECTORY include/ DESTINATION include)

  2. my_application_pkg

    • find_package(my_library_pkg REQUIRED)

    • target_link_libraries(my_application_node my_library_pkg::my_library)

위 단계가 로컬 저장소의 각각의 패키지 CMakeLists에 들어있으면 colcon build 시 자동으로 빌드 및 링크가 이루어진다.

배포판(Release) 패키지와 로컬 저장소 패키지의 충돌 사례

ROS2 패키지를 로컬 저장소에서 개발 및 빌드하여 사용하다 보면, 동일한 이름의 패키지가 정식 배포판(예: apt 패키지)과 충돌을 일으키는 경우가 종종 있다. 예를 들어, 아래와 같은 상황을 가정해 보자.

  • 로컬 작업공간 my_ros2_ws/src/my_example_pkg에서 동작 중인 패키지

  • 시스템 전역에 설치된 ros-humble-my-example-pkg (apt를 통해 설치한 동일 패키지)

이 경우 빌드 또는 런타임 중 의존 관계 해결이 애매해질 수 있다. 특히 $source /opt/ros/humble/setup.bash$ source ~/my_ros2_ws/install/setup.bash를 모두 한 터미널에서 실행하면, 우선순위에 따라 실제 로드되는 패키지가 서로 다를 수 있다. 이를 방지하기 위한 방법은 다음과 같다.

패키지명 변경:

  • 로컬 저장소에서 별도로 개발하는 패키지라면, 정식 배포판과 중복되지 않도록 다른 이름(예: my_example_pkg_experimental)을 사용한다.

빌드 전 apt 패키지 제거:

  • 이미 시스템에 설치된 동일 패키지 버전이 불필요하다면, apt로 제거한다.

Workspace 분리:

  • 공식 배포판 환경과 개발 환경을 명확히 구분하기 위해, 서로 다른 개발용 워크스페이스를 구성하거나 Docker 등으로 격리된 환경을 사용한다.

로컬 저장소 패키지 버전 번호 활용

package.xml<version> 태그에서 버전 번호를 관리하면, 패키지가 어느 정도 안정된 상태인지, 어떤 기능이 추가되었는지 협업자가 알 수 있다. 예를 들어 주요 변경사항이 있을 때 0.1.0에서 0.2.0으로 올리는 식으로 버전 업을 수행한다. 이때, 다음을 함께 고려한다.

SemVer(Semantic Versioning):

  • 원칙 준수: MAJOR.MINOR.PATCH 구조를 유지하고, 하위 호환이 깨지면 MAJOR를 올리는 식으로 관리한다.

  • Git 태그와 연동: 버전 번호를 올릴 때, Git 태그(예: v0.2.0)도 같이 달아서 특정 버전의 소스 상태를 추적하기 쉽게 한다.

로컬 패키지 재설치 혹은 부분 업데이트

ROS2 패키지를 자주 수정하다 보면, 빌드와 설치 과정을 반복하게 된다. 간혹 빌드 산출물에 문제가 생기거나 캐시가 꼬여서 정상적인 동작이 불가능할 때, 다음과 같은 접근 방법을 취한다.

  • --packages-up-to: 선택된 패키지와 의존성이 있는 상위 패키지까지 모두 빌드한다.

  • --symlink-install: 빌드 산출물이 심볼릭 링크로 잡혀 있어, 파일 변경 즉시 반영된다.

혹은 워크스페이스 전체를 깨끗이 만들려면,

이렇게 하면 이전의 빌드 캐시나 설치 파일을 모두 삭제한 뒤 새로 빌드하게 된다.

ROS2 로컬 저장소 배포 시 고려사항

자체 개발한 로컬 ROS2 패키지를 배포(예: 사내 서버 혹은 공개 레포지토리)하려면 다음과 같은 점을 챙겨야 한다.

  1. 호환성(Compatibility)

    • 의존성이 잘 명시되어 있는지, 특정 ROS2 버전(Humble/Foxy 등)과 맞는지 확인.

  2. 테스트와 문서화

    • 패키지 사용자를 위해 간단한 샘플 코드, README, test 코드 등을 잘 정리.

  3. 배포 형식

    • 소스 배포: Git repository나 archive(zip/tar)로 소스만 전달

    • 바이너리 배포: Debian 패키지(예: my_pkg_0.2.0-1_amd64.deb)를 만들어 apt 저장소나 기타 패키지 서버를 통해 배포

소스 스냅샷과 잦은 변경 관리

빠르게 로컬에서 패키지를 개발하면서도, 특정 시점의 스냅샷(배포 후보 버전)을 남길 수 있는 방법은 다음과 같다.

  1. Git branch/tag 활용

    • release-0.2.0와 같은 브랜치를 따로 만든 뒤, 필요한 수정만 적용해 안정화를 진행한다.

  2. CI/CD 자동화

    • Git에 push될 때마다 자동으로 빌드를 수행하고, 결과 아티팩트를 저장(CI 서버에서 .deb 파일 생성 등).

  3. Changelog 관리

    • 변경 기록을 CHANGELOG.rst 등 별도 파일로 작성해 두면, 로컬 저장소의 변경 히스토리를 체계적으로 추적할 수 있다.

Last updated