Dependency(의존성) 충돌 해결 방안

패키지 의존관계 점검

ROS2 Humble 환경에서 발생하는 대표적인 문제 중 하나는 패키지 간 의존성 충돌이다. 패키지의 $package.xml$ 또는 setup.py에 명시된 의존 라이브러리 버전이 서로 호환되지 않으면 빌드 과정에서 에러가 발생하거나 런타임 환경에서 정상 동작이 불가능해진다. 의존관계를 점검하기 위해서는 다음과 같은 과정을 거치는 것이 일반적이다.

  1. 의존성 확인

    • $colcon list --paths-only --base-paths src 등을 통해 현재 작업공간(workspace)에 포함된 모든 패키지를 나열한다.

    • 각 패키지의 $package.xml$ 혹은 setup.py 등을 직접 열어 의존 항목을 확인한다.

  2. 버전 호환성 확인

    • 패키지가 요구하는 라이브러리나 ROS2 기능의 특정 버전을 확인한다.

    • 예를 들어, rclcpp>=4.0.0 버전을 요구하고, 다른 패키지는 3.x.x 버전을 요구한다면 충돌이 발생할 수 있다.

  3. 중복 의존성 검사

    • 여러 패키지가 같은 라이브러리를 사용하지만 버전이 서로 다른 경우가 많다.

    • 이런 상황에서는 package manager(예: apt) 레벨에서 의존성을 재검토해야 한다.

C++ 라이브러리 버전 충돌 사례

C++ 기반 패키지들은 대체로 CMake를 활용해 빌드가 이루어지므로, CMakeLists.txt 안에 종속된 라이브러리 버전이 제대로 표기되어 있어야 한다. 만약 다음과 같은 형태로 필요 라이브러리가 제대로 연결되지 않으면 에러가 발생한다.

find_package(geometry_msgs REQUIRED)
find_package(tf2_ros 0.18.0 EXACT REQUIRED)
...
  • EXACT 옵션: 특정 버전을 정확히 요구한다. 다른 버전이 깔려 있어도 빌드 오류가 날 수 있다.

  • REQUIRED 옵션: 해당 패키지를 반드시 발견해야만 빌드가 진행된다.

C++ 라이브러리가 제공하는 API가 버전에 따라 다르게 구현되어 있으면, 빌드 타임뿐 아니라 런타임에서도 symbol lookup error와 같은 문제가 발생할 수 있다. 이때 다음과 같은 단계로 문제를 진단할 수 있다.

공유 라이브러리 심볼 확인

  • ldd <실행파일> 명령어로 어떤 라이브러리가 어떤 경로에서 로드되는지 확인한다.

  • 여러 경로에서 동일 라이브러리가 중복 로드되는 경우 충돌을 야기할 수 있다.

패키지 매니저 동기화

  • Ubuntu나 Debian 계열에서는 다음 명령어로 의존 라이브러리를 최신 상태로 갱신한다.

  • ROS2 Humble에서 제공하는 공식 저장소(repo) 버전과 시스템 전역에 설치된 다른 repo 버전이 충돌할 수 있으므로, ROS2용 repo를 우선순위 높게 설정하거나 필요한 경우 PPA를 조정한다.

Python 라이브러리 버전 충돌 사례

ROS2 패키지 중 Python 기반 노드를 사용하는 경우, 시스템 전역 파이썬 환경과 가상환경(virtualenv) 또는 Conda 환경 사이에서 충돌이 발생하기도 한다. 가령 numpy 혹은 torch 같은 라이브러리가 특정 버전 이상을 필요로 하는데, 시스템 전역에 설치된 버전이 낮다면 런타임 시 ImportError가 발생하기 쉽다. 이와 같은 문제를 해결하기 위한 주요 방법은 다음과 같다.

Python 환경 분리:

  • 작업공간마다 별도의 가상환경 또는 Conda 환경을 사용하여 의존성 충돌을 회피한다. 예시:

  • 의존 라이브러리가 서로 다른 버전을 요구하는 경우, 하나의 환경에서 전부 충족시키기 어렵다면 여러 개의 가상환경을 만들어 관리한다.

pip 패키지 버전 잠금(lock):

  • requirements.txt 또는 poetry.lock 등을 활용해 패키지 버전을 명시적으로 고정한다.

  • ROS2 빌드 시에도 $colcon build 전에 필요한 패키지를 pip install -r requirements.txt 형태로 동기화해둔다.

중복 설치 확인

  • pip show numpy 등으로 실제 설치 경로를 확인하여, 의도치 않게 전역 환경과 가상환경에 동일 패키지가 서로 다른 버전으로 공존하고 있는지 확인한다.

워크스페이스 관리 전략

ROS2 프로젝트를 여러 개 동시에 진행하거나, 다양한 버전의 종속성을 필요로 하는 경우에는 워크스페이스를 다단계로 구성하는 것이 중요하다. 예를 들어, underlay_ws, overlay_ws 식으로 여러 개의 작업공간을 정의하고, 상위 작업공간에 대한 종속성을 하위 작업공간이 상속하여 사용하도록 설계한다. 아래는 mermaid를 사용하여 단순화한 예시다.

spinner
  • Underlay Workspace: ROS2 Humble의 기본 구성 및 공통으로 사용하는 패키지들을 위치시킨다.

  • Overlay Workspace: 프로젝트별 혹은 버전별 고유 패키지를 배치하여 별도로 빌드한다.

  • 패키지 충돌이 발생하면 상위 작업공간과 하위 작업공간 사이의 버전을 재조정하거나, 의존성을 상위(Underlay)에서 제거하고 하위(Overlay)에서 재설치하는 방법도 고려한다.

의존성 충돌 방지를 위한 팁과 주의사항

ROS2 설치 경로 및 환경 변수 점검

ROS_DISTRO 환경 변수:

  • ROS2 Humble이 설치된 환경에서는 $echo$ROS_DISTRO 명령어를 통해 현재 사용 중인 ROS 버전을 확인한다.

  • 잘못된 버전(예: ros2-foxy 환경 설정 스크립트가 활성화)으로 인해 빌드 시 엉뚱한 버전의 라이브러리를 참조할 수 있으므로 주의한다.

ROS_PACKAGE_PATH:

  • $echo$ROS_PACKAGE_PATH로 현재 ROS 패키지 검색 경로를 확인한다.

  • 프로젝트에 사용하지 않는 경로가 들어 있다면 빌드가 혼선을 일으켜 충돌의 원인이 될 수 있다.

  • 필요에 따라 unset ROS_PACKAGE_PATH 또는 . /opt/ros/humble/setup.bash 등으로 재설정한다.

apt 혹은 다른 패키지 매니저와의 충돌

System-level 패키지와 ROS2 repo 버전 충돌:

  • 예를 들어, Ubuntu 22.04에서 기본적으로 설치되는 특정 라이브러리가 ROS2 Humble에서 요구하는 라이브러리 버전보다 낮거나 높을 수 있다.

  • 이 경우 PPA를 추가하거나, ROS Official Repo를 최우선으로 설정하여 필요한 버전으로 업그레이드한다.

colcon build 시 자동으로 잡히지 않는 의존성:

  • 일부 패키지는 $package.xml$에 기술되지 않은 의존성을 필요로 할 수 있다.

  • $sudo apt-get install libsomepackage-dev와 같이 시스템 라이브러리를 직접 깔아야 하는지 미리 파악한다.

Docker 환경을 활용한 버전 충돌 회피

  • 다양한 OS 및 ROS2 버전에서 개발 테스트를 진행해야 할 경우, Docker 이미지를 활용하여 충돌을 최소화할 수 있다.

  • 예시:

  • 컨테이너 내부에서 빌드된 워크스페이스는 호스트 환경과 독립적이므로, 패키지 충돌 문제를 효율적으로 디버깅할 수 있다.

소스 빌드(Source Build)와 설치형(apt) 혼용 시 주의

  • ROS2 Humble의 일부 패키지를 별도로 소스 빌드하여 사용하고, 나머지는 apt를 통해 설치된 환경을 사용할 경우, 설치 경로가 중복될 수 있다.

  • $colcon build 후 생성되는 install/ 디렉토리와 /opt/ros/humble/ 디렉토리 중 어느 쪽의 라이브러리가 우선순위를 가질지 명확히 확인해야 한다.

  • $source install/setup.bash 명령어를 통해 소스 빌드된 패키지를 사용 중이라면, 동일 이름의 패키지가 /opt/ros/humble/ 디렉토리에 있는지 체크하고 불필요하다면 제거하거나 우선순위를 재조정한다.

rosdep 활용하기

의존성 관리는 소스 빌드 전후로 꾸준히 체크해야 하는데, ROS2 환경에서는 rosdep을 이용해 자동화할 수 있다.

설치 확인:

  • 일반적으로 ROS2 설치 시 rosdep이 같이 설치되지만, 환경에 따라 누락될 수 있으므로 다음 명령어로 확인한다.

의존성 설치:

  • 작업공간의 src/ 경로에 존재하는 모든 패키지의 시스템 레벨 의존성을 자동 설치하려면 다음과 같이 수행한다.

  • 이때, package.xml에 제대로 기술되어 있지 않은 의존성은 자동 설치 목록에 누락된다.

빌드 설정(CMake) 상세 점검

find_package 설정 확인:

  • $find_package(some_library REQUIRED)에서 some_library 패키지가 중복 설치되어 있거나 버전이 안 맞으면 문제가 발생한다.

  • $cmake --trace-expand 옵션으로 CMake가 어떤 경로에서 패키지를 찾는지 추적해볼 수 있다.

CMake Policy 설정:

  • CMake 버전 차이로 인해 policies가 다르게 적용될 경우, 경로가 뒤바뀌거나 링크 순서가 바뀔 수 있다.

  • 패키지 내 cmake_minimum_required(VERSION 3.x.x) 구문에서 해당 버전에 알맞은 policy가 제대로 적용되는지 확인한다.

Advanced 디버깅 기법

의존성 충돌 문제는 단순 빌드 오류로만 나타나지 않고, 런타임 시점에 예기치 않은 에러를 일으킬 수도 있다. 아래는 자주 사용되는 디버깅 기법이다.

Symbol Lookup Error 추적:

  • 실행 파일 또는 노드 실행 시 $symbol lookup error: ... undefined symbol: ...$ 형태의 메시지가 뜨면, 특정 동적 라이브러리(so 파일)의 버전이 맞지 않아 심볼을 찾지 못했을 가능성이 크다.

  • $LD_LIBRARY_PATH 환경 변수를 확인해 보고, 필요한 라이브러리의 실제 설치 경로와 버전을 비교한다.

gdb를 통한 라이브러리 로딩 검사:

  • $gdb --args ./my_ros2_node로 디버거 환경에서 프로세스를 실행해볼 수 있다.

  • 프로그램 시작 전(start 또는 run 명령 전)에 $set environment LD_DEBUG=libs 등을 주어서, 어떤 순서로 라이브러리가 로딩되는지 확인 가능하다.

strace/ldd 사용:

  • $ldd ./my_ros2_node로 빌드된 바이너리가 참조하는 모든 동적 라이브러리를 목록화한다.

  • $strace -e open,openat ./my_ros2_node로 라이브러리 파일 오픈 경로를 추적해, 중복 설치된 라이브러리와 충돌이 있는지 파악한다.

라이브러리 다중 버전 공존 전략

LD_LIBRARY_PATH 분리:

  • 서로 다른 버전의 라이브러리를 공존시켜야 한다면, 노드를 실행하기 전에 LD_LIBRARY_PATH를 명시적으로 분리해준다.

  • 이후 다른 노드를 실행할 때는 /home/user/libs/version2 디렉토리를 우선하도록 바꾸는 식으로 관리한다.

Docker 이미지를 중첩해서 사용:

  • 특정 라이브러리 버전이 상이한 노드를 동시에 실행해야 할 경우, Docker 컨테이너를 여러 개 띄우고 각 컨테이너 내에서 필요한 라이브러리를 세팅할 수도 있다.

  • 컨테이너 간 통신은 ROS2의 DDS(데이터 분배 서비스) 기능을 통해 네트워크로 연결하면 된다.

CI(Continuous Integration) 파이프라인에서의 의존성 검증

ROS2 프로젝트를 대규모로 운영하거나 여러 팀에서 협업하는 경우, 의존성 충돌이 발생하지 않도록 사전에 자동 점검을 도입하는 것이 효과적이다. 대표적인 방법은 다음과 같다.

빌드 및 테스트 자동화:

  • GitHub Actions, GitLab CI 등 CI 환경에서 Docker 이미지를 기반으로 $colcon build$colcon test를 수행한다.

  • 워크스페이스마다 필요한 시스템 의존성을 $rosdep install --from-paths src --ignore-src -y로 자동 설치하도록 설정한다.

  • Python 의존성(예: numpy, torch 등)은 $pip install -r requirements.txt 등을 통해 설치 버전을 고정(lock)한다.

빌드 매트릭스(Build Matrix):

  • ROS2 버전별(Humble, Iron 등) 또는 OS별(Ubuntu 22.04, Debian 11 등)로 병렬 빌드를 실행해, 다양한 환경에서 동일 소스가 정상 빌드되는지 검증한다.

  • 의존성 호환성이 의심되는 경우 특정 조합에서만 문제를 일으키는지 빠르게 확인 가능하다.

테스트 커버리지 확장:

  • 단순히 빌드가 성공하는 것만으로는 런타임 의존성 충돌을 잡아내기 어렵다.

  • $colcon test --event-handlers console_direct+ 등을 통해 단위 테스트, 통합 테스트를 충분히 돌려야 런타임 충돌 사례까지 빠르게 잡아낼 수 있다.

의존성 변화 추적(Dependency Tracking)

프로젝트 진행 중, 의존성 패키지의 버전이 상향(업데이트)되면 기존 코드와 호환성이 깨질 수 있다. 이를 예방하기 위해서는 버전 변경 사항을 추적하는 장치가 필요하다.

  1. package.xml 변경 이력 관리

    • Git이나 SVN 같은 버전 관리 시스템을 통해 $package.xml$ 파일이 어떻게 바뀌었는지, 해당 시점에 빌드는 정상적이었는지 기록을 남긴다.

    • 필요 시 특정 태그(tag) 또는 커밋으로 돌아가서 빌드 환경을 재현해볼 수 있다.

  2. 종속 패키지 릴리즈 노트 확인

    • 외부 라이브러리나 ROS2 코어 패키지가 업데이트될 때, 릴리즈 노트를 확인하여 breaking change(하위 호환 불가 변경)가 있는지 미리 파악한다.

    • rclcpp, nav2, gazebo_ros_pkgs 등 주요 패키지의 GitHub 릴리즈 페이지나 ROS2 공식 문서를 주기적으로 모니터링한다.

  3. 버전 핀(pin) 및 업데이트 주기 설정

    • Python 패키지나 시스템 라이브러리에 대해서는, 프로젝트 내부적으로 “분기별 1회” 등 업데이트 주기를 정하여, 변경 폭을 한 번에 관리한다.

    • 예측 불가능한 시점에 의존성이 깨지는 문제를 방지하고, 테스트의 안정성을 높일 수 있다.

운영 환경과 개발 환경의 동기화

로컬(개발 환경)에서는 빌드와 런타임이 정상인데, 실제 로봇이나 서버(운영 환경)에서 동작시 충돌이 발생하는 경우가 종종 있다. 이를 방지하기 위한 방법은 다음과 같다.

  1. Infrastructure as Code

    • Dockerfile, Ansible, Terraform 등 자동화 도구를 사용해 운영 환경을 프로비저닝한다.

    • 로컬 환경과 최대한 유사한 환경(리눅스 버전, ROS2 버전, apt 패키지 버전 등)으로 구성해, 환경 차이로 인한 의존성 충돌을 최소화한다.

  2. 환경 변수 정리

    • $RMW_IMPLEMENTATION, $ROS_DOMAIN_ID, $LD_LIBRARY_PATH 등 ROS2 관련 환경 변수를 운영 환경에서도 동일하게 적용한다.

    • 노드 실행 스크립트에 환경 변수 설정을 명시해, 수동 설정 차이로 발생하는 오류를 방지한다.

  3. 하드웨어 종속 라이브러리 점검

    • GPU(예: CUDA 버전)나 센서 드라이버 의존성이 있는 경우, 개발 머신과 실제 로봇의 하드웨어 드라이버 버전이 달라서 충돌이 일어날 수 있다.

    • 이럴 때는 Docker 컨테이너 내부에 GPU나 센서 드라이버를 동일 버전으로 설치하여, 운영 환경을 에뮬레이션한다.

Last updated