제1부 계산 그래프의 코어
1.12 라이프사이클 노드 — 관리형 상태기계
1.12.1 이 장에서 배우는 것 / 선수 지식
일반 노드의 문제는 "켜져 있다"와 "일할 준비가 됐다"를 구분할 수 없다는 것이다. 프로세스가 기동됐다고 카메라가 열린 것도, 지도가 로드된 것도 아니다. 시스템이 커지면 이 모호함이 부팅 순서 문제·경쟁 조건·"가끔 실패하는 시작"으로 나타난다.
라이프사이클 노드(managed node)는 이 문제에 표준 상태기계로 답한다. 노드가 자기 상태를 선언하고, 외부에서 전이를 명령하며, 전이 실패가 명시적으로 다뤄진다. Nav2 전체가 이 위에 세워져 있고(8.1), 하드웨어 드라이버·센서 노드에서도 사실상 표준이다.
배우는 것:
- 상태와 전이 전체 — 4개의 주 상태, 6개의 전이 상태, 각 전이가 무엇을 해야 하는가.
- 각 콜백에 무엇을 넣는가 — 특히
on_configure와on_activate의 경계. LifecyclePublisher— 비활성 상태에서 발행이 억제되는 메커니즘과 그 함정.- 오류 처리 — 전이 실패,
ErrorProcessing상태, 복구 경로. - 관리자 패턴 — 여러 노드의 전이를 조율하는 법(Nav2
lifecycle_manager). - 일반 노드와의 판단 기준 — 언제 라이프사이클이 값을 하는가.
- 전이 실패 실측(1.12.10) — 반환값별 결과 상태와, 감시가 놓치는
finalized. - 관리자 실측(1.12.11) — 순서는 맞고, 롤백은 하지 않으며, bond에는 조건이 있다.
선수 지식: 1.1(노드), 1.7(서비스 — 전이는 서비스로 명령된다), 1.9(파라미터), 1.4(토픽·발행).
정직: 상태·전이 정의는 ROS 2 설계 문서(Managed Nodes)와
lifecycle_msgs에 근거한다.rclcpp_lifecycle의 API 세부(특히LifecycleNode의 인터페이스 노출 범위)는 배포판에 따라 확장되어 왔다(확인 필요). 이 환경에서 컴파일·실행 검증하지 않았다(목차 3). 다만 전이 실패 경로는 실물로 확인했다(실행 확인 — 1.12.10).
1.12.2 상태기계 전체
주 상태 4개:
| 상태 | 의미 | 이 상태에서 노드는 |
|---|---|---|
Unconfigured | 생성됐지만 아무것도 준비 안 됨 | 자원 없음. 파라미터만 읽을 수 있음 |
Inactive | 준비 완료, 일은 안 함 | 자원 보유, 발행 억제, 콜백 비활성 |
Active | 정상 동작 | 전부 동작 |
Finalized | 종료 | 소멸 대기 |
전이 상태 6개(전이 중 잠시 머무는 상태): Configuring, CleaningUp, ShuttingDown, Activating, Deactivating, ErrorProcessing.
Inactive가 이 설계의 핵심이다. "자원은 확보했지만 일은 안 하는" 상태가 있기 때문에 다음이 가능해진다.
- 부팅 순서 제어 — 모든 노드를
Inactive까지 전환한 뒤, 준비가 확인되면 일제히Active로. 경쟁 조건이 사라진다. - 빠른 재시작 — 프로세스를 강제 종료했다 살리는 대신
deactivate→activate. 하드웨어 재초기화가 필요 없다. - 부분 실패 처리 — 한 노드가
Configuring에서 실패하면 시스템 전체를Active로 전환하지 않는다. - 안전한 정지 —
deactivate로 출력을 멈추되 상태는 유지.
1.12.3 콜백 — 무엇을 어디에 넣는가
#include "rclcpp_lifecycle/lifecycle_node.hpp"
using CallbackReturn =
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn;
class SensorDriver : public rclcpp_lifecycle::LifecycleNode
{
public:
SensorDriver() : rclcpp_lifecycle::LifecycleNode("sensor_driver")
{
// 생성자에서는 파라미터 선언만 — 자원 획득 금지
declare_parameter("device", std::string("/dev/ttyUSB0"));
declare_parameter("rate_hz", 20.0);
}
CallbackReturn on_configure(const rclcpp_lifecycle::State &) override
{
device_ = get_parameter("device").as_string();
rate_hz_ = get_parameter("rate_hz").as_double();
// 자원 획득 — 실패할 수 있는 일은 전부 여기서
if (!hw_.open(device_)) {
RCLCPP_ERROR(get_logger(), "장치 열기 실패: %s", device_.c_str());
return CallbackReturn::FAILURE; // Unconfigured로 되돌아감
}
// 엔티티 생성 (아직 활성화되지 않음)
pub_ = create_publisher<sensor_msgs::msg::Imu>("imu", rclcpp::SensorDataQoS());
timer_ = create_wall_timer(
std::chrono::duration<double>(1.0 / rate_hz_),
std::bind(&SensorDriver::on_timer, this));
timer_->cancel(); // ★ Inactive에서는 실행되면 안 된다
RCLCPP_INFO(get_logger(), "구성 완료: %s @ %.1f Hz", device_.c_str(), rate_hz_);
return CallbackReturn::SUCCESS;
}
CallbackReturn on_activate(const rclcpp_lifecycle::State & state) override
{
LifecycleNode::on_activate(state); // 발행자 활성화 (기반 클래스 호출 필수)
hw_.start_streaming();
timer_->reset(); // 타이머 시작
RCLCPP_INFO(get_logger(), "활성화");
return CallbackReturn::SUCCESS;
}
CallbackReturn on_deactivate(const rclcpp_lifecycle::State & state) override
{
timer_->cancel(); // 먼저 멈추고
hw_.stop_streaming();
LifecycleNode::on_deactivate(state); // 발행자 비활성화
RCLCPP_INFO(get_logger(), "비활성화");
return CallbackReturn::SUCCESS;
}
CallbackReturn on_cleanup(const rclcpp_lifecycle::State &) override
{
// on_configure에서 검출한 것을 전부 해제
timer_.reset();
pub_.reset();
hw_.close();
return CallbackReturn::SUCCESS;
}
CallbackReturn on_shutdown(const rclcpp_lifecycle::State & state) override
{
// 어느 상태에서든 올 수 있다 — 방어적으로
if (timer_) timer_->cancel();
hw_.close();
return CallbackReturn::SUCCESS;
}
CallbackReturn on_error(const rclcpp_lifecycle::State &) override
{
RCLCPP_ERROR(get_logger(), "오류 처리 — 자원 정리");
hw_.close();
return CallbackReturn::SUCCESS; // SUCCESS → Unconfigured, FAILURE → Finalized
}
};
경계 규칙 — 이것이 라이프사이클을 제대로 쓰는 핵심이다:
| 콜백 | 해야 할 일 | 하면 안 되는 일 |
|---|---|---|
| 생성자 | 파라미터 선언만 | 자원 획득, 하드웨어 접근, 실패할 수 있는 일 |
on_configure | 자원 획득, 엔티티 생성, 파라미터 읽기, 실패 가능한 모든 것 | 데이터 발행, 하드웨어 구동 |
on_activate | 스트리밍 시작, 타이머 시작 | 실패할 수 있는 초기화(여기서 실패하면 곤란하다) |
on_deactivate | 출력 정지, 타이머 정지 | 자원 해제 |
on_cleanup | on_configure가 획득한 것 전부 해제 | — |
on_shutdown | 최종 정리(어느 상태에서든 안전하게) | 새 자원 획득 |
on_error | 안전 정리 | 오래 걸리는 작업 |
"실패할 수 있는 일은 on_configure에"가 원칙이다. 그래야 시스템이 활성화되기 전에 문제를 알 수 있다. on_activate에서 실패하면 이미 다른 노드들이 활성화된 뒤일 수 있어 대응이 복잡해진다.
반환값:
| 반환 | 결과 |
|---|---|
SUCCESS | 목표 상태로 전이 |
FAILURE | 원래 상태로 복귀(전이 취소) |
ERROR | ErrorProcessing으로 → on_error 호출 |
1.12.4 LifecyclePublisher — 발행 억제와 그 함정
rclcpp_lifecycle::LifecyclePublisher<sensor_msgs::msg::Imu>::SharedPtr pub_;
LifecycleNode::create_publisher는 일반 발행자가 아니라 LifecyclePublisher를 준다. Active가 아닐 때 publish()를 호출하면 조용히 버려진다.
void on_timer() {
auto msg = read_sensor();
pub_->publish(msg); // Inactive면 아무 일도 안 일어난다
}
이것은 안전장치다 — 비활성 노드가 실수로 데이터를 내보내는 것을 막는다.
함정: "조용히 버려진다"가 디버깅을 어렵게 만든다. 노드가
Inactive인데 그 사실을 모르고 "왜 데이터가 안 오지"를 코드에서 찾게 된다. 가장 먼저 확인할 것은ros2 lifecycle get이다.
ros2 lifecycle nodes # 라이프사이클 노드 목록
ros2 lifecycle get /sensor_driver # 현재 상태
함정:
on_activate에서 기반 클래스 호출을 누락하면 발행자가 활성화되지 않아Active상태인데도 발행이 안 된다.LifecycleNode::on_activate(state)를 반드시 호출한다.
함정: 타이머·구독은 자동으로 비활성화되지 않는다.
LifecyclePublisher만 상태를 인식한다. 타이머를cancel()하지 않으면Inactive에서도 콜백이 실행되고, 하드웨어를 계속 읽고, CPU를 쓴다. 구독 콜백도 마찬가지로 계속 호출된다 — 필요하면 콜백 안에서 상태를 확인하거나 구독을 해제해야 한다.
void on_scan(const sensor_msgs::msg::LaserScan::SharedPtr msg) {
if (get_current_state().id() !=
lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) {
return; // Inactive에서는 처리하지 않음
}
process(msg);
}
1.12.5 외부에서 전이시키기
CLI:
ros2 lifecycle nodes
ros2 lifecycle get /sensor_driver
ros2 lifecycle list /sensor_driver # 현재 가능한 전이
ros2 lifecycle set /sensor_driver configure
ros2 lifecycle set /sensor_driver activate
ros2 lifecycle set /sensor_driver deactivate
노출되는 서비스·토픽:
| 인터페이스 | 역할 |
|---|---|
~/change_state | 전이 명령 |
~/get_state | 현재 상태 조회 |
~/get_available_states | 가능한 상태 |
~/get_available_transitions | 현재 가능한 전이 |
~/transition_event | 전이 발생 토픽 — 외부 감시용 |
~/transition_event를 구독하면 다른 노드의 상태 변화를 실시간으로 감시할 수 있다. 시스템 오케스트레이터가 이를 쓴다.
launch에서 자동 전이:
from launch_ros.actions import LifecycleNode
from launch.actions import EmitEvent, RegisterEventHandler
from launch_ros.events.lifecycle import ChangeState
from launch_ros.event_handlers import OnStateTransition
import lifecycle_msgs.msg
driver = LifecycleNode(
package='my_pkg', executable='sensor_driver',
name='sensor_driver', namespace='',
)
configure = EmitEvent(event=ChangeState(
lifecycle_node_matcher=launch.events.matches_action(driver),
transition_id=lifecycle_msgs.msg.Transition.TRANSITION_CONFIGURE,
))
# configure 성공 후 자동으로 activate
activate_after_configure = RegisterEventHandler(OnStateTransition(
target_lifecycle_node=driver, goal_state='inactive',
entities=[EmitEvent(event=ChangeState(
lifecycle_node_matcher=launch.events.matches_action(driver),
transition_id=lifecycle_msgs.msg.Transition.TRANSITION_ACTIVATE,
))],
))
정직: launch의 라이프사이클 이벤트 API는 장황하고 오류가 나기 쉽다. 노드가 여럿이면 이 방식보다 관리자 노드(다음 절) 가 실용적이다. launch 심층은 14.3·14.4에서 다룬다.
1.12.6 관리자 패턴 — 여러 노드를 조율하기
노드가 10개면 전이 순서·실패 처리·감시가 필요하다. Nav2의 lifecycle_manager가 이 문제의 참조 구현이다.
관리자가 하는 일:
- 순서 있는 전이 — 의존 순서대로
configure→ 전부 성공하면 순서대로activate. - 실패 처리 — 하나가 실패하면 중단한다. 다만 이미 활성화한 노드를 자동으로 되돌리지는 않는다(실행 확인, 1.12.11.2) — 되돌리려면
RESET·SHUTDOWN을 명시적으로 부른다. - 생존 감시(bond) — 활성화된 노드와 연결을 유지하다가, 노드가 종료되면 감지해 시스템을 안전 상태로 내린다. 단 노드가 bond를 만들어 줘야 한다(1.12.11.3).
- 재시작 명령 — 전체를
deactivate→activate.
# Nav2의 경우
ros2 service call /lifecycle_manager_navigation/manage_nodes \
nav2_msgs/srv/ManageLifecycleNodes "{command: 0}" # STARTUP
bond가 중요한 이유: 라이프사이클 상태는 노드가 정상적으로 전이할 때만 갱신된다. 노드가 크래시하면 상태 서비스에 응답이 없을 뿐, 시스템은 그것을 즉시 모른다. bond는 양방향 하트비트로 이를 감지한다.
견해: 직접 관리자를 만들기 전에
nav2_lifecycle_manager를 그대로 쓸 수 있는지 검토하라. 항법과 무관한 시스템에서도 "노드 목록을 순서대로 활성화하고 감시한다"는 기능은 동일하다. 다만 Nav2 의존이 들어오는 것이 부담이면(14.7), 같은 패턴을 자체 구현하는 것도 어렵지 않다 — 핵심은 순서·실패 롤백·bond 세 가지다.
1.12.7 언제 라이프사이클을 쓰는가
값을 하는 경우:
| 상황 | 이유 |
|---|---|
| 하드웨어 자원을 확보하는 노드 | 획득 실패를 명시적으로 다룰 수 있다 |
| 부팅 순서 의존이 있는 시스템 | Inactive 단계에서 동기화 |
| 안전상 출력을 멈춰야 하는 경우 | deactivate로 즉시 정지 |
| 시간이 오래 걸리는 초기화(지도·모델 로드) | 재시작 없이 재활성화 |
| 원격에서 관리해야 하는 노드 | 표준 인터페이스로 조작 |
| 부분 실패를 감내해야 하는 시스템 | 어느 노드가 왜 활성화되지 않았는지 명확 |
값을 못 하는 경우:
| 상황 | 이유 |
|---|---|
| 단순 계산·변환 노드 | 상태가 없어 전이가 무의미 |
| 도구·일회성 스크립트 | 복잡도만 증가 |
| 팀이 라이프사이클을 이해하지 못하는 상황 | 잘못 쓰면 일반 노드보다 불리하다 |
"잘못 쓰면 더 불리하다"가 중요하다. 흔한 오용:
- 생성자에서 자원을 확보한다 → 라이프사이클의 의미가 사라진다.
on_configure와on_activate의 경계를 안 지킨다 → 재활성화가 안 된다.- 타이머·구독을 비활성화하지 않는다 →
Inactive인데 계속 동작한다. on_cleanup을 비워 둔다 →cleanup후configure가 실패한다(자원 중복 획득).
견해: 드라이버·서버 계열은 라이프사이클, 처리·변환 계열은 일반 노드를 기본으로 본다. 그리고 라이프사이클을 도입한다면 위 네 가지 오용을 코드 리뷰 항목으로 두라 — 이것들이 지켜지지 않은 라이프사이클 노드는 복잡도만 늘린 일반 노드다.
1.12.10 전이 실패를 실제로 만들어 봤다 — 실측
1.12.3의 반환값 표(SUCCESS·FAILURE·ERROR)와 on_error의 결과는 명세에 근거해 적은 것이다. 콜백이 원하는 값을 반환하도록 만들어 밖에서 전이를 요청했다(실행 확인, Jazzy).
1.12.10.1 표가 전부 맞는다
| # | on_configure | on_error | 최종 상태 | on_error 호출 |
|---|---|---|---|---|
| ① | SUCCESS | — | inactive → (활성) active | — |
| ② | FAILURE | — | unconfigured(원래 상태) | 안 됨 |
| ③ | ERROR | SUCCESS | unconfigured | 됨 |
| ④ | ERROR | FAILURE | finalized | 됨 |
FAILURE와 ERROR의 차이가 분명하다 — FAILURE는 on_error를 거치지 않고 조용히 원래 상태로 돌아가고, ERROR만 ErrorProcessing을 지난다. 로그에도 그대로 나타난다.
CLI 응답도 갈린다 — FAILURE·ERROR 모두 Transitioning failed이지만, 상태를 다시 물어야 어디로 갔는지 안다. *"실패했다"만 보고 상태를 안 보면 ③과 ④를 구분할 수 없다.
1.12.10.2 on_activate 실패는 두 단계를 되돌린다
| 전이 | 실패 전 상태 | 반환 | 실패 후 상태 |
|---|---|---|---|
configure | unconfigured | ERROR | unconfigured |
activate | inactive | ERROR | unconfigured ← ! |
inactive로 돌아가지 않는다. on_error가 SUCCESS를 반환하면 항상 Unconfigured로 보내므로, 활성화 실패는 on_configure에서 확보한 자원까지 잃는다.
1.12.3의 경계 규칙이 여기서 정당화된다. "실패할 수 있는 일은
on_configure에"·"on_activate에서 실패하면 곤란하다"가 막연한 조언이 아니다 — 활성화 실패는 설정까지 되돌리므로, 재시도하려면configure부터 다시 해야 하고 그동안 다른 노드는 이미active다.
1.12.10.3 finalized는 종단이고, 노드는 종료되지 않는다
④에서 finalized에 들어간 노드를 살펴봤다.
$ ros2 lifecycle list /lcprobe
(가능한 전이가 하나도 없다)
$ ros2 lifecycle set /lcprobe configure
Unknown transition requested, available ones are:
$ ros2 node list | grep -c lcprobe
1 ← 프로세스는 실행 중이다
전이는 하나도 받지 않는데 그래프에는 그대로 있다. 되살릴 방법이 없으므로 프로세스를 재시작하는 수밖에 없다.
오케스트레이션에 직접 영향이 있다(14.2). "노드가 실행 중인가"를
ros2 node list나 프로세스 감시로 확인하는 방식은 이 상태를 정상으로 읽는다. 노드는 존재하고, 프로세스도 실행 중이고, 크래시도 없다 — 다만 아무것도 하지 않는다.감시는 상태로 해야 한다.
/lcprobe/get_state또는ros2 lifecycle get으로 실제 상태를 보고,finalized·unconfigured에 머무르는 노드를 장애로 취급해야 한다. 1.12.6의 관리자 패턴이 이 확인을 포함해야 하는 이유다.
1.12.10.4 확인하지 못한 것
- 관리자(
nav2_lifecycle_manager등)가 부분 실패를 어떻게 처리하는지는 측정하지 않았다(확인 필요)— 1.12.2가 말한 "한 노드가 실패하면 전체를 Active로 전환하지 않는다"는 관리자의 정책이며 이 실험은 노드 한 개의 상태기계만 봤다. on_shutdown·on_cleanup의 실패 경로는 시험하지 않았다(확인 필요).- 전이 중 타임아웃(콜백이 오래 걸릴 때)의 동작은 측정하지 않았다
(확인 필요).
1.12.11 nav2_lifecycle_manager를 실제로 실행해 봤다 — 실측
1.12.6은 관리자가 하는 일을 넷으로 적었다. nav2_lifecycle_manager 1.3.12에 실패하는 노드를 연결해 하나씩 확인했다(실행 확인, Jazzy). 노드는 1.12.10에서 쓴 반환값을 파라미터로 고르는 라이프사이클 노드를 그대로 썼다.
1.12.11.1 ① 순서 있는 전이 — 그대로다
정상 구성(node_names: [nodeA, nodeB])의 로그:
Configuring nodeA
Configuring nodeB
Activating nodeA
Activating nodeB
Managed nodes are active
configure를 전부 끝낸 뒤 activate로 넘어간다. 1.12.6이 적은 "의존 순서대로 configure → 전부 성공하면 순서대로 activate"가 정확하다.
1.12.11.2 ② 실패 처리 — 중단은 하지만 되돌리지 않는다
1.12.6은 "하나가 실패하면 중단하고 이미 전환한 것을 되돌린다"고 적었다. 앞은 맞고 뒤는 틀렸다.
| nodeB의 실패 지점 | 반환 | nodeA 최종 | nodeB 최종 |
|---|---|---|---|
configure | FAILURE | inactive | unconfigured |
configure | ERROR | inactive | unconfigured |
activate | FAILURE | active | inactive |
activate | ERROR | active | unconfigured |
관리자 로그는 매번 이렇게 끝난다.
[ERROR] Failed to change state for node: nodeB
[ERROR] Failed to bring up all requested nodes. Aborting bringup.
Aborting이라고 말하지만 앞선 노드를 원래대로 내리지 않는다. 특히 넷째 줄이 심각하다 — nodeB가 활성화에 실패했는데 nodeA는 active로 남아 데이터를 발행한다.
부분 활성 시스템이 남는다는 뜻이다. 시스템은 "부팅 실패"인데 일부는 실행되고 있고, 아무도 그것을 정리하지 않는다. 되돌리려면
manage_nodes서비스로RESET(3)이나SHUTDOWN(4)을 명시적으로 호출해야 한다(8.1.14.3의 상수표).자체 관리자를 만든다면 이 부분을 직접 구현해야 한다 — 1.12.6이 "핵심은 순서·실패 롤백·bond 셋"이라 했는데, 참조 구현이 그중 롤백을 하지 않는다. 필요하면 스스로 넣어야 한다.
1.12.11.3 ③ bond — 노드 쪽 지원이 필요하다
bond_timeout을 기본값(0이 아닌 값)으로 두고 평범한 rclcpp_lifecycle::LifecycleNode 를 물리면 이렇게 된다.
[ERROR] Server nodeA was unable to be reached after 4.00s by bond.
This server may be misconfigured.
[ERROR] Failed to bring up all requested nodes. Aborting bringup.
전이는 전부 성공했고 노드는 active인데, 관리자가 bond 연결을 못 맺어 부팅을 실패로 판정한다.
bond는 노드가 만들어 줘야 한다 — Nav2의 관리 대상 노드는 nav2_util::LifecycleNode를 상속해 bond를 생성한다. 표준 rclcpp_lifecycle::LifecycleNode에는 그 기능이 없다.
| 하고 싶은 것 | 필요한 것 |
|---|---|
| 순서 있는 전이만 관리 | 표준 라이프사이클 노드 + bond_timeout: 0.0 |
| 생존 감시까지 | 노드가 bond를 만들어야 한다(nav2_util::LifecycleNode 등) |
*1.12.6의 견해("
nav2_lifecycle_manager를 그대로 쓸 수 있는지 검토하라")에 조건이 붙는다. 전이 관리만 필요하면 표준 노드로도 되지만bond_timeout: 0.0을 줘야 하고, 그러면 crash 감지는 포기하는 것이다. 생존 감시가 필요하면 노드 쪽도 Nav2 기반 클래스를 써야 한다* — 그것이 곧 14.7가 말한 의존이 들어온다는 뜻이다.오류 메시지가 "This server may be misconfigured"라 원인을 찾기 좋다 — bond 미지원 노드를 연결했을 때 나오는 전형적 증상이다.
1.12.11.4 확인하지 못한 것
- 노드를 크래시시켰을 때 bond가 실제로 감지하는지는 측정하지 않았다 — bond를 만드는 노드(
nav2_util::LifecycleNode)를 쓰지 않았기 때문이다(확인 필요). manage_nodes의RESET·SHUTDOWN이 부분 활성 상태를 실제로 정리하는지는 확인하지 않았다(확인 필요).attempt_respawn_reconnection·bond_respawn_max_duration파라미터의 동작은 시험하지 않았다.
1.12.8 함정 카탈로그
| # | 함정 | 증상 | 대처 |
|---|---|---|---|
| 1 | 생성자에서 자원 획득 | 전이 의미 상실, 실패 처리 불가 | on_configure로 |
| 2 | on_activate에서 기반 클래스 미호출 | Active인데 발행 안 됨 | LifecycleNode::on_activate(state) |
| 3 | 타이머를 cancel() 안 함 | Inactive에서도 동작 | 명시적 정지 |
| 4 | 구독 콜백이 상태 무시 | 비활성인데 처리 | 콜백에서 상태 확인 |
| 5 | on_cleanup 비워 둠 | 재configure 실패 | 대칭적으로 해제 |
| 6 | on_activate에서 실패 가능 작업 | 시스템 부분 활성화 상태 | on_configure로 이동 |
| 7 | 상태 확인 없이 "데이터 안 옴" 디버깅 | 시간 낭비 | ros2 lifecycle get 먼저 |
| 8 | 크래시를 상태로 감지 시도 | 감지 안 됨 | bond·프로세스 감시 |
| 9 | 전이 순서 미정의 | 경쟁 조건 | 관리자 노드 |
| 10 | on_shutdown이 특정 상태 가정 | 예외·크래시 | 방어적 구현 |
| 11 | launch 이벤트로 다수 노드 조율 | 장황·취약 | 관리자 노드 |
| 12 | 일반 노드와 혼합 시 순서 보장 안 됨 | 부팅 경쟁 | 경계 명확히(14.2) |
| 13 | 노드 존재만으로 실행 중이라고 판단 | finalized 노드도 그래프에 남는다 | 상태로 감시(1.12.10.3) |
| 14 | on_activate 실패 후 inactive일 것으로 기대 | unconfigured로 떨어진다 | 자원까지 잃는다(1.12.10.2) |
| 15 | "전이 실패"만 보고 원인 판단 | FAILURE·ERROR가 같은 메시지 | 상태를 다시 물어라(1.12.10.1) |
| 16 | 관리자가 실패 시 되돌려 줄 것으로 기대 | 중단만 하고 부분 활성이 남는다 | RESET·SHUTDOWN을 명시 호출(1.12.11.2) |
| 17 | 표준 라이프사이클 노드에 bond 기대 | 부팅이 실패로 판정된다 | bond_timeout: 0.0 또는 Nav2 기반 클래스(1.12.11.3) |
1.12.9 체크포인트
- 주 상태 4개와 전이 상태 6개를 말할 수 있는가.
Inactive가 존재하기 때문에 가능해지는 일 네 가지는? - 생성자·
on_configure·on_activate에 각각 무엇을 넣고 무엇을 넣지 않는가. "실패할 수 있는 일은on_configure에"의 근거는? - 콜백 반환값 세 가지가 각각 어떤 상태 전이를 일으키는가.
LifecyclePublisher가 비활성 상태에서 하는 일과, 그것이 디버깅을 어렵게 만드는 이유를 말할 수 있는가.- 라이프사이클 노드에서 자동으로 비활성화되지 않는 것은 무엇인가. 어떻게 처리하는가.
- 데이터가 안 올 때 라이프사이클 노드라면 가장 먼저 실행할 명령은?
- 관리자 패턴이 해 주는 네 가지와, bond가 필요한 이유를 설명할 수 있는가.
- 라이프사이클이 값을 하는 상황과 오히려 해가 되는 상황을 각각 세 가지 들 수 있는가.
- 라이프사이클을 잘못 쓰는 대표적 네 가지 방식은 무엇인가.
FAILURE와ERROR의 차이를 결과 상태와on_error호출 여부로 설명할 수 있는가(1.12.10.1).on_activate에서 실패하면 어느 상태로 가는가. 그것이 왜 아픈가(1.12.10.2).finalized노드를 프로세스 감시로 감지할 수 없는 이유는(1.12.10.3).- 관리자가 부팅에 실패했을 때 앞선 노드는 어떤 상태로 남는가(1.12.11.2).
- 표준 라이프사이클 노드를
nav2_lifecycle_manager에 물리려면 무엇을 조정해야 하며, 그 대가는(1.12.11.3).
다음 장 1.13은 제1부의 마지막 — 컴포지션이다. 여러 노드를 한 프로세스에 담아 통신 비용을 없애는 방법과, 그 대가로 잃는 격리를 다룬다.