# 라이프사이클 노드(Lifecycle Node)와 QoS 활용

#### 라이프사이클 노드 개념

라이프사이클 노드(Lifecycle Node)는 ROS2에서 노드의 생명 주기를 명확하게 관리하기 위해 제공되는 기능이다. 일반 노드(normal node)와 달리 특정 상태(State)와 전이(Transition)를 명시적으로 구분하여, 노드가 동작 가능한 시점과 수행해야 할 작업을 체계적으로 제어할 수 있다. 이를 통해 노드 초기화, 파라미터 설정, 네트워크 연결과 같은 과정을 엄격하게 구분하고, 특정 상태에서만 센서나 액추에이터와 연동되도록 구성함으로써 더욱 안정적인 시스템 구성과 디버깅이 가능하다.

라이프사이클 노드는 다음과 같은 장점을 가진다.

* **명확한 상태 관리**: 노드의 다양한 실행 단계(예: unconfigured, active 등)를 구분해 문제 발생 시 정확히 어느 상태에서 문제가 생겼는지 파악 가능
* **예측 가능성**: 노드가 전이 과정을 따르므로, 노드가 의도치 않은 상태에서 동작하거나 메시지를 송수신하지 않도록 방지
* **안정성 향상**: 각 상태별로 리소스 할당과 해제를 체계적으로 관리하여, 하드웨어나 소프트웨어 리소스를 안정적으로 사용

아래는 ROS2에서 정의하는 라이프사이클 노드의 대표적인 상태와 전이를 요약한 도식이다.

{% @mermaid/diagram content="stateDiagram-v2
\[\*] --> unconfigured
unconfigured --> configuring : on\_configure
configuring --> inactive : SUCCESS
configuring --> unconfigured : FAILURE
inactive --> activating : on\_activate
activating --> active : SUCCESS
activating --> inactive : FAILURE
active --> deactivating : on\_deactivate
deactivating --> inactive : SUCCESS
deactivating --> active : FAILURE
inactive --> cleaningup : on\_cleanup
cleaningup --> unconfigured : SUCCESS
cleaningup --> inactive : FAILURE
unconfigured --> shuttingdown : on\_shutdown
inactive --> shuttingdown : on\_shutdown
active --> shuttingdown : on\_shutdown
shuttingdown --> finalized : SUCCESS
shuttingdown --> unconfigured : FAILURE" %}

#### 라이프사이클 노드 주요 상태

* **unconfigured**: 노드가 초기화되었지만 아직 파라미터나 퍼블리셔, 서브스크라이버, 서비스, 액션 등이 준비되지 않은 상태
* **inactive**: 노드가 필요한 자원을 확보하고 토픽, 서비스 등이 초기화되었으나, 실제 동작(예: 메시지 송신, 콜백 실행) 등이 진행되지 않는 상태
* **active**: 노드가 실제로 메시지 송수신과 콜백을 수행하며, 시스템 일부로 활발히 동작하는 상태
* **finalized**: 노드가 완전히 종료된 상태

#### 라이프사이클 노드 전이(Transition)

* **on\_configure**: unconfigured 상태에서 inactive 상태로 전이하기 위해 수행하는 콜백 함수. 여기서 퍼블리셔, 서브스크라이버, 서비스 등을 초기화한다.
* **on\_activate**: inactive 상태에서 active 상태로 전이하기 위해 수행하는 콜백 함수. 여기서 실제 메시지 송수신이 일어날 수 있도록 인터페이스를 활성화한다.
* **on\_deactivate**: active 상태에서 inactive 상태로 전이하기 위해 수행하는 콜백 함수. 여기서 메시지 송수신 동작을 중단한다.
* **on\_cleanup**: inactive 상태에서 unconfigured 상태로 돌아가기 위해 수행하는 콜백 함수. 여기서 퍼블리셔, 서브스크라이버, 서비스 등의 자원을 해제한다.
* **on\_shutdown**: 모든 상태에서 final 상태로 가는 과정을 처리하는 콜백 함수. 노드를 안전하게 종료하기 위해 필요한 작업을 수행한다.

#### 라이프사이클 노드와 QoS의 관계

라이프사이클 노드를 사용할 경우, 노드가 active 상태가 되기 전까지는 메시지 송수신을 하지 않는다. 이는 QoS 설정과 매우 밀접한 관계가 있다. 예를 들어 노드가 inactive 상태에서 QoS 설정을 바꾸거나, 퍼블리셔의 QoS 프로파일을 재설정한 뒤 on\_activate를 통해 활성화하면, active 상태에 진입했을 때 해당 QoS가 제대로 반영된다.

ROS2의 QoS(품질 서비스, Quality of Service) 설정은 토픽 레벨에서 다음과 같은 매개변수를 지정할 수 있다.

* **Reliability**: 메시지 전달 보장 수준 (Best Effort / Reliable)
* **Durability**: 과거 메시지 보존 여부 (Volatile / Transient Local 등)
* **History**: 버퍼 크기(Keep All / Keep Last)
* **Deadline**: 메시지 전달 데드라인 시간
* **Liveliness**: 노드 생존 여부 파악 방식

라이프사이클 노드에서 QoS를 활용하면 다음과 같은 이점이 있다.

1. **상태 전이 시점에서 QoS 재설정**: on\_configure 단계에서 QoS 매개변수를 동적으로 재설정한 뒤, on\_activate 시점에 반영 가능
2. **비활성 상태(Inactive)에서의 메시지 처리를 통제**: QoS 정책을 통해 active 상태가 아니면 메시지를 수신하지 않도록 설정할 수 있어, 노드 로직이 복잡해지는 것을 방지
3. **신뢰성 있는 시스템 구성**: Reliable + Transient Local처럼 강력한 전달 보장 설정을 라이프사이클에 맞춰 적용할 수 있어, 중요한 데이터를 놓치지 않음

아래는 라이프사이클 노드를 C++로 구현하여 QoS 설정을 적용하는 예시 코드이다.

```cpp
#include <rclcpp/rclcpp.hpp>
#include <rclcpp_lifecycle/lifecycle_node.hpp>
#include <std_msgs/msg/string.hpp>

using rclcpp_lifecycle::LifecycleNode;
using rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface;

class MyLifecycleNode : public LifecycleNode
{
public:
  explicit MyLifecycleNode(const std::string & node_name)
  : LifecycleNode(node_name)
  {
    // QoS 설정 예시
    rclcpp::QoS qos_profile(rclcpp::KeepLast(10));
    qos_profile.reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE);
    qos_profile.durability(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL);

    // 퍼블리셔 초기화는 on_configure에서 할 예정이므로 포인터만 선언해 둔다.
    publisher_ = nullptr;
  }

protected:
  LifecycleNodeInterface::CallbackReturn on_configure(const rclcpp_lifecycle::State &)
  {
    RCLCPP_INFO(get_logger(), "on_configure() 호출");

    // 여기서 퍼블리셔 초기화
    rclcpp::QoS qos_profile(rclcpp::KeepLast(10));
    qos_profile.reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE);
    qos_profile.durability(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL);

    publisher_ = this->create_publisher<std_msgs::msg::String>("lifecycle_chatter", qos_profile);

    return LifecycleNodeInterface::CallbackReturn::SUCCESS;
  }

  LifecycleNodeInterface::CallbackReturn on_activate(const rclcpp_lifecycle::State &)
  {
    RCLCPP_INFO(get_logger(), "on_activate() 호출");
    // 퍼블리셔 활성화
    publisher_->on_activate();
    return LifecycleNodeInterface::CallbackReturn::SUCCESS;
  }

  LifecycleNodeInterface::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &)
  {
    RCLCPP_INFO(get_logger(), "on_deactivate() 호출");
    // 퍼블리셔 비활성화
    publisher_->on_deactivate();
    return LifecycleNodeInterface::CallbackReturn::SUCCESS;
  }

  LifecycleNodeInterface::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &)
  {
    RCLCPP_INFO(get_logger(), "on_cleanup() 호출");
    // 퍼블리셔 자원 해제
    publisher_.reset();
    return LifecycleNodeInterface::CallbackReturn::SUCCESS;
  }

private:
  rclcpp_lifecycle::LifecyclePublisher<std_msgs::msg::String>::SharedPtr publisher_;
};

int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);

  auto lc_node = std::make_shared<MyLifecycleNode>("my_lifecycle_node");
  rclcpp::executors::SingleThreadedExecutor exe;

  exe.add_node(lc_node->get_node_base_interface());
  exe.spin();

  rclcpp::shutdown();
  return 0;
}
```

위 코드에서 보듯이, `on_configure` 함수 내에서 퍼블리셔의 QoS를 설정하고 생성한다. 이는 라이프사이클 노드가 `inactive` 상태가 된 후, `on_activate`를 통해 `active` 상태가 되면 메시지를 송신할 수 있도록 준비해 두는 과정이다.

#### 라이프사이클 노드 진입과 QoS 조합 시나리오

라이프사이클 노드와 QoS 설정을 조합할 때는 상황에 따라 다음과 같은 시나리오가 발생할 수 있다.

1. 초기 unconfigured 상태에서 파라미터(예: 통신 신뢰도, 메시지 유지 여부 등) 확인
   * 파라미터 서버를 통해 노드가 사용할 QoS 프로파일에 대한 설정값(예: Reliability = Reliable)을 받은 후, `on_configure`에서 이를 적용
2. inactive 상태에서 QoS 프로파일 재설정
   * 사용자가 동적으로 QoS를 변경해야 하는 경우, `on_deactivate` -> `on_cleanup` -> `on_configure` 단계를 통해서 퍼블리셔를 새로 구성
3. active 상태 진입 전, 네트워크 환경 점검
   * `on_configure` 단계에서 네트워크 환경(대역폭, 지연)을 측정 후, Best Effort와 Reliable을 비교하여 필요 시 QoS를 수정

이러한 시나리오를 통해 라이프사이클 노드와 QoS 설정이 유기적으로 동작함으로써, 시스템 요구 사항에 부합하는 동적인 통신 구성을 구축할 수 있다.

#### 라이프사이클 매니저(Lifecycle Manager)와 QoS 동적 제어

라이프사이클 노드를 실제로 운용할 때는, 각 라이프사이클 노드를 직접 호출하면서 상태 전이를 수동으로 제어해야 하는 경우가 많다. 이때 ROS2에서 제공하는 라이프사이클 매니저(Lifecycle Manager)나 유사한 역할을 수행하는 별도의 관리 노드를 사용하면, 여러 라이프사이클 노드의 상태 전이를 한곳에서 일괄적으로 관리하면서 QoS도 동적으로 조절할 수 있다.

예를 들어, 다음과 같은 시나리오를 생각해 보자.

1. 센서 노드(라이프사이클 노드)
   * 카메라나 LiDAR 센서 등에서 데이터를 읽어오는 라이프사이클 노드
   * 초기에는 unconfigured 상태에서 시작
   * `on_configure`에서 센서를 초기화하고, 특정 QoS(예: Reliable, Keep Last 10)를 적용한 퍼블리셔를 생성
2. 제어 노드(라이프사이클 노드)
   * 센서 노드에서 넘어오는 데이터를 받아서 로봇의 제어 신호를 계산
   * 초기에는 unconfigured 상태에서 시작
   * `on_configure`에서 서브스크라이버와 퍼블리셔를 설정하되, 센서 노드의 QoS에 맞춰서 subscription QoS를 설정
3. 라이프사이클 매니저 노드
   * 두 노드 모두에 대해 상태 전이를 제어
   * `on_configure` -> `on_activate` 순서대로 호출하여, 센서 -> 제어 노드 순으로 활성화
   * 만약 동작 도중 센서 노드의 QoS를 변경해야 한다면, 라이프사이클 매니저가 센서 노드를 deactivate -> cleanup -> configure -> activate 순으로 돌려서 새 QoS를 반영

이때 라이프사이클 매니저가 내부적으로 다음과 같은 알고리즘을 수행할 수 있다.

* **노드 상태 감시**: 라이프사이클 매니저는 타이머나 콜백으로 각 라이프사이클 노드의 상태를 주기적으로 확인한다. 그리고 모든 노드가 정상적으로 inactive 상태에 도달했는지, active 상태에 도달했는지 등을 체크한다.
* **QoS 업데이트 전략**: 특정 노드(예: 센서 노드)에서 QoS를 변경해야 한다면, 다음과 같은 순서를 밟는다.
  1. 해당 노드를 deactivate (active -> inactive)
  2. 필요한 경우 cleanup (inactive -> unconfigured)
  3. 파라미터 업데이트 (unconfigured에서 새로운 QoS 파라미터 반영)
  4. configure (unconfigured -> inactive)
  5. activate (inactive -> active)
* **상태 전이 시 타이밍 제어**: 센서 노드가 활성화되기 전에 제어 노드가 활성화되지 않도록 순서를 보장해 주어야 하며, 반대로 제어 노드를 먼저 비활성화한 뒤에 센서 노드를 비활성화하는 식으로 안정적 정지 순서를 구성할 수도 있다.

#### QoS 동적 파라미터 예시

라이프사이클 노드에서 QoS를 동적으로 제어하기 위해서는, ROS2 파라미터 서버(ROS2 Parameter Server)나 자체적인 설정 파일을 통해 아래와 같은 값을 주고받을 수 있다.

* `$reliability$`: `"reliable"` 혹은 `"best_effort"`
* `$durability$`: `"volatile"` 혹은 `"transient_local"`
* `$history$`: `"keep_last"` 혹은 `"keep_all"`
* `$depth$`: 토픽 버퍼의 크기 (예: 10, 100)

예를 들어, 센서 노드가 on\_configure에서 다음과 같은 파라미터를 참조한다고 하자.

```cpp
auto reliability = this->get_parameter("sensor_qos.reliability").as_string();
auto durability = this->get_parameter("sensor_qos.durability").as_string();
auto depth = this->get_parameter("sensor_qos.depth").as_int();
```

이런 식으로 얻은 파라미터에 따라, 코드 내부에서 실제 `rclcpp::QoS` 객체를 생성할 때 동적으로 값을 설정할 수 있다.

#### QoS 정책과 라이프사이클 노드의 상호 영향

QoS 정책은 기본적으로 퍼블리셔와 서브스크라이버 간의 통신 계약(Contract)이다. 라이프사이클 노드가 active 상태로 전이된다고 해서 QoS가 자동으로 변경되지는 않는다. 그러나 라이프사이클 노드가 inactive에서 active로 전환될 때, 미리 설정해 둔 QoS가 적용된 퍼블리셔나 서브스크라이버가 실제 송수신을 시작하게 된다.

* **센서 노드의 Reliable QoS와 제어 노드의 Best Effort QoS가 충돌**
  * 센서 노드가 on\_configure에서 Reliable로 설정해 두었는데, 제어 노드가 on\_configure에서 Best Effort로 설정했다면, 서로 다른 QoS 프로파일 간의 호환성(Compatibility)이 떨어져서 연결(connection)이 제대로 이루어지지 않을 수 있다.
  * 이런 문제를 사전에 방지하기 위해, 라이프사이클 매니저가 상태 전이 이전에 QoS Compatibility를 미리 검사하거나, 파라미터 검증을 수행할 수 있다.
* **Transient Local과 Volatile의 메시지 유효성**
  * 센서 노드가 Transient Local로 설정되어 있으면, 노드가 active 상태에 있지 않더라도, 메시지가 퍼블리셔 버퍼(히스토리)에 보존된다.
  * 반면에, 제어 노드가 Volatile로 설정되어 있으면, 제어 노드가 active 상태로 전이되기 전에 보내진 메시지는 수신하지 못한다.
  * 이러한 차이로 인해, 시스템이 복잡해지지 않도록 명확하게 의도를 설정해야 한다.

#### 실제 활용 시 주의점

* **에러 핸들링**: 라이프사이클 노드가 on\_configure 중간에 실패(FAILURE)를 반환하거나, on\_activate 도중 예외가 발생한다면 해당 노드는 이전 상태로 되돌아가야 한다. 이때 QoS 설정도 제대로 해제하거나 재할당해야 한다.
* **시간 동기화 이슈**: 센서 노드가 QoS를 Reliable로 설정하면, 네트워크 부하나 지연이 늘어날 수 있다. 이를 고려하여 on\_configure 단계에서 메시지 주기, 버퍼 크기, 타이머 설정 등을 함께 조정해야 한다.
* **사용자 정의 콜백 내 복잡도**: 라이프사이클 노드 콜백(예: on\_configure, on\_activate 등) 내부가 복잡해지면, 노드 상태 전이 시간이 길어질 수 있다. QoS 매개변수나 하드웨어 리소스 초기화 과정을 효율적으로 구성해야 한다.

#### 라이프사이클 노드 테스팅 및 디버깅 전략

라이프사이클 노드는 상태 전이와 콜백이 복잡해질 수 있으므로, 체계적인 테스팅과 디버깅 전략이 필요하다. 특히 QoS 설정이 상황에 따라 달라지는 경우, 노드가 원치 않는 상태에서 메시지를 송수신하는지 여부를 면밀히 확인해야 한다.

1. **단위 테스트(Unit Test)로 콜백 함수 검증**
   * on\_configure, on\_activate 등 주요 콜백 함수별로 독립적인 단위 테스트를 작성하여, 리턴값이 예상대로 나오는지 확인
   * 예: on\_configure가 SUCCESS를 반환할 때 퍼블리셔/서브스크라이버가 정상적으로 생성되었는지 확인
2. **통합 테스트(Integration Test)에서 라이프사이클 매니저와의 연동 확인**
   * 라이프사이클 매니저가 여러 노드의 상태 전이를 제어할 때, 순서가 의도대로 진행되는지 (예: 센서 노드가 먼저 활성화 -> 제어 노드 활성화) 검사
   * QoS 설정 변경을 위한 deactivate -> cleanup -> configure -> activate 과정을 스크립트 혹은 테스트 노드로 검증
3. **rqt\_lifecycle와 같은 시각화 툴 활용**
   * ROS2 Foxy 이후부터 사용 가능한 rqt\_lifecycle(플러그인 설치 필요) 등으로, 라이프사이클 노드의 현재 상태와 상태 전이 이벤트를 모니터링
   * QoS 매개변수가 바뀌었을 때 어떤 상태 전이를 거쳤는지 직관적으로 확인 가능
4. **QoS 호환성(Compatibility) 검사 스크립트**
   * 퍼블리셔와 서브스크라이버의 QoS가 호환되지 않아 메시지가 오가지 않는 경우를 찾기 위해, ROS2 CLI 도구나 별도 스크립트를 통해 확인
   * 예: `ros2 topic info -v <토픽이름>` 명령으로 현재 연결된 노드들의 QoS 정보를 직접 확인
5. **성능 로깅과 이벤트 트레이싱**
   * on\_configure, on\_activate 시점에서 CPU, 메모리 사용량과 네트워크 사용량을 로깅해 두면, QoS 변경에 따른 부하 증가를 사전에 파악할 수 있음
   * LTTng, eCAL 등의 이벤트 트레이싱 툴을 사용하면, 라이프사이클 콜백 별로 실제 소요 시간을 측정하고 병목 지점을 식별 가능

#### 라이프사이클 노드와 QoS 튜닝을 위한 수학적 고려

QoS 설정(예: Reliable, Transient Local)으로 인해 네트워크 지연이나 버퍼 오버플로가 발생할 수 있으므로, 시스템이 요구하는 전송율(throughput), 지연(latency), 데이터 손실률(loss rate)을 수치적으로 계산하여 판단하는 것이 좋다.

예를 들어, 퍼블리셔가 초당 $r$ Hz로 메시지를 발행하고, 메시지 하나의 크기가 $s$ bytes라면, 필요한 네트워크 대역폭은

$$
B = r \times s \quad(\text{bytes/second})
$$

이다. 네트워크의 실효 대역폭 $C$가 이보다 충분히 커야 Reliable 전송을 안정적으로 수행할 수 있다. 만약 $B$가 $C$에 근접하거나 초과한다면, Best Effort로 설정하거나 메시지 크기를 축소해야 한다.

또한, 메시지 전달 성공률 $P$를 측정한다면, Reliable 전송에서 기대하는 $P$가 충분히 높아야 실제 시스템 요구사항을 만족할 수 있다. 만약 Best Effort로 전환하면 이론적으로 $P$가 떨어질 수 있으니, 이에 대한 트레이드오프(trade-off) 분석이 필요하다.

#### 라이프사이클 노드와 애플리케이션 레벨 파라미터 연동

라이프사이클 노드는 QoS 설정 외에도 애플리케이션 레벨 파라미터(예: 센서 읽기 주기, 제어 주기 등)를 함께 관리하는 경우가 많다. 예를 들어, 아래와 같은 ROS2 파라미터가 있다고 하자.

* `$sensor_freq$`: 센서 읽기 주기 (Hz)
* `$control_freq$`: 제어 주기 (Hz)

이를 라이프사이클 노드의 on\_configure에서 다음처럼 초기화해 줄 수 있다.

```cpp
LifecycleNodeInterface::CallbackReturn on_configure(const rclcpp_lifecycle::State &)
{
  sensor_freq_ = this->get_parameter("sensor_freq").as_double();
  control_freq_ = this->get_parameter("control_freq").as_double();

  if(sensor_freq_ <= 0.0 || control_freq_ <= 0.0) {
    RCLCPP_ERROR(get_logger(), "주기는 양의 값이어야 한다.");
    return LifecycleNodeInterface::CallbackReturn::FAILURE;
  }

  // 퍼블리셔/서브스크라이버 초기화
  // QoS 설정 및 기타 리소스 할당
  return LifecycleNodeInterface::CallbackReturn::SUCCESS;
}
```

이처럼 라이프사이클 노드에서 파라미터를 로드하거나 검증한 뒤, QoS 설정과 함께 애플리케이션 로직에 반영할 수 있다.

#### 복수 토픽 QoS 동시 제어

하나의 노드에서 여러 퍼블리셔/서브스크라이버를 등록하고, 각각 다른 QoS 프로파일을 적용할 수도 있다. 이 경우 라이프사이클 노드의 on\_configure, on\_cleanup 등 콜백에서 모든 퍼블리셔/서브스크라이버에 대해 다음과 같은 처리를 진행해야 한다.

1. **on\_configure**: 모든 퍼블리셔/서브스크라이버를 생성. 각 토픽마다 파라미터나 사양에 맞춰 QoS 설정
2. **on\_activate**: 모든 퍼블리셔/서브스크라이버의 on\_activate 호출 (publisher\_->on\_activate())
3. **on\_deactivate**: 모든 퍼블리셔/서브스크라이버의 on\_deactivate 호출
4. **on\_cleanup**: 필요 시 퍼블리셔/서브스크라이버를 reset()하여 자원 해제

예를 들어, 이미지 토픽은 대역폭이 크고, 딜레이가 있어도 반드시 모든 프레임을 받아야 한다면 Reliable, KeepLast(5)와 같은 설정을 쓸 수 있다. 반면 로깅용 텍스트 토픽은 조금 유실돼도 괜찮다면 Best Effort, KeepLast(1) 같은 설정을 적용 가능하다. 이때도 라이프사이클 노드가 inactive 상태일 때 QoS를 변경하면, active가 되기 전에 변경 사항을 모두 적용할 수 있다.

#### 메시지 필터링(Message Filtering)과 QoS

라이프사이클 노드가 여러 종류의 QoS를 다루거나, 한 노드에서 여러 토픽을 수신하여 융합(fusion)을 수행해야 할 때, 메시지 필터링(Message Filtering) 기법이 유용하다. 예를 들어, 다음과 같은 경우를 가정해 보자.

* LiDAR 토픽: Reliable, KeepLast(10)
* IMU 토픽: Best Effort, KeepLast(50)

두 센서의 데이터 레이트와 QoS 특성이 달라서, 특정 시점에 정합(동기화)되지 않을 수 있다. 이를 해결하기 위해 $t\_i$ 시점에서 LiDAR 메시지가 수신되면, 그 시점 근방의 IMU 데이터를 모아서 프로세싱할 수 있도록 필터링 로직을 구현해야 한다. 라이프사이클 노드가 active로 전환되기 전에는 이러한 필터링 로직도 동작하지 않도록 구성하여, 잘못된 초기 데이터 처리를 방지할 수 있다.

#### 배포(Deployment) 시 시나리오

라이프사이클 노드와 QoS 설정을 최종 배포할 때, 프로세스가 다음과 같이 구성될 수 있다.

1. ROS2 파라미터 서버 또는 YAML 파일로 QoS 설정값 저장
   * 예: sensor\_qos.yaml 파일에 Reliability, Durability, Depth 정보 기록
2. 런치 파일(Launch File)에서 파라미터를 로딩
   * `ros2 launch my_lifecycle_bringup.launch.py` 시 실행 시점에 각 노드에 파라미터를 주입
3. 라이프사이클 매니저 노드 실행
   * 센서 노드와 제어 노드를 순차적으로 configure -> activate
   * QoS가 충돌하거나 파라미터가 유효하지 않으면 예외 처리
4. 운영 중 QoS 재설정 필요 시
   * 라이프사이클 매니저가 특정 노드를 deactivate -> cleanup -> configure -> activate 하는 절차를 수행
   * 파라미터 변경을 적용하여 네트워크 트래픽이나 신뢰성 요구가 달라진 환경에 대처

이와 같은 배포 단계에서, QoS 설정이 올바르게 되었는지(예: Reliable와 Reliable, 혹은 Best Effort와 Best Effort의 호환성)는 사전에 스크립트나 ros2 CLI 툴을 통해 검증해야 한다.

#### QoS의 Deadline, Lifespan, Liveliness와 라이프사이클 노드

ROS2 QoS 정책에는 Reliability, Durability, History 외에도 Deadline, Lifespan, Liveliness 같은 세부 옵션이 존재한다. 라이프사이클 노드 구조와 결합하면, 노드 활성(activate) 시점에 이들 QoS 설정이 실제로 적용되고, 비활성(deactivate) 시점에는 해당 QoS 요구사항이 중단된다. 주요 개념은 다음과 같다.

1. **Deadline**
   * 퍼블리셔가 일정 주기($d$) 내에 새 메시지를 보내지 못하거나, 서브스크라이버가 그 주기 내에 메시지를 받지 못하면 “Deadline Missed” 이벤트가 발생한다.
   * 라이프사이클 노드가 active 상태에서만 Deadline을 엄격히 적용하도록 설계할 수 있다. 즉, inactive 상태에서는 메시지를 아예 보내지 않으므로 Deadline 체크를 비활성화한다.
2. **Lifespan**
   * 퍼블리셔가 보낸 메시지가 일정 시간($l$)이 지난 후에 무효화(버퍼에서 제거)되는 옵션이다.
   * 라이프사이클 노드가 active 상태일 때만 메시지 유효 기간을 관리하도록 설계하면, inactive 상태에서는 메시지를 발행하지 않으므로 Lifespan 관련 오버헤드가 발생하지 않는다.
3. **Liveliness**
   * 노드 혹은 토픽이 “정상적으로 살아있다(alive)”는 것을 보장하기 위한 옵션으로, 특정 주기 내에 Heartbeat를 보내야 하며 이를 받지 못하면 “Liveliness Lost” 이벤트가 발생한다.
   * 라이프사이클 노드가 active 상태에 있을 때만 Heartbeat를 보내도록 설정해, unconfigured 또는 inactive 상태에서는 Liveliness 체크가 발생하지 않도록 구성 가능하다.

이러한 세부 QoS 옵션을 활용해 다음과 같은 시나리오를 구성할 수 있다.

* **하드 실시간(Hard Real-time) 시스템**: Deadline 옵션을 짧게 두고, 라이프사이클 노드가 active 상태에서 이를 항상 준수하도록 설정
* **로그 기록용 토픽**: Lifespan을 짧게 두어, 오래된 데이터가 버퍼를 차지하지 않도록 함
* **주기적 상태 점검**: Liveliness를 활용해 노드가 active 상태일 때만 주기적으로 존재(Heartbeat)를 알리도록 설정

#### 실시간(Real-time) 고려사항

ROS2는 실시간성을 어느 정도 지원할 수 있도록 설계되었지만, 완전한 하드 실시간 보장은 별도의 RTOS(Real-Time Operating System) 환경이나 커스텀 스케줄링이 필요하다. 라이프사이클 노드와 QoS를 실시간 관점에서 사용할 때 주의해야 할 점은 다음과 같다.

1. **스레드 우선순위와 실행기(Executor)**
   * 실시간 스레드 우선순위를 부여하기 위해 RT 커널이 필요할 수 있으며, `rclcpp::Executor`를 커스텀 실행기로 구성할 수도 있다.
   * 라이프사이클 콜백(`on_configure`, `on_activate` 등)은 짧은 시간 내에 수행될 수 있도록 설계해야 한다.
2. **메모리 할당 최소화**
   * 힙 메모리 동적 할당은 실시간 시스템에서 예측 불가능성을 초래하기 쉽다.
   * 퍼블리셔, 서브스크라이버를 라이프사이클 노드가 active로 전환할 때마다 새로 동적 할당하기보다는, 미리 할당해 두고 `on_activate` 시에만 참조를 활성화하는 방식을 고려할 수 있다.
3. **QoS로 인한 지연(latency) 관리**
   * Reliable 전송은 네트워크 재전송, ACK/NAK 등이 발생하므로 지연 시간이 늘어날 수 있다.
   * 실시간성 요구가 높은 시스템에서는 오히려 Best Effort를 쓰거나, Deadline을 활용해 지연이 발생하면 명시적으로 에러 처리하는 방식을 택할 수도 있다.

#### 고급 상태 전이(Advanced Transitions) 활용

ROS2 라이프사이클 노드 모델을 보면, Failure나 Error 상태가 도식으로 정의되어 있다. 즉, `on_configure`나 `on_activate` 등에서 실패(FAILURE)를 리턴하면, 자동으로 원래 상태로 돌아가거나 Error 상태로 전이될 수 있다. 이 과정을 잘 활용하면 QoS 충돌 또는 리소스 부족 문제 발생 시 바로 에러 상태로 돌려서, 시스템이 잘못된 상태에서 동작하는 것을 방지할 수 있다.

* `on_configure`에서 QoS 충돌 발견 시
  * 예: 퍼블리셔는 Reliable를 요구하지만, 현재 네트워크가 Best Effort만 지원하는 상황
  * 이 경우 센서 노드 `on_configure` 콜백에서 FAILURE를 반환하고, Error 상태로 전환한 뒤 관리자가 Error를 처리
* Error 상태에서 복구(Cleanup) 시나리오
  * Error 상태에서 `on_cleanup`을 호출해, 이미 생성된 퍼블리셔/서브스크라이버를 모두 해제하고, 다시 unconfigured 상태로 되돌아갈 수 있음
  * 이후 사용자가 QoS 설정을 수정하거나 네트워크 문제를 해결한 뒤, configure -> activate 진행

#### 멀티 로봇 시나리오에서의 라이프사이클 노드와 QoS

여러 대의 로봇이 동시에 운영되며, 서로 다른 네트워크 대역폭과 서로 다른 센서 스펙을 사용할 수 있다. 이 경우 라이프사이클 노드와 QoS 설정은 더욱 복잡해질 수 있다.

1. **각 로봇별 라이프사이클 매니저**
   * 로봇마다 독립된 라이프사이클 매니저를 두고, 센서 노드, 제어 노드, 경로 계획 노드 등을 관리
   * 네트워크 환경(예: 무선 연결) 변화에 따라, QoS를 각 로봇별로 다르게 조정
2. **중앙 매니저가 모든 로봇 노드 상태 제어**
   * 중앙 서버에서 각 로봇 노드들의 라이프사이클 상태를 모니터링
   * QoS 충돌이 일어나거나, 특정 노드가 메시지를 누락(Deadline Missed)하는 상황이 발생하면 해당 노드를 deactivate 시킨 뒤, 파라미터를 수정해 다시 activate
3. **대역폭 제약 시 동적 QoS 다운그레이드**
   * 여러 로봇이 동시에 고화질 카메라 스트리밍을 Reliable로 전송하면 네트워크가 과부하 상태가 될 수 있음
   * 이때 중앙 매니저가 Best Effort로 일시 전환(라이프사이클 노드의 deactivate -> cleanup -> configure -> activate)해서 임시로 트래픽을 줄이는 전략

이와 같은 멀티 로봇 시나리오에서는 라이프사이클 노드와 QoS를 적절히 조합해 네트워크 및 자원을 효율적으로 할당해야 한다.

#### 라이프사이클 노드와 보안(Security)

ROS2에서는 DDS(Dataplicity Distribution Service) 보안 계층이나 SROS2를 통해 보안을 지원한다. QoS 설정과 라이프사이클 노드 관점에서 다음과 같은 지점이 중요하다.

1. active 상태에서만 인증(Handshake) 수행
   * 노드가 unconfigured 상태일 때는 DDS 보안 핸드셰이크를 수행하지 않고, on\_configure 이후에 보안 세션을 맺도록 설계 가능
2. QoS 정책과 보안 레벨 매칭
   * 보안 수준(예: 암호화, 인증)이 높아질수록 메시지 크기가 커지거나 지연이 증가하기 쉽다. Reliable 전송과 중복되는 오버헤드가 없도록 균형을 맞춰야 한다.
3. 보안 정책 동적 변경 시
   * 노드 보안 정책을 변경하려면 라이프사이클 전이를 통해 기존 보안 세션을 종료하고, 다시 수립할 필요가 있을 수 있다.

이처럼 라이프사이클 노드는 보안 기능과도 긴밀하게 연관될 수 있으며, 시스템 요구 사항에 따라 `on_configure` 단계에서 보안 설정을 초기화하고, `on_activate` 시점에 실제 통신을 시작하도록 구성할 수 있다.

#### 예제: 이미지 처리 파이프라인에서의 라이프사이클과 QoS

실제 예시로, 카메라 -> 이미지 프로세서 -> AI 모델 추론 -> 제어 노드로 이어지는 파이프라인을 생각해 보자.

1. **카메라 노드(라이프사이클)**
   * `on_configure`에서 카메라 드라이버 초기화 및 퍼블리셔 QoS 설정
   * 고속 프레임 전송이 필요하므로 Best Effort, KeepLast(10) 사용
   * `on_activate`가 되면 실시간으로 이미지 전송
2. **이미지 프로세서 노드(라이프사이클)**
   * 카메라 이미지를 받아 전처리(해상도 축소, 노이즈 필터링)
   * QoS는 카메라 노드와 호환되도록 Best Effort, KeepLast(10) 사용
   * `on_configure`에서 GPU 리소스나 OpenCV 세팅, `on_activate`에서 프로세싱 시작
3. **AI 모델 추론 노드(라이프사이클)**
   * 전처리된 이미지를 받아 추론 실행
   * 중요 데이터이므로 Reliable, KeepLast(1) 사용 (최신 프레임만 필요)
   * `on_activate`에서 AI 엔진 로드, 추론 스레드 시작
4. **제어 노드(라이프사이클)**
   * 추론 결과를 받아서 로봇의 동작 제어 신호를 생성
   * QoS는 Reliable, TransientLocal로 설정해 과거 추론 결과도 잠깐 보존 가능
   * `on_activate`에서 제어 루프 시작

이 파이프라인을 라이프사이클 매니저가 순차적으로 configure -> activate 하면서, QoS 호환성을 점검하고 필요한 리소스를 올바른 시점에 할당해 줄 수 있다. 예를 들어 이미지 프로세서 노드가 configure되기 전에 AI 모델 추론 노드가 활성화되지 않도록 제어할 수 있다.

#### QoS 이벤트(Events)와 라이프사이클 노드

ROS2에서는 QoS 정책에 의해 발생하는 이벤트(예: Deadline Missed, Liveliness Lost 등)를 처리할 수 있는 인터페이스를 제공한다. 이를 통해 라이프사이클 노드가 active 상태에서만 이벤트를 모니터링하거나, 이벤트 발생 시 적절히 deactivate 상태로 전환하여 에러 처리를 수행할 수 있다.

* **Deadline Missed**
  * 퍼블리셔/서브스크라이버가 설정된 Deadline 기간 내에 메시지 교환을 하지 못했을 때 발생
  * 라이프사이클 노드의 콜백 함수(예: `on_deadline_missed`)를 만들어, Deadline Missed 이벤트가 여러 번 누적되면 노드를 deactivate 상태로 전환하고, 네트워크 상태나 메시지 주기를 재설정하는 로직을 구현할 수 있다.
* **Liveliness Lost** / **Liveliness Changed**
  * 퍼블리셔가 일정 주기로 Heartbeat를 보내지 못하거나, 서브스크라이버 입장에서 특정 퍼블리셔의 Liveliness가 끊긴 경우에 발생
  * 라이프사이클 매니저가 이를 감지해 노드를 deactivate하고, 인프라 점검(네트워크 불안정, 노드 다운 등)을 수행한 뒤 다시 configure -> activate를 시도할 수 있다.
* **Incompatible QoS**
  * 퍼블리셔/서브스크라이버 간 QoS가 맞지 않아 실제로 메시지가 전달되지 않을 때 발생
  * 라이프사이클 노드가 configure 단계에서 QoS 호환성 검사를 미리 수행할 수도 있지만, 런타임 중에 새로운 노드가 등장해 충돌할 수도 있으므로 이벤트 기반 모니터링이 필요하다.

이와 같은 이벤트들은 `rclcpp::SubscriptionEvents`, `rclcpp::PublisherEvents` 등의 클래스를 이용해 콜백 등록이 가능하며, 라이프사이클 노드의 active 상태에서만 이벤트를 감지하도록 설계하면 불필요한 로깅이나 예외 처리를 줄일 수 있다.

```cpp
#include <rclcpp/rclcpp.hpp>
#include <rclcpp/qos_event.hpp>
#include <rclcpp_lifecycle/lifecycle_node.hpp>
#include <std_msgs/msg/string.hpp>

using rclcpp_lifecycle::LifecycleNode;
using rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface;
using DeadlineEvent = rclcpp::QOSDeadlineOfferedInfo;

class LifecycleWithQosEventNode : public LifecycleNode
{
public:
  explicit LifecycleWithQosEventNode(const std::string & node_name)
  : LifecycleNode(node_name)
  {}

protected:
  LifecycleNodeInterface::CallbackReturn on_configure(const rclcpp_lifecycle::State &)
  {
    RCLCPP_INFO(get_logger(), "on_configure() called");

    // QoS 예시
    rclcpp::QoS qos_profile(rclcpp::KeepLast(10));
    qos_profile.deadline(rclcpp::Duration(1, 0)); // 1초 Deadline
    publisher_ = this->create_publisher<std_msgs::msg::String>("topic_with_deadline", qos_profile);

    // Deadline Missed 이벤트 콜백 등록
    auto callback = [this](DeadlineEvent info) {
      RCLCPP_WARN(this->get_logger(), 
                  "Deadline Missed. total_count: %d", 
                  info.total_count);
      // 상황에 따라 deactivate 시도 가능
      // this->deactivate();
    };
    event_handler_ = publisher_->add_event_handler<rclcpp::QOSEventType::DeadlineOffered>(callback);

    return LifecycleNodeInterface::CallbackReturn::SUCCESS;
  }

  LifecycleNodeInterface::CallbackReturn on_activate(const rclcpp_lifecycle::State &)
  {
    RCLCPP_INFO(get_logger(), "on_activate() called");
    publisher_->on_activate();
    return LifecycleNodeInterface::CallbackReturn::SUCCESS;
  }

  LifecycleNodeInterface::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &)
  {
    RCLCPP_INFO(get_logger(), "on_deactivate() called");
    publisher_->on_deactivate();
    return LifecycleNodeInterface::CallbackReturn::SUCCESS;
  }

private:
  rclcpp_lifecycle::LifecyclePublisher<std_msgs::msg::String>::SharedPtr publisher_;
  rclcpp::EventHandlerBase::SharedPtr event_handler_;
};
```

이 예시에서는 Deadline Missed 이벤트를 잡아내고, 필요한 경우 노드를 deactivate할 수 있도록 여지를 남겼다. 다른 이벤트(LivelinessLost, IncompatibleQoS 등)도 유사한 방식으로 핸들러를 등록할 수 있다.

#### 라이프사이클 노드와 Launch 시스템의 연동

ROS2 Launch 시스템(파이썬 기반 스크립트)에서 라이프사이클 노드와 QoS 설정을 선언적으로 관리할 수 있다. 예를 들어, 다음과 같은 `launch` 스크립트를 통해 노드에 파라미터를 넘기고, 상태 전이를 자동화할 수 있다.

```python
import launch
from launch import LaunchDescription
from launch_ros.actions import LifecycleNode
from launch.actions import EmitEvent
from launch_ros.event_handlers import OnStateTransition
from launch_ros.events.lifecycle import ChangeState
from launch_ros.lifecycle import NodeState

def generate_launch_description():
    lifecycle_node = LifecycleNode(
        package='my_lifecycle_pkg',
        executable='my_lifecycle_exe',
        name='my_lifecycle_node',
        parameters=[{
            'sensor_qos.reliability': 'reliable',
            'sensor_qos.durability': 'transient_local',
            'sensor_qos.depth': 10
        }],
        output='screen'
    )

    # 노드가 unconfigured -> inactive가 되면 active로 전이 이벤트 발생
    activate_event = EmitEvent(
        event=ChangeState(
            lifecycle_node_matcher=lambda node: node == lifecycle_node,
            transition_id=1  # configure -> activate는 1번 전이(실제 ID는 구현에 따라 다를 수 있음)
        )
    )

    return LaunchDescription([
        lifecycle_node,
        OnStateTransition(
            target_lifecycle_node=lifecycle_node,
            start_state=NodeState.UNCONFIGURED,
            goal_state=NodeState.INACTIVE,
            actions=[activate_event]
        ),
    ])
```

* **LifecycleNode** 액션을 사용해, 노드를 라이프사이클 노드로 띄울 수 있다.
* 파라미터를 YAML이나 딕셔너리 형태로 넘겨주어 QoS 프로필 등을 설정 가능하다.
* `OnStateTransition`과 `EmitEvent(ChangeState)`를 이용하면 특정 상태 전이가 완료된 뒤 다음 전이를 발동해, 자동으로 활성화를 이어갈 수 있다.

#### 강건성(Robustness)을 위한 상태 전이 실패 처리

실제 시스템에서는 on\_configure나 on\_activate 과정에서 예상치 못한 오류가 발생할 수 있다. 예를 들어, 아래와 같은 상황을 고려해 보자.

* 센서 드라이버가 하드웨어와의 연결에 실패해 on\_configure 콜백이 `FAILURE`를 반환
* QoS 파라미터가 잘못되어 `create_publisher`에서 예외가 발생

이때 라이프사이클 노드의 상태 전이는 자동으로 `unconfigured` 상태로 되돌아가거나, Error 상태로 전이된다. 라이프사이클 매니저 혹은 Launch 스크립트는 이러한 상태 변화를 감지해 다음과 같은 복구 절차를 수행할 수 있다.

1. 에러 로그를 확인한 뒤, 파라미터(예: QoS.depth)를 수정
2. 노드를 Cleanup -> unconfigured 상태로 완전히 되돌림
3. configure -> activate 전이를 재시도

또한, ROS2 파라미터 이벤트(PARAMETER\_EVENT)나 서비스 콜을 통해, 노드 실행 중에 파라미터를 수정하고 재설정한 뒤 configure/activate 과정을 다시 밟도록 설계할 수도 있다.

#### LifecycleNode vs. 단순 노드(Simple Node) 비교

일반 노드와 라이프사이클 노드의 차이를 요약하면 다음과 같다.

| 항목            | 일반 노드                       | 라이프사이클 노드                                          |
| ------------- | --------------------------- | -------------------------------------------------- |
| **상태**        | 초기에 곧바로 활성화됨                | unconfigured, inactive, active, finalized 등 명시적 상태 |
| **상태 전이**     | 없음(별도 로직으로 직접 구현)           | on\_configure, on\_activate 등의 콜백으로 자동 관리          |
| **QoS 설정 변경** | 런타임 수정 시 재생성 필요, 명확한 로드맵 부족 | inactive 상태에서만 재설정 가능, 활성화 시 QoS가 확정               |
| **자원 관리**     | 노드 실행과 함께 즉시 모든 자원 할당       | configure, cleanup 등 상태별로 자원 동적 할당/해제 가능           |
| **주요 활용**     | 간단한 토픽 송수신, 데모, 테스트 등       | 실제 운영 환경에서 정확한 초기화/종료, 안정성 요구                      |

라이프사이클 노드는 구조가 복잡해지는 단점이 있으나, 장기적으로 운영되는 시스템에서 높은 안정성과 유연성을 제공한다는 점에서 장점이 크다.

#### 라이프사이클 노드와 시뮬레이션

Gazebo, Webots 등 ROS2 시뮬레이션 환경에서도 라이프사이클 노드를 이용할 수 있다. 특히, 실제 하드웨어 센서 대신 시뮬레이션 플러그인을 사용하는 경우에도 다음과 같은 순서로 전이가 이뤄진다.

1. **on\_configure**: 가상 센서 플러그인(카메라, LiDAR 등) 초기화, QoS 설정
2. **on\_activate**: 시뮬레이션 시계에 맞춰 주기적으로 데이터를 퍼블리시
3. **on\_deactivate**: 데이터 퍼블리시를 일시 중단
4. **on\_cleanup**: 시뮬레이터 리소스를 해제하거나, 가상 모델 제거

시뮬레이션 환경에서는 QoS를 단순화(Best Effort 등)할 수도 있지만, 실제 하드웨어 환경과 동일한 QoS를 설정해 테스트하는 편이 오류를 미연에 방지하는 데 유리하다.

#### 라이프사이클 노드의 확장 포인트(Extension Points)

라이프사이클 노드는 주어진 콜백 이외에도, 다양한 훅(Hook)을 추가해 세부 로직을 삽입할 수 있다.

* `on_shutdown`
  * 노드가 완전히 종료되기 직전에 리소스 해제를 최종 점검
* `on_error`
  * 전이 과정에서 에러가 발생했을 때 자동 호출되는 콜백
* `on_cleanup`
  * inactive -> unconfigured로 갈 때 자원 해제 외에, DB 연결 종료, 파일 닫기 등 기타 작업도 수행

이 외에도 사용자 정의 함수나 타이머 콜백을 추가해, 노드가 active 상태일 때만 주기적 로직을 돌리는 식의 구조를 구현할 수 있다. 예를 들어, `on_activate`에서 타이머를 시작하고, `on_deactivate`에서 타이머를 중단하면, 해당 주기적 작업이 inactive 상태에서는 작동하지 않게 된다.

#### 라이프사이클 노드에서 발생 가능한 문제 사례

라이프사이클 노드를 실제 프로젝트에 적용할 때, 다음과 같은 문제 사례가 자주 보고된다. 이를 사전에 알고 대응책을 마련하면 안정적인 시스템 구현에 도움이 된다.

1. **상태 전이 순서 누락**
   * `on_configure` 후 `on_activate`를 반드시 호출해야 하는데, configure 단계를 거치지 않은 상태에서 activate를 호출하여 예외가 발생하는 경우
   * 라이프사이클 매니저 또는 Launch 파일에서 전이 순서를 명시적으로 제어해야 한다.
2. **QoS 불일치로 인한 통신 실패**
   * 퍼블리셔는 Reliable, 서브스크라이버는 Best Effort 등으로 설정되어 연결이 실패하거나 메시지가 전달되지 않는 사례
   * 라이프사이클 노드에서 `on_configure` 시점을 이용해 서로 맞는 QoS인지 확인 후, 맞지 않으면 Failure 처리나 에러 상태로 전환하는 로직을 추가할 수 있다.
3. **콜백 내에서 과도한 처리**
   * `on_configure`, `on_activate` 등 라이프사이클 콜백에서 복잡한 연산(예: 대용량 파일 로딩, 긴 대기 등)을 수행해 상태 전이가 지연됨
   * 가급적 콜백 내에서는 반드시 필요한 초기화만 수행하고, 나머지는 active 상태에서 별도 스레드나 타이머 콜백으로 처리하는 것이 권장된다.
4. **비정상 종료 시 자원 누수**
   * 중간에 노드가 크래시되거나, `on_deactivate`·`on_cleanup` 과정을 거치지 못하고 종료되면, 파일 핸들이나 하드웨어 리소스가 해제되지 않을 수 있다.
   * 비정상 종료 상황까지 고려해, `on_shutdown`이나 에러 핸들링 콜백에서 자원을 철저히 해제하도록 설계한다.
5. **멀티 스레드 환경에서의 동기화 이슈**
   * 라이프사이클 콜백과 사용자 정의 콜백(타이머, 서브스크립션)이 동시에 접근하는 변수에 대한 동기화 문제가 발생할 수 있다.
   * 상태 전이 중에는 주요 공유 자원에 대한 Mutex 등을 사용해 접근을 제한하는 것이 안전하다.

#### 라이프사이클 노드로의 마이그레이션 전략

기존에 단순 노드(Simple Node)로 구현된 시스템을 라이프사이클 노드로 전환하는 경우, 다음 단계를 거치는 것이 좋다.

1. **상태 정의**
   * 현재 노드가 “초기화”, “동작 중”, “종료” 등 어떤 단계를 거치는지 정리하고, 라이프사이클 노드의 unconfigured, inactive, active, shuttingdown 상태와 매핑한다.
2. **콜백 분리**
   * 기존에 `main` 함수나 생성자에서 일괄 처리하던 초기화 로직을 `on_configure`로, 메시지 송수신 시작 로직을 `on_activate`로, 종료 혹은 자원 해제 로직을 `on_cleanup` 또는 `on_shutdown`으로 분리한다.
3. **QoS 설정 점검**
   * 기존 노드에서 명시적으로 QoS를 설정하지 않았다면, ROS2 기본 QoS(Profile)를 사용 중이었을 가능성이 크다. 라이프사이클 노드로 전환하면서, 필요한 QoS(특히 Reliable vs. Best Effort, Depth, Durability 등)를 명확히 지정해 준다.
4. **전이(Transition) 테스트**
   * unconfigured -> configure -> inactive -> activate -> deactivate -> cleanup 등 모든 전이 과정을 순서대로 테스트해 본다.
   * 전이 과정에서 예외가 발생하거나, 의도치 않은 상태(콜백 중 중단 등)에 빠지지 않는지 확인한다.
5. **라이프사이클 매니저 또는 Launch 연동**
   * 여러 라이프사이클 노드를 동시에 운용한다면, Launch 파일에서 전이 순서를 자동화하거나, 라이프사이클 매니저 노드로부터 전이 명령을 받아 상태를 제어하도록 설계한다.

#### 라이프사이클 노드 성능 및 프로파일링

대규모 시스템에서 라이프사이클 노드의 전이가 잦거나, QoS 설정이 복잡하다면 성능과 프로파일링도 신경 써야 한다.

1. **콜백 실행 시간 측정**
   * `on_configure`, `on_activate`, `on_deactivate`, `on_cleanup` 각각의 실행 시간을 로깅하거나 트레이싱 도구(LTTng 등)를 통해 측정한다.
   * 지나치게 긴 콜백은 시스템 전반에 지연을 야기하므로, 콜백 내부를 최적화하거나 비동기 처리로 분리한다.
2. **메모리 사용 추적**
   * 퍼블리셔, 서브스크라이버, 타이머를 생성하고 해제할 때 메모리 할당량이 어떻게 변하는지 추적한다.
   * 특히 반복적으로 configure->activate->deactivate->cleanup을 수행하는 경우, 메모리 누수가 없는지 확인해야 한다.
3. **QoS 프로파일 변경에 따른 네트워크 트래픽**
   * Reliable vs. Best Effort, KeepLast vs. KeepAll 등 설정 변화에 따라 네트워크 사용량과 지연이 어떻게 변하는지 모니터링한다.
   * 라이프사이클 노드 상태 전이 구간에서, 네트워크나 DDS 레이어에서 발생하는 패킷 재전송, 연결 재협상 등을 면밀히 살펴본다.
4. **스레드/실행기(Executor) 구성 최적화**
   * 라이프사이클 노드를 포함한 전체 노드들이 단일 스레드 실행기를 공유하는지, 또는 멀티 스레드 실행기를 사용하는지에 따라 성능이 달라질 수 있다.
   * 실시간성(Real-Time)이 요구되는 시스템에서는 RMW 구현체, RT 스케줄링, 고우선순위 스레드 할당 등을 종합적으로 고려해야 한다.

#### 라이프사이클 노드 사용 시 대표적인 ROS2 API

라이프사이클 노드는 다음과 같은 ROS2 API와 함께 주로 사용된다.

* `rclcpp_lifecycle::LifecycleNode`
  * 라이프사이클 노드의 기본 클래스로, `on_configure` 등 콜백을 오버라이드하여 사용
* `rclcpp_lifecycle::LifecyclePublisher`
  * 라이프사이클 노드에서 퍼블리셔를 생성할 때 사용. 노드 상태에 따라 `on_activate`, `on_deactivate`로 퍼블리셔를 활성/비활성
* `rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface`
  * 라이프사이클 노드의 콜백 반환값(CallbackReturn::SUCCESS, FAILURE 등)을 정의하고, 전이 함수를 제공
* `rclcpp::QoS`, `rmw_qos_profile_t`
  * 라이프사이클 노드에서 퍼블리셔/서브스크라이버를 생성할 때, 원하는 QoS 정책을 설정하기 위해 사용
* `ChangeState`, `GetState`, `rclcpp_lifecycle::State`
  * 라이프사이클 매니저나 Launch 파일에서 노드 상태를 제어할 때 사용되는 메시지/서비스/함수들

#### 라이프사이클 노드 예시: 서비스 서버/클라이언트

라이프사이클 노드는 퍼블리셔/서브스크라이버뿐 아니라 서비스 서버/클라이언트, 액션 서버/클라이언트에서도 동일한 방식으로 동작한다. 예를 들어, 서비스 서버를 라이프사이클 노드에서 정의한다면:

```cpp
auto server_ = this->create_service<my_msgs::srv::DoSomething>(
  "my_service",
  [this](const std::shared_ptr<my_msgs::srv::DoSomething::Request> request,
         std::shared_ptr<my_msgs::srv::DoSomething::Response> response) {
    // active 상태에서만 서비스 콜백 수행
    if (this->get_current_state().id() == LifecycleNodeInterface::State::PRIMARY_STATE_ACTIVE) {
      // 서비스 로직
      response->success = true;
    } else {
      // active가 아니면 서비스 요청을 처리하지 않거나 에러 반환
      response->success = false;
    }
  });
```

* `on_configure`에서 `create_service`를 호출하고, on\_activate에서 실제 로직을 활성화할 수 있다.
* active가 아닌 상태에서 들어온 요청은 에러 처리(또는 무시)함으로써, 시스템이 불완전한 상태에서 함수를 수행하지 않도록 방지한다.

#### 에지(Edge) 디바이스와 라이프사이클 노드

에지 디바이스(작은 임베디드 컴퓨터, SBC 등)에서 ROS2를 사용하면 CPU, 메모리 자원이 매우 제한적일 수 있다. 라이프사이클 노드를 도입하면 다음과 같은 장점이 있다.

* **비활성 상태에서 최소 자원 사용**
  * inactive 상태일 때는 주기적인 메시지 처리를 하지 않으므로 CPU 부하를 줄일 수 있다.
  * 필요 시에만 active로 전환해 자원을 사용하고, 다시 inactive 혹은 unconfigured 상태로 전환해 전력/자원 소모를 억제할 수 있다.
* **온디맨드(On-demand) 센서 구동**
  * 예: 카메라 센서가 항상 동작할 필요가 없다면, 라이프사이클 매니저가 필요할 때만 카메라 노드를 activate하고, 촬영 종료 후 deactivate하거나 cleanup하여 전력 절감.
* **에러 복구 절차 간소화**
  * 에지 디바이스에서 센서가 일시적으로 오동작하면, 라이프사이클 전이를 통해 빠르게 복구(cleanup->configure->activate)할 수 있다.
  * 별도의 디바이스 리부팅 없이 노드 단위로 재활성화가 가능하다.

#### 향후 고려 사항

* **라이프사이클 노드의 타 노드 의존성 관리**: 특정 노드가 activate되기 전에 반드시 다른 노드가 active되어야 하는 의존 관계(Dependency) 관리를 정교하게 해야 한다.
* **ROS2 버전 차이**: Humble, Galactic, Foxy 등에서 라이프사이클 노드 API가 약간씩 다를 수 있으므로, 공식 문서를 항상 참조해야 한다.
* **CI/CD 및 자동화 테스트**: 라이프사이클 노드는 상태 전이 그래프가 많으므로, 자동화된 테스트(예: GitHub Actions, GitLab CI 등)에서 각 상태 전이를 스크립트로 검증하면 유지보수에 도움이 된다.
