AEGLOSLAB ROS 2 Jazzy 대전

제14부 시스템 설계·품질·운영

14.3 launch 심층 I — 구조와 substitution

14.3.1 이 장에서 배우는 것 / 선수 지식

4.9에서 launch의 기본을 다뤘다. 이 장은 왜 launch가 그렇게 생겼는지 — 그 설계의 근본을 분석한다.

핵심 개념 하나를 먼저 세운다. launch 파일은 스크립트가 아니라 "실행 계획을 기술하는 것"이다. generate_launch_description()은 실행 시점에 한 번 호출되어 액션의 목록을 만들 뿐이고, 실제 실행은 그다음에 launch 시스템이 한다. 이 구분을 모르면 "왜 내 if 문이 동작하지 않는가"라는 벽에 부딪힌다 — ROS 2 launch 학습의 최대 난관이다.

배우는 것:

  • 실행 모델 — 기술 시점과 실행 시점.
  • substitution — 지연 평가의 핵심.
  • 주요 substitution — 무엇이 있는가.
  • 조건 — IfCondition과 그 이유.
  • 액션 — 무엇을 할 수 있는가.
  • 이벤트 — 반응형 구성.
  • 파라미터 전달 — 흔한 함정들.
  • 디버깅 — launch가 안 될 때.

선수 지식: 0.6(패키지·launch 첫 경험), 1.9(파라미터), 1.1.5(리매핑), 1.13(컴포지션).

정직: substitution·condition 클래스는 Jazzy에서 전수 조회하고 조건 평가를 실제로 실행해 확인했다(실행 확인 — 14.3.12). 그 결과 이 장이 다루지 않던 substitution 여러 개와, 폐기 예정 클래스 두 개를 찾았다. 93차에 함정 문단 하나를 추가로 재현하다가 서술 오류를 찾아 정정했다(14.3.12.4) — 기본값 없는 LaunchConfiguration은 "빈 문자열이 됐다가 종료되는" 것이 아니라 참조하는 순간 즉시 예외를 던진다. 표기가 없는 나머지 서술(실행 모델·이벤트·구조 설명)은 문서·소스에 근거하며 실행 검증하지 않았다(목차 3).

14.3.2 실행 모델 — 두 시점

이것이 이 장에서 가장 중요한 개념이다.

ros2 launch 실행 ① 기술 시점 generate_launch_description() 호출 LaunchDescription = 액션 목록 ② 실행 시점 launch 시스템이 액션 수행 substitution 평가 프로세스 기동 · 이벤트 처리
시점무슨 일이
① 기술Python 코드가 실행되어 액션 목록을 만든다
② 실행launch가 액션을 하나씩 수행하며 substitution을 평가

결정적 함의:

사실결과
launch 인자 값은 ①에서 모른다Python if로 분기 불가
substitution은 ②에서 평가값이 필요하면 substitution으로
①의 Python 변수는 상수실행 중 변하지 않음
LaunchConfiguration은 값이 아니라 "참조"문자열 연산 불가
# ✗ 동작하지 않는다 — 기술 시점에는 값이 없다
use_sim = LaunchConfiguration('use_sim_time')
if use_sim == 'true':            # ✗ 항상 False (객체 비교)
    nodes = [sim_node]
else:
    nodes = [real_node]

# ✓ 조건은 substitution으로 표현
Node(package='p', executable='sim_node',
     condition=IfCondition(LaunchConfiguration('use_sim_time')))
Node(package='p', executable='real_node',
     condition=UnlessCondition(LaunchConfiguration('use_sim_time')))

견해: "launch 인자로 분기하려면 condition을 쓴다"가 규칙이다. Python if는 기술 시점에 이미 알 수 있는 것(환경 변수, 파일 존재 여부, 함수 인자)에만 쓸 수 있다. 이 구분을 명확히 하면 launch의 상당 부분이 이해된다.

견해: Python if를 쓸 수 있는 경우도 있다 — OpaqueFunction(14.3.6)을 쓰면 실행 시점에 값을 얻어 Python으로 분기할 수 있다. 다만 그러면 launch의 선언적 성격을 잃으므로, 꼭 필요할 때만 쓰는 것이 좋다.

14.3.3 substitution

실행 시점에 평가되는 "값의 약속"이다.

substitution의미
LaunchConfiguration('x')launch 인자 값
PathJoinSubstitution([...])경로 결합
FindPackageShare('pkg')패키지 share 경로
TextSubstitution(text=...)리터럴
EnvironmentVariable('X')환경 변수
Command([...])명령 실행 결과(xacro 등)
PythonExpression([...])파이썬 식 평가
AndSubstitution·OrSubstitution·NotSubstitution논리 — 전부 실재(실행 확인)
LocalSubstitution이벤트 문맥
# 경로 결합 — 문자열 연산 대신
params = PathJoinSubstitution([
    FindPackageShare('my_robot_bringup'), 'config', 'nav.yaml'])

# xacro 처리 (5.4)
robot_description = ParameterValue(
    Command(['xacro ', PathJoinSubstitution([
        FindPackageShare('my_robot_description'), 'urdf', 'robot.urdf.xacro']),
        ' use_sim:=', LaunchConfiguration('use_sim_time')]),
    value_type=str)

# 파이썬 식으로 조건 조합
condition=IfCondition(PythonExpression([
    "'", LaunchConfiguration('mode'), "' == 'nav' and ",
    LaunchConfiguration('use_sim_time')]))

함정: Command(['xacro ', path])에서 'xacro ' 뒤 공백을 빠뜨리면 명령이 붙어 버린다. substitution 리스트는 그대로 이어 붙여지므로, 공백을 명시적으로 넣어야 한다. 흔한 실수다.

함정: Command의 결과를 파라미터로 줄 때 ParameterValue(..., value_type=str)로 감싸야 하는 경우가 있다(확인 필요: 배포판별 필요성). 감싸지 않으면 타입 추론이 잘못돼 URDF가 비정상적으로 전달될 수 있다.

견해: os.path.join과 PathJoinSubstitution을 구분하라. 기술 시점에 확정된 경로는 os.path.join으로 충분하지만, launch 인자가 섞이면 반드시 PathJoinSubstitution 이어야 한다. 섞어 쓰다 오류가 나는 것이 흔하다.

견해: PythonExpression은 강력하지만 읽기 어렵다. 따옴표 처리가 까다롭고 디버깅이 어렵다. 복잡한 조건이 필요하면 OpaqueFunction으로 옮기는 것이 대개 더 명확하다.

14.3.4 조건

DeclareLaunchArgument('use_rviz', default_value='true',
                      description='RViz2를 함께 실행')

Node(package='rviz2', executable='rviz2',
     condition=IfCondition(LaunchConfiguration('use_rviz')))
조건의미
IfCondition(x)x가 참이면 실행
UnlessCondition(x)x가 거짓이면
LaunchConfigurationEquals폐기 예정(실행 확인) — IfCondition(EqualsSubstitution(a, b))로 대체

함정: 참으로 인정되는 문자열이 정해져 있다 — 대소문자를 무시하고 true·1만 참, false·0만 거짓이다(실행 확인). 'yes'·'ON'은 거짓으로 판정되는 것이 아니라 InvalidConditionExpressionError로 중단된다. 빈 문자열도 마찬가지다. 조용한 오판이 아니라는 점은 다행이지만, launch 파일이 그 자리에서 종료된다. DeclareLaunchArgument의 choices로 값을 제한하면 실수를 줄인다.

DeclareLaunchArgument('mode', default_value='nav',
                      choices=['nav', 'manip', 'idle'],
                      description='시스템 모드')

견해: 모든 launch 인자에 description과 가능하면 choices를 넣으라. ros2 launch <pkg> <file> --show-args로 인자 목록을 볼 수 있어(14.3.8), 사용자가 소스를 안 읽어도 되도록 한다. 이것이 launch 파일의 사용성을 크게 바꾼다.

14.3.5 주요 액션

액션역할
NodeROS 노드 실행
ExecuteProcess임의 프로세스
ComposableNodeContainer·LoadComposableNodes컴포넌트(2.7)
IncludeLaunchDescription다른 launch 포함
GroupAction묶기·범위
PushRosNamespace네임스페이스 적용
SetParameter전역 파라미터
SetEnvironmentVariable환경 변수
DeclareLaunchArgument인자 선언
TimerAction지연 실행
OpaqueFunctionPython 콜백(14.3.6)
RegisterEventHandler이벤트(14.3.7)
LogInfo메시지
# 그룹 + 네임스페이스 (1.1.6)
GroupAction([
    PushRosNamespace('robot1'),
    IncludeLaunchDescription(
        PythonLaunchDescriptionSource(PathJoinSubstitution([
            FindPackageShare('my_robot_bringup'), 'launch', 'base.launch.py'])),
        launch_arguments={'use_sim_time': LaunchConfiguration('use_sim_time')}.items()),
])

견해: GroupAction으로 범위를 만드는 것이 대규모 구성의 핵심이다(14.4). PushRosNamespace·SetParameter가 그룹 안에만 적용되므로, 여러 로봇·여러 서브시스템을 격리할 수 있다.

함정: SetParameter는 그 범위의 모든 노드에 적용된다. use_sim_time을 전역으로 주는 데는 편리하지만(1.10.9), 의도치 않은 노드에도 들어간다. 범위를 GroupAction으로 제한하는 것이 안전하다.

함정: TimerAction으로 순서를 맞추는 것은 취약하다(14.2.3의 반복). 이벤트 핸들러나 준비 확인이 견고하다. 다만 간단한 개발용 launch에서는 실용적일 수 있으므로, 운영용과 구분한다.

14.3.6 OpaqueFunction

실행 시점에 Python 코드를 실행하는 탈출구다.

def launch_setup(context, *args, **kwargs):
    # ★ 여기서는 값을 실제로 읽을 수 있다
    mode = LaunchConfiguration('mode').perform(context)
    robot = LaunchConfiguration('robot_name').perform(context)

    nodes = []
    if mode == 'nav':                       # ✓ 이제 Python if가 동작
        nodes.append(Node(package='nav2_bringup', executable='...'))
    elif mode == 'manip':
        nodes.append(Node(package='moveit_ros_move_group', executable='...'))

    # 파일 존재 확인 등 임의 로직 가능
    cfg = os.path.join(get_package_share_directory('my_bringup'),
                       'config', f'{robot}.yaml')
    if not os.path.exists(cfg):
        raise RuntimeError(f'설정 파일 없음: {cfg}')

    return nodes

def generate_launch_description():
    return LaunchDescription([
        DeclareLaunchArgument('mode', default_value='nav'),
        DeclareLaunchArgument('robot_name', default_value='robot1'),
        OpaqueFunction(function=launch_setup),
    ])
쓸 때이유
복잡한 조건 분기PythonExpression보다 명확
설정 파일 검증존재·내용 확인
동적 노드 생성개수가 인자에 의존
오류를 조기에명확한 메시지

견해: OpaqueFunction은 남용하면 launch가 스크립트가 된다. 선언적 구조의 이점(구성 파악·재사용·도구 지원)을 잃는다. 분기가 서너 갈래를 넘거나 검증이 필요할 때만 쓰고, 단순 조건은 condition으로 두는 것이 좋다.

견해: 설정 파일 존재를 launch에서 검증하는 것은 매우 유용하다. 파일이 없으면 노드가 비정상적인 기본값으로 조용히 기동되는 대신(1.7.9), 명확한 오류 메시지와 함께 즉시 실패한다. 현장 진단 시간을 크게 줄인다.

14.3.7 이벤트

반응형 구성을 만든다.

# 프로세스 종료에 반응
RegisterEventHandler(
    OnProcessExit(
        target_action=map_saver_node,
        on_exit=[LogInfo(msg='지도 저장 완료'), EmitEvent(event=Shutdown())]))

# 프로세스 시작에 반응
RegisterEventHandler(
    OnProcessStart(
        target_action=driver_node,
        on_start=[TimerAction(period=2.0, actions=[controller_node])]))

# 생애주기 전이에 반응 (1.12)
RegisterEventHandler(
    OnStateTransition(
        target_lifecycle_node=nav_node,
        goal_state='active',
        entities=[LogInfo(msg='항법 활성화됨')]))
이벤트용도
OnProcessExit종료 후 정리·연쇄
OnProcessStart시작 후 후속
OnStateTransition생애주기 조율(1.12·14.2.3)
OnShutdown종료 처리
OnExecutionComplete액션 완료

견해: 생애주기 이벤트로 부팅 순서를 조율하는 것이 launch의 표준적 해법이다(14.2.3). TimerAction의 지연 대신 "드라이버가 active가 되면 컨트롤러를 활성화" 로 기술하면, 느린 날에도 올바르게 동작한다.

함정: OnProcessExit로 재시작을 구현하면 무한 루프가 될 수 있다. 노드가 계속 종료되면 계속 재시작된다(14.2.6의 함정). 횟수 제한이 필요하면 launch보다 systemd(14.8)가 적합하다.

견해: launch는 "기동"에 강하고 "장기 운영 관리"에는 약하다. 재시작 정책·자원 제한·로그 관리는 systemd나 컨테이너 오케스트레이터의 영역이다. 개발에는 launch, 운영에는 systemd + launch 조합이 흔하다(14.8).

14.3.8 파라미터 전달의 함정

방법특성
딕셔너리코드에 직접
YAML 파일 경로외부 관리
섞어 쓰기순서 주의
SetParameter범위 전체
Node(package='my_pkg', executable='my_node',
     name='my_node',                    # ★ 이름이 YAML 키와 일치해야
     namespace='robot1',
     parameters=[
         params_yaml,                   # 파일
         {'use_sim_time': LaunchConfiguration('use_sim_time')},  # 덮어쓰기
     ],
     remappings=[('/scan', '/robot1/scan')],
     output='screen')

함정: YAML 파일의 노드 이름·네임스페이스가 실제와 다르면 파라미터가 적용되지 않는다(1.9.7). 조용히 기본값으로 동작하므로 발견이 늦다. ros2 param get으로 실제 값을 확인하는 습관이 필요하다.

# 네임스페이스가 있으면 YAML도 맞춰야 한다
/robot1/my_node:
  ros__parameters:
    rate: 20.0

# 와일드카드 — /** 는 네임스페이스 깊이와 무관하게 모두 적용된다(실행 확인)
/**:
  ros__parameters:
    use_sim_time: true

견해: /** 와일드카드가 use_sim_time 같은 공통 파라미터에 유용하다(실행 확인 — 아래 실측). 다만 의도치 않은 노드에도 적용되므로 범위를 인지해야 한다.

두 와일드카드의 차이(실행 확인, Jazzy):

키적용 대상
/*루트 직속 노드만 — /top은 받고 /a/deep은 못 받는다
/**모든 깊이 — /top·/a/deep·/a/b/deeper 전부

와일드카드와 특정 노드 항목은 함께 적용된다. /**에 use_sim_time, /my_ns/specific_node에 only_here를 두면 그 노드는 둘 다 받는다(실측). 즉 "공통은 /**, 개별은 노드 이름으로"가 성립한다.

함정: /*를 "모든 노드"로 오해하기 쉽다. 네임스페이스를 쓰는 순간(다중 로봇·컴포넌트 컨테이너) 그 아래 노드는 /*에 걸리지 않는다 — use_sim_time처럼 빠지면 조용히 틀리는 파라미터에 /*를 쓰면 위험하다. 공통 파라미터에는 /**를 쓰라.

함정: 파라미터 목록은 "뒤가 앞을 덮어쓴다"만으로는 부족하다 — 지정자의 구체성이 순서에 우선한다(실행 확인, 14.4.11.2). 부분 덮어쓰기는 딥 머지로 기대대로 동작하지만, 나중에 준 /**: 파일도 앞선 /노드이름: 항목은 못 덮고 명령줄 -p조차 진다. 파일과 딕셔너리를 섞을 때는 이 규칙까지 감안해 의도를 주석으로 남기라.

견해: 리매핑과 네임스페이스를 함께 쓸 때 혼란이 크다(1.1.5·1.1.6). 절대 이름(/scan)과 상대 이름(scan)의 차이가 결과를 바꾸므로, ros2 node info로 실제 이름을 확인하는 것이 확실하다.

14.3.9 디버깅

# 인자 목록 확인
ros2 launch my_pkg my.launch.py --show-args

# 실행 없이 기술만 출력 (확인 필요: 옵션)
ros2 launch my_pkg my.launch.py --print-description

# 상세 로그
ros2 launch my_pkg my.launch.py --debug

# 실제 실행되는 명령 확인
# (Node 액션이 만드는 프로세스 인자)
증상원인 후보
조건이 안 먹힘Python if 사용·문자열 값
경로 오류os.path vs substitution 혼용
파라미터 미적용이름·네임스페이스 불일치
xacro 실패Command 공백·경로
노드가 안 뜸실행 파일명·패키지 미설치
순서 문제이벤트·준비 확인 필요
오버레이 문제옛 launch 파일 실행(4.5.8)

견해: "고쳤는데 반영이 안 된다"면 오버레이를 의심하라(11.1.4). colcon build로 설치된 launch 파일이 실행되므로, 소스만 고치고 빌드하지 않으면 옛것이 실행된다. 심볼릭 설치(--symlink-install)를 쓰면 이 문제가 줄어든다(4.5).

견해: launch 오류 메시지는 읽기 어렵지만 실마리가 있다. 스택 트레이스 안에 substitution 평가 실패 지점이 나오므로, 끝까지 읽는 것이 추측보다 빠르다.

14.3.12 substitution·condition 전수 — 실측

Jazzy에 설치된 launch·launch_ros를 직접 조회해 클래스 전수 목록을 얻고, 조건 평가는 값을 넣어 실행해 봤다(실행 확인).

python3 -c "import launch.substitutions as s; print([n for n in dir(s) if n[0].isupper()])"

14.3.12.1 이 장이 다루지 않던 substitution

launch.substitutions는 22종이다. 14.3.4의 표에 없던 것 중 실무에서 쓸 만한 것들:

클래스하는 일언제
EqualsSubstitution · NotEqualsSubstitution두 값 비교 → 'true'/'false'폐기된 LaunchConfigurationEquals의 정식 대체
IfElseSubstitution조건에 따라 둘 중 하나값 자체를 갈라야 할 때(노드를 갈라야 하면 IfCondition)
AllSubstitution · AnySubstitution리스트 전체 AND / OR조건이 여러 개일 때 And를 겹치지 않아도 된다
NotSubstitution부정
FileContent파일 내용을 그대로URDF를 Command로 매번 생성하지 않고 미리 만들어 둔 파일을 읽을 때
AnonName익명 이름 생성
LaunchLogDir이번 실행의 로그 디렉터리산출물을 로그 옆에 남길 때
ThisLaunchFile · ThisLaunchFileDir자기 자신 경로
PathSubstitution · PathJoinSubstitution경로

launch_ros.substitutions는 5종이다: FindPackageShare · FindPackage · FindPackagePrefix · ExecutableInPackage · Parameter.

FindPackageShare(share 디렉터리)와 FindPackagePrefix(설치 prefix)를 혼동하기 쉽다. 설정 파일·URDF는 share, 실행 파일은 ExecutableInPackage다.

14.3.12.2 조건 평가 — 값별 실측

IfCondition에 값을 넣어 실제로 평가시킨 결과다:

넣은 값결과
'true' · 'True' · '1'참
'false' · 'False' · '0'거짓
'yes'InvalidConditionExpressionError
''(빈 문자열)InvalidConditionExpressionError

내부 상수는 VALID_TRUE_EXPRESSIONS = ['true', '1'] · VALID_FALSE_EXPRESSIONS = ['false', '0']이고, 비교 전에 소문자화하므로 'True'도 통한다.

이 규칙은 xacro와 같다. 5.4.12.2에서 xacro의 xacro:if를 같은 값들로 시험했는데 결과가 일치한다 — 둘 다 true/1 계열은 참, yes는 오류. 로봇 설정을 launch 인자 → xacro 인자로 넘길 때 값이 그대로 통한다는 뜻이라, 실무에서 신경 쓸 일이 하나 줄어든다.

함정: 빈 문자열이 오류라는 점을 놓치기 쉽다(위 표, IfCondition('')). DeclareLaunchArgument에 default_value를 반드시 주는 이유가 이것이다 — 정확한 실패 경로는 14.3.12.4에서 정정한다.

14.3.12.3 폐기 예정 두 개

UserWarning: The 'LaunchConfigurationEquals' and 'LaunchConfigurationNotEquals'
Conditions are deprecated. Use the 'EqualsSubstitution' ...

실제로 생성만 해도 위 경고가 출력된다(실행 확인). 대체는 이렇다:

# 옛 방식 (폐기 예정)
Node(..., condition=LaunchConfigurationEquals('mode', 'nav'))

# 지금 방식
Node(..., condition=IfCondition(
    EqualsSubstitution(LaunchConfiguration('mode'), 'nav')))

조건 클래스는 IfCondition·UnlessCondition 둘로 수렴하고, 비교는 substitution이 맡는 구조로 정리된 것이다. 조합이 자유로워져 And/Or/Not과 섞어 쓸 수 있다.

14.3.12.4 "선언 안 됐거나 기본값이 없으면 빈 문자열"은 틀렸다 — 실측

실행 확인(2026-08-18, ros:jazzy-ros-base, ros2 launch) — 14.3.12.2의 함정 문단은 "LaunchConfiguration이 선언되지 않았거나 기본값이 없으면 빈 문자열이 되고, 그 자리에서 launch가 종료된다"고 적었다. 재현해 보니 빈 문자열 단계 자체가 없었다.

시나리오실제 오류
DeclareLaunchArgument 없이 LaunchConfiguration('use_sim')을 바로 참조launch configuration 'use_sim' does not exist
DeclareLaunchArgument('use_sim')로 선언은 했지만 default_value 없음, CLI 인자도 안 줌Included launch description missing required argument 'use_sim' …

둘 다 종료 코드 1로 즉시 종료되는 것은 맞지만, 값이 빈 문자열로 치환된 뒤 종료되는 것이 아니라 LaunchConfiguration을 평가(perform)하는 순간 그 자체가 예외를 던진다. 선언 여부에 따라 오류 메시지도 다르다 — 선언조차 안 됐으면 "존재하지 않는다", 선언은 됐는데 값이 없으면 "필수 인자 누락"이다.

결론은 바뀌지 않지만 원인은 바뀐다. "default_value를 반드시 주라"는 처방은 여전히 옳다 — 다만 그 이유는 "안 그러면 빈 문자열이 조용히 흘러들어서"가 아니라 "안 그러면 그 자리에서 명시적 예외로 즉시 멈추기 때문"이다. 실무 영향은 오히려 반대다 — 빈 문자열이라면 하위 노드까지 흘러가 조용히 이상 동작할 위험이 있지만, 실제로는 launch 시작 단계에서 크고 시끄럽게 실패하므로 디버깅은 이 문단이 암시하는 것보다 쉽다.

14.3.10 함정 카탈로그

#함정증상대처
1Python if로 launch 인자 분기항상 한쪽condition
2LaunchConfiguration을 문자열로 취급오류·이상 동작substitution
3os.path.join에 substitution 혼용경로 오류PathJoinSubstitution
4Command(['xacro', path]) 공백 누락명령 실패'xacro '
5ParameterValue 미사용URDF 전달 이상감싸기
6참 문자열 오해조건 무시됨choices 제한
7인자에 description 없음사용성 저하항상 작성
8SetParameter 범위 과다의도치 않은 적용GroupAction
9TimerAction으로 순서간헐 실패이벤트·준비 확인
10OpaqueFunction 남용선언성 상실필요 시만
11YAML 노드 이름 불일치파라미터 미적용(경고 없음)이름·NS 대조 → /**:로 바꿔 적용되면 확정(11.1.14.3)
12파라미터 순서 혼동예상 밖 값주석·확인
13리매핑 절대/상대 혼동잘못된 연결node info
14OnProcessExit 재시작 루프무한 반복systemd
15빌드 없이 launch 수정반영 안 됨빌드·symlink
16LaunchConfiguration 기본값 미지정참조하는 순간 즉시 예외로 중단(빈 문자열이 흘러가는 것이 아니다)default_value 필수(14.3.12.2·14.3.12.4)

14.3.11 체크포인트

  • launch의 두 시점을 설명하고, 그 결정적 함의 네 가지를 말할 수 있는가.
  • Python if를 쓸 수 있는 경우와 없는 경우를 구분할 수 있는가.
  • 주요 substitution 아홉 가지와 각각의 용도는? 실제로 제공되는 것은 몇 종인가(14.3.12.1).
  • Command로 xacro를 처리할 때의 두 함정은?
  • os.path.join과 PathJoinSubstitution의 사용 기준은?
  • PythonExpression의 한계와 대안은?
  • 조건 액션 사용법과, 참 문자열 함정의 대처는?
  • launch 인자에 description·choices를 넣으라는 조언의 근거는?
  • 주요 액션 열세 가지 중 대규모 구성의 핵심은 무엇인가.
  • SetParameter의 범위 문제와 대처는?
  • OpaqueFunction을 쓸 네 상황과, 남용의 위험은?
  • 설정 파일 검증을 launch에서 하는 것의 이점은?
  • 이벤트 다섯 종류와, 부팅 순서 조율의 표준적 해법은?
  • launch와 systemd의 역할 구분은?
  • 파라미터 전달의 함정 네 가지를 들 수 있는가.
  • launch 디버깅 명령 셋과, "반영이 안 된다"의 원인은?

다음 장 14.4은 이 도구로 대규모 시스템을 구성하는 방법 — 계층화·재사용·다중 로봇을 다룬다.