25.3.2 C++ 클래스 가시성 범위 제공을 위한 ament_export_include_directories 노출 전략

25.3.2 C++ 클래스 가시성 범위 제공을 위한 ament_export_include_directories 노출 전략

C++ 언어를 기반으로 구동되는 ROS 2 아키텍처에서 특정 패키지가 타 패키지에게 제공할 수 있는 소프트웨어 자산은 크게 두 가지로 대별된다. 하나는 컴파일러가 심볼을 링킹하기 위한 바이너리 아티팩트(.so, .a 등 라이브러리 파일)이며, 다른 하나는 다운스트림 시스템의 전처리기(Preprocessor)가 구문 분석 단계에서 클래스의 시그니처와 멤버 변수 형태를 사전에 인지할 수 있도록 돕는 인터페이스 명세서, 즉 헤더 파일(.hpp 또는 .h)이다. C++ 컴파일 파이프라인의 특성상 아무리 훌륭한 동적 라이브러리가 존재하더라도, 헤더 파일의 가시성(Visibility)이 확보되지 않으면 어떠한 객체도 인스턴스화할 수 없다. ament_export_include_directories 매크로는 이러한 헤더 파일의 경로를 ament 인덱스 생태계로 노출하여 가시성 범위를 제어하는 핵심 수단이다.

1. 인터페이스 캡슐화와 디렉터리 기반 가시성 제어

현대 C++ 소프트웨어 공학의 강력한 캡슐화 원칙에 따라, ROS 2 패키지 설계 시 헤더 파일은 그 역할에 따라 두 물리적 공간으로 엄격히 분리된다.

  1. 퍼블릭 헤더 (Public Headers): 외부 패키지가 호출해도 무방한 오픈 API 클래스 선언부가 위치하며, 관례적으로 소스 트리의 include/${PROJECT_NAME}/ 하위에 배치된다.
  2. 프라이빗 헤더 (Private Headers): 패키지 내부 구현 코드(.cpp)들 끼리만 공유하는 은닉 구조체나 헬퍼(Helper) 클래스가 위치하며, 보통 소스 파일들이 모여있는 src/ 디렉터리 내에 혼재되어 외부 가시성이 차단된다.

ament_export_include_directories(include) 매크로의 학술적 목표는 Ament 빌드 디스커버리 시스템에게 “본 패키지를 요구하는 타 패키지의 컴파일러 플래그에 -I<install_prefix>/include 옵션을 암묵적으로 주입하라“고 지시하는 것이다. 이 선언을 통해 프라이빗 헤더는 안전하게 은닉된 상태로 유지하면서, 다운스트림 컴파일러에게 오직 퍼블릭 헤더만이 존재하는 구역에 대한 논리적 접근 권한(Access Right)을 부여한다.

2. 인스톨 룰(Install Rule)과의 필수적 연동 논리

개발자가 간과하기 쉬운 치명적인 아키텍처 결함은 ament_export_include_directories 매크로가 물리적인 파일 복사 연산을 전혀 수행하지 않는다는 점이다. 이 함수는 단지 ament 메타데이터 레지스트리에 ‘경로(Path)’ 값을 등재하는 텍스트 바인딩 역할만을 수행한다.

실제로 타 패키지가 이 헤더를 워크스페이스 설치 파티션(예: install/패키지명/include/)에서 물리적으로 읽어들이기 위해서는, 네이티브 CMake의 install(DIRECTORY ...) 커맨드가 필수적으로 선행되어야 한다.

# 물리적 복사: 소스 트리의 include 폴더를 설치 계층으로 이관
install(DIRECTORY include/
  DESTINATION include
)

# 논리적 노출: 복사된 설치 계층의 include 폴더를 ament 레지스트리에 등록
ament_export_include_directories(include)

이러한 물리적 행위 패러다임(설치)과 논리적 행위 패러다임(어멘트 익스포트)의 분리는 크로스 컴파일 환경이나 FHS(Filesystem Hierarchy Standard)를 따르는 루트 시스템 전역 설치 시 환경 경로 의존성을 극도로 낮추어 주는 설계이다.

3. 네임스페이스 섀도잉(Shadowing) 방지 기법

수천 개의 패키지가 군집을 이루는 ROS 2 글로벌 워크스페이스 상에서 Utils.hppTypes.hpp와 같은 흔한 명칭은 필연적으로 파일명 충돌을 야기한다. 이를 방지하기 위해 ROS 2 C++ 스타일 가이드는 인클루드 익스포트 시 네임스페이스 기반 하위 디렉터리 구조를 강제한다.

즉, 익스포트 되는 디렉터리의 루트인 include/ 바로 밑에 헤더 파일을 두지 않고, 반드시 include/${PROJECT_NAME}/헤더.hpp 형태로 고립시킨 후 include/ 폴더 자체를 노출한다. 이렇게 조치함으로써 다운스트림 패키지의 전처리기는 #include <my_package/Utils.hpp>와 같이 패키지명 자체가 네임스페이스로 작용하는 정밀한 경로 탐색 기능을 획득하게 되며, 컴파일러 계층에서의 심볼 계층적 충돌 및 파일 섀도잉 현상을 원천적으로 차단한다. (참고 버전: ament_cmake 코어, ROS 2 Humble/Jazzy)