# QoS 설정 불일치로 인한 통신 문제

#### 개요

ROS2에서 퍼블리셔(Publisher)와 서브스크라이버(Subscriber)가 정상적으로 실행되고 있음에도, 토픽 메시지가 전달되지 않거나 지연되는 문제가 간혹 발생한다. 이러한 문제는 대부분 QoS(Quality of Service) 설정이 서로 맞지 않아 생긴다. ROS2에서는 토픽 통신 시 QoS 프로파일(Policy)을 설정할 수 있는데, 이를 통해 메시지 손실, 버퍼 설정, 라티ENCY 요구사항 등을 제어한다. 하지만 퍼블리셔와 서브스크라이버가 서로 다른 QoS Policy를 갖는다면, 양방향 통신이 원활하지 못하거나 심지어 통신 자체가 성립하지 않는 상황이 생길 수 있다.

#### QoS Policy 종류

ROS2의 QoS Policy는 여러 가지가 있으나, 주로 다음이 통신에 직접적인 영향을 준다.

1. **Reliability**
   * Reliable: 퍼블리셔가 전송한 모든 메시지를 보장받는다. 중간에 손실된 메시지가 있으면 재전송을 시도한다.
   * Best Effort: 가능한 한 빠르게 메시지를 전송하되, 손실에 대한 보장은 하지 않는다.
2. **Durability**
   * Volatile: 노드가 구독을 시작한 시점 이후부터의 메시지만 받을 수 있다.
   * Transient Local: 퍼블리셔가 종료 전까지 보관하고 있던 이전 메시지도, 서브스크라이버가 새롭게 연결되었을 때 받을 수 있다.
3. **History**
   * Keep All: 모든 메시지를 큐에 보관한다.
   * Keep Last: 가장 최근 N개의 메시지만 큐에 보관한다.
4. **Depth**
   * Keep Last 정책일 때, 저장할 메시지 수 N을 정한다. 예: Depth=10.

위 Policy들은 퍼블리셔와 서브스크라이버 양쪽에서 일치해야만 의도한 대로 동작한다.

#### QoS 설정 불일치 사례 분석

ROS2 개발 환경에서 자주 발생하는 문제 중 하나는 퍼블리셔는 Reliable를 설정했는데, 서브스크라이버가 Best Effort를 사용하는 경우이다. 이때 서브스크라이버는 메시지를 받아도 재전송 요청을 수행하지 않으므로, 네트워크 상태가 불안정하거나 패킷 손실이 있을 때 메시지가 유실될 수 있다.

또 다른 예로는 Durability에서 퍼블리셔는 Transient Local로 설정했으나, 서브스크라이버는 Volatile로 설정한 경우이다. 이때 새롭게 구독을 시작한 서브스크라이버는 과거 메시지를 받지 못하기 때문에, 예상과 다른 결과가 발생할 수 있다.

이러한 설정 불일치로 인해 “왜 토픽 데이터가 전달되지 않을까”라는 의문이 생기기 쉽다. 초기 디버깅 과정에서는 퍼블리셔와 서브스크라이버의 코드 구현 부분에서 문제를 찾기 쉽지만, 실제로는 QoS Policy가 서로 맞지 않아 메시지가 전송되지 않는 경우가 많다.

#### 예시 프로젝트에서의 발생 상황

개발 중인 특정 토픽 예시를 들어 문제를 살펴보자. 다음은 간단한 퍼블리셔 예시 코드이다:

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

int main(int argc, char* argv[])
{
  rclcpp::init(argc, argv);
  auto node = rclcpp::Node::make_shared("example_publisher");

  // QoS 설정
  rclcpp::QoS qos(rclcpp::KeepLast(10));
  qos.reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE);
  qos.durability(RMW_QOS_POLICY_DURABILITY_VOLATILE);

  auto publisher = node->create_publisher<std_msgs::msg::String>("example_topic", qos);

  rclcpp::WallRate loop_rate(1);
  while (rclcpp::ok()) {
    auto msg = std_msgs::msg::String();
    msg.data = "Hello QoS!";
    publisher->publish(msg);
    RCLCPP_INFO(node->get_logger(), "Publishing: '%s'", msg.data.c_str());
    rclcpp::spin_some(node);
    loop_rate.sleep();
  }

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

서브스크라이버 예시는 다음과 같다:

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

class ExampleSubscriber : public rclcpp::Node
{
public:
  ExampleSubscriber() : Node("example_subscriber")
  {
    // QoS 설정
    rclcpp::QoS qos(rclcpp::KeepLast(10));
    qos.reliability(RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT);
    qos.durability(RMW_QOS_POLICY_DURABILITY_VOLATILE);

    subscription_ = this->create_subscription<std_msgs::msg::String>(
      "example_topic", qos,
      [this](std_msgs::msg::String::SharedPtr msg) {
        RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
      });
  }

private:
  rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};

int main(int argc, char* argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<ExampleSubscriber>());
  rclcpp::shutdown();
  return 0;
}
```

퍼블리셔는 Reliability를 Reliable로 지정했지만, 서브스크라이버는 Best Effort를 사용하고 있다. 이렇게 QoS 설정이 불일치하면, 네트워크 환경에 따라 정상 수신이 되거나 되지 않을 수 있다. 특히 무선 네트워크나 패킷 유실이 많은 환경이라면 메시지 유실이 흔히 발생할 수 있다.

#### 디버깅 접근 방법

QoS 설정 불일치로 인해 메시지가 수신되지 않거나 유실되는 경우, 우선 퍼블리셔와 서브스크라이버가 사용하는 QoS 프로파일을 비교하여 문제를 추적해야 한다. 코드를 한눈에 살펴보기 어렵다면 아래와 같은 접근 방법을 시도할 수 있다.

**RMW(ROS Middleware) 로그 레벨 상향 조정**: ROS2 실행 시, 환경 변수나 파라미터를 통해 로그 레벨을 높이면 QoS 관련 경고 메시지를 확인할 수 있다. 예를 들어, 아래와 같이 실행 스크립트나 쉘에서 환경 변수를 설정해볼 수 있다:

```bash
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
export RCUTILS_LOGGING_SEVERITY_THRESHOLD=DEBUG
ros2 run my_package example_publisher
```

또는 ROS2 CLI에서 직접 `--ros-args --log-level debug` 옵션을 사용하기도 한다.

**QoS 호환성 체크**: ROS2 Foxy 이상 버전부터 지원되는 $ros2 doctor$ 명령을 이용하면 QoS 설정 충돌 여부를 확인할 수 있는 기능(또는 플러그인)이 있다. 아직은 제한적이지만, 다음과 같이 사용할 수 있다:

```bash
ros2 doctor --report
```

이 명령어를 실행하면, 노드 간 호환되지 않는 QoS 설정이 있는지 간략한 리포트를 볼 수 있다. 다만, 모든 케이스를 탐지하지는 못하므로 직접 코드에서 QoS 설정을 점검하는 과정이 여전히 필요하다.

**간단한 테스트 노드 사용**: 문제가 발생하는 퍼블리셔나 서브스크라이버 코드 외에, QoS를 다양하게 시도하는 간단한 테스트 노드를 만들어서 서로 다른 QoS를 넣어가며 통신 결과를 확인해 본다. 이를 통해 “Reliability만 바꿔도 메시지를 수신하기 시작한다” 등의 패턴을 빠르게 파악할 수 있다.

**rqt\_graph나 ROS2 CLI 활용**:

* `rqt_graph`: 노드와 토픽 연결 상태를 시각화해주지만, QoS 설정을 보여주지는 않는다. 그러나 통신 관계가 잘 맺어져 있는지(토픽 이름, 노드 이름 등)는 확인 가능하다.
* `ros2 topic info`, `ros2 topic list -v`: 특정 토픽에 대한 QoS 정보를 조회할 수 있다. 예를 들어:

  ```bash
  ros2 topic info /example_topic --verbose
  ```

  위 명령어를 통해 퍼블리셔와 서브스크라이버가 사용 중인 QoS 설정(신뢰성, 지속성, 히스토리 등)을 CLI에서 확인할 수 있다.

#### QoS 상호 호환성

ROS2에서 QoS Policy가 서로 다르다고 해서 반드시 통신이 불가능한 것은 아니다. 오히려 상위 호환이 가능할 경우도 있다. 예를 들어, 한쪽이 Reliable, 다른 한쪽이 Best Effort라면 일반적으로 통신 자체는 맺어지되, Best Effort가 재전송 요청을 하지 않으므로 “메시지 수신은 되지만 간헐적으로 유실될 수 있다” 같은 식의 동작이 가능하다.

아래는 퍼블리셔와 서브스크라이버의 QoS 설정이 같거나, 혹은 상호 호환되는지 간단히 표현한 다이어그램이다:

{% @mermaid/diagram content="flowchart LR
A(Publisher QoS) -- QoS Mismatch --> D{통신 실패<br>또는 메시지 유실}
A(Publisher QoS) -- QoS Compatible --> B\[Subscriber QoS]

```
B[Subscriber QoS] -- Works Partially --> C(메시지<br>정상 수신)
B[Subscriber QoS] -- Not Perfect --> D{통신 실패<br>또는 메시지 유실}" %}
```

위 다이어그램처럼, QoS가 완벽히 일치하면 당연히 정상 통신이 이루어지지만, 일부 Policy만 다를 경우에는 “부분적으로 동작”하거나 “실패”하는 등 다양한 결과가 나타난다.

#### 자주 발생하는 QoS 불일치 패턴

ROS2 시스템에서 QoS를 설정할 때, 표면적으로는 Publisher와 Subscriber 모두 “메시지가 잘 전달될 것 같다”는 의도로 작성하지만, 실제로는 몇 가지 경우에 불일치가 자주 발생한다. 아래는 현장에서 흔히 보는 QoS 불일치 패턴과 그 증상이다.

1. **Reliability Mismatch**
   * Publisher: Reliable, Subscriber: Best Effort
   * 증상: 메시지를 주기적으로 퍼블리시해도, 네트워크 상태가 조금만 나빠지면 Subscriber 측에서 메시지를 못 받거나 간헐적으로 누락된다.
   * 원인: Subscriber는 Best Effort이므로 메시지 손실이 있어도 재전송을 요청하지 않는다.
2. **Durability Mismatch**
   * Publisher: Transient Local, Subscriber: Volatile
   * 증상: Subscriber를 나중에 실행하면, 과거에 퍼블리시된 메시지를 기대했는데 보이지 않는다.
   * 원인: Subscriber가 Volatile이면 “새롭게 연결”된 시점부터의 메시지만 받을 수 있고, 과거 메시지는 받지 못한다.
3. **Depth 설정 불일치**
   * Publisher: Depth=10, Subscriber: Depth=1
   * 증상: 퍼블리셔가 10개까지의 메시지를 큐에 저장하고 전송하려고 해도, 서브스크라이버가 큐를 1개만 유지한다면 메시지가 일부 누락되거나 최신 메시지만 간신히 전달될 수 있다.
   * 원인: Subscriber 측에서 큐 사이즈가 작으면 과도하게 쌓인 메시지를 처리하지 못해 누락 가능성이 커진다.
4. **History 정책 불일치**
   * Publisher: Keep All, Subscriber: Keep Last
   * 증상: 빠르게 퍼블리시되는 토픽인 경우, Subscriber가 Keep Last이면 최신 메시지만 전달받는다. 하지만 Publisher 입장에서는 모든 메시지를 보관하며 전송을 시도하기 때문에, 메시지 저장 및 전송 비용이 늘어난다.
   * 원인: 한쪽에서 모든 메시지를 보관하려 해도, 다른 한쪽에서 보관 의사가 없으면 결국 모든 메시지가 다 전달되지 않는다.

#### QoS 불일치에 대한 대응 전략

1. **설계 단계에서 QoS를 명확히 정의** 시스템 요구사항(실시간성, 메시지 유실 허용 범위 등)을 고려하여, 프로젝트 초기에 퍼블리셔/서브스크라이버 간 QoS 정책을 문서화한다.
2. **코드 구현 시 QoS 설정 부분을 모듈화** QoS 설정이 여러 곳에서 중복 정의되거나 하드코딩되지 않도록, 별도의 함수를 두거나 Config 파일(예: YAML)을 통해 한 번에 관리한다. 이를 통해 퍼블리셔와 서브스크라이버의 QoS 불일치를 줄일 수 있다.
3. **테스트 환경에서 다양한 QoS 조합 시도** 실제 네트워크 상태(유선/무선, 패킷 손실율, 지연 등)에 따라 QoS 조합별로 결과를 비교한다. 자동화된 테스트 스크립트나 CI(Continuous Integration) 환경에서 QoS가 다른 시나리오를 반복적으로 검증하면 도움이 된다.
4. **QoS 동적 변경 활용** ROS2에서는 런타임에 QoS를 변경하기가 쉽지 않지만, 일부 상황에서는 노드를 재시작하지 않고도 파라미터 동적 업데이트를 지원하도록 구현할 수 있다. 단, 이는 RMW나 노드 설계 방식에 따라 제한적이다.

#### Reliability Mismatch 예시 (수정 전/후)

아래는 Publisher와 Subscriber가 Reliability에서 불일치하는 전형적인 예를 보여준다.

**수정 전 (퍼블리셔: Reliable, 서브스크라이버: Best Effort)**

```cpp
// 퍼블리셔
rclcpp::QoS qos_pub(rclcpp::KeepLast(10));
qos_pub.reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE);
auto publisher = node->create_publisher<std_msgs::msg::String>("topic", qos_pub);

// 서브스크라이버
rclcpp::QoS qos_sub(rclcpp::KeepLast(10));
qos_sub.reliability(RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT);
auto subscriber = node->create_subscription<std_msgs::msg::String>(
  "topic", qos_sub, callback);
```

**수정 후 (퍼블리셔: Reliable, 서브스크라이버: Reliable)**

```cpp
// 퍼블리셔
rclcpp::QoS qos_pub(rclcpp::KeepLast(10));
qos_pub.reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE);
auto publisher = node->create_publisher<std_msgs::msg::String>("topic", qos_pub);

// 서브스크라이버
rclcpp::QoS qos_sub(rclcpp::KeepLast(10));
qos_sub.reliability(RMW_QOS_POLICY_RELIABILITY_RELIABLE);
auto subscriber = node->create_subscription<std_msgs::msg::String>(
  "topic", qos_sub, callback);
```

수정 후에는 양쪽이 메시지 재전송 메커니즘을 지원하므로, 손실을 최소화할 수 있다. 물론, Best Effort가 더 나은 경우도 있으며(저지연성, 리소스 절약 등), 의도에 맞게 설정해야 한다.

#### 추가 QoS Policy: Deadline, Liveliness, Lifespan

앞서 설명한 QoS Policy(신뢰성, 지속성, 히스토리, Depth) 외에도 ROS2에서는 Deadline, Liveliness, Lifespan 등 다양한 정책을 제공한다. 이 정책들도 퍼블리셔와 서브스크라이버 사이에서 불일치가 발생하면 예기치 않은 동작이 일어날 수 있다.

1. **Deadline**
   * 퍼블리셔가 특정 주기로 메시지를 퍼블리시할 것을 예상(보장)하고, 서브스크라이버도 같은 주기로 메시지를 받을 것을 기대한다.
   * 예: 퍼블리셔가 Deadline을 100ms로 설정하면, 100ms마다 메시지를 퍼블리시해야 하며, 서브스크라이버는 100ms 내에 새로운 메시지를 받지 못하면 Deadline Miss 이벤트가 발생한다.
   * **불일치 시 증상**: 퍼블리셔는 빠른 주기로 퍼블리시하지만, 서브스크라이버는 더 긴 Deadline을 설정해놓았다면 Deadline Miss 이벤트가 잘못 트리거되거나, 반대로 너무 짧게 잡아놓은 경우 실제 퍼블리셔 주기보다 빠르게 타임아웃이 발생해버려 Miss가 잦아진다.
2. **Liveliness**
   * 노드가 “살아있음(Alive)”을 증명하는 방식이나 빈도를 제어한다.
   * 예: Manual by Topic, Automatic 등 다양한 모드가 있으며, 퍼블리셔가 정해진 주기로 ‘liveliness’를 갱신해야 한다.
   * **불일치 시 증상**: 퍼블리셔는 Automatic 모드로 liveliness를 갱신하는데, 서브스크라이버가 Manual 모드로 대기하는 경우, 실제로 노드는 살아 있지만 서브스크라이버 측에서 Liveliness Lost 이벤트가 발생할 수 있다.
3. **Lifespan**
   * 퍼블리셔가 퍼블리시한 메시지가 유효한 기간(Time-to-live)을 정한다. Lifespan이 만료된 메시지는 서브스크라이버에서 더 이상 수신되지 않는다.
   * 예: Lifespan을 5초로 설정하면, 메시지가 퍼블리시된 지 5초가 지난 뒤에는 해당 메시지를 폐기한다.
   * **불일치 시 증상**: 퍼블리셔는 Lifespan을 길게 잡아서 과거 메시지를 유지하려고 했는데, 서브스크라이버는 매우 짧게 설정하거나 기본값(무제한)을 사용하는 경우, 새 메시지는 잘 수신되지만 특정 상황에서 오래된 메시지가 소실되어 의도치 않은 데이터 손실이 생긴다.

#### 다중 노드/멀티머신 환경에서의 QoS 문제

단일 노드와 단일 머신에서 QoS가 맞지 않는 경우도 있지만, ROS2 시스템은 흔히 여러 대의 머신을 통해 분산 처리한다. 특히 멀티머신 환경에서는 네트워크 지연, 패킷 손실률이 달라질 수 있으므로, QoS 설정이 더 민감해진다.

1. **유선 vs. 무선 네트워크**
   * 무선(Wi-Fi, LTE) 환경에서는 패킷 손실과 지연 변동이 크다. Reliable를 설정한 퍼블리셔와 서브스크라이버라면 재전송이 잦아져서 시스템 부하가 커질 수 있다.
   * 반면 Best Effort를 무선 환경에서 쓰면 패킷 손실이 발생해도 재전송이 없어 메시지 손실이 잦아진다. 실제 사용 시에는 타협점을 잘 찾아야 한다.
2. **브리지(Bridge) 사용 시**
   * ROS2와 ROS1 간 브리지를 사용하거나, DDS 구현체를 혼합(Fast DDS, Cyclone DDS 등)하여 사용하는 경우, QoS 매핑이 완벽히 일치하지 않을 수 있다. 브리지 내부에서 호환 가능한 QoS로 변환되지만, 일부 정책이 유실되거나 약화될 수 있다.
   * 예: ROS1에서는 QoS 개념이 상대적으로 단순하거나 노출이 제한적이므로, ROS2의 복잡한 QoS가 완전히 대응되지 않을 수 있다.
3. **멀티 네트워크 인터페이스**
   * 하나의 시스템에서 여러 네트워크 인터페이스(예: 유선과 무선을 동시에 사용)를 가지면, DDS 레벨에서 자동으로 모아쳐 브로드캐스팅이 이루어지는데, 이때 QoS 설정이 다른 노드와 충돌할 수 있다.
   * 예: 일부 노드는 유선 인터페이스를 통해 Reliable QoS를 잘 유지하지만, 무선 인터페이스를 통해 접속한 노드는 재전송 부담으로 인해 지연이 발생한다.

#### QoS Policy 우선순위 고려

ROS2 DDS 계층에서는 퍼블리셔가 설정한 QoS와 서브스크라이버가 설정한 QoS 중 일부를 합의(negotiation) 또는 상위 호환으로 인식하여 동작한다. 하지만 특정 정책(예: Reliability)은 퍼블리셔와 서브스크라이버가 다를 때, RMW별로 어떻게 처리할지 사양이 조금씩 다르다.

* **Fast DDS**: 일부 정책은 “Publisher가 Reliable이면 Subscriber가 Best Effort여도 일단 연결은 가능하되, 재전송은 하지 않는다” 식으로 동작한다.
* **Cyclone DDS**: 위와 동일하게 연결은 되지만, QoS 충돌이 있으면 Warning이나 Error 로깅이 남을 수 있다.

따라서 프로젝트에서 사용하는 RMW 구현체와 버전을 명시적으로 확인해두고, QoS 호환 방식이 어떻게 동작하는지 이해해야 한다.

#### 자주 놓치는 QoS 설정 체크리스트

QoS 문제를 디버깅하다 보면, 분명히 “퍼블리셔와 서브스크라이버의 설정이 같아 보이는데 왜 메시지가 안 가지?” 같은 난감한 상황을 마주할 수 있다. 실제로는 코드상에서 사소하게 다르거나, 기본값(Default QoS)과 충돌이 일어나는 등 여러 배경 요소가 얽혀 있을 수 있으므로, 아래 체크리스트를 참고해보자.

1. **토픽 이름이 정확히 일치하는지**
   * QoS와는 직접 관련 없어 보이지만, 토픽 이름이 다르면 어떤 QoS를 써도 통신이 이루어지지 않는다. 비슷한 이름(ex. `/example_topic` vs. `example_topic`)일 경우, 네임스페이스 문제인지 확인한다.
2. **퍼블리셔와 서브스크라이버에서 사용하는 ‘Node 이름’, ‘Namespace’**
   * ROS2에서 노드의 네임스페이스와 토픽의 네임스페이스가 미묘하게 다르면, 실제 토픽명이 달라져 QoS 문제가 아니라 네임스페이스 충돌 문제일 수 있다.
3. **퍼블리셔와 서브스크라이버가 동일 RMW를 사용하는지**
   * 예: Fast DDS와 Cyclone DDS를 혼합 사용할 때, QoS 호환성에 차이가 생길 수 있다. 특정 Policy가 A RMW에서는 완벽히 지원되지만 B RMW에서는 제한적으로만 지원되어 문제를 일으킬 수 있다.
   * `$ printenv | grep RMW_IMPLEMENTATION` 명령 등을 통해 현재 어떤 RMW가 로드되어 있는지 확인한다.
4. **코드 내에서 하드코딩된 QoS**
   * 일부 패키지 예제 코드나 라이브러리에서 QoS를 하드코딩해놓는 경우가 있다. 예를 들어, Subscriber 생성 시 QoS를 별도 설정할 수 없는 함수를 쓰면, 의도와 다르게 기본 QoS(Best Effort 등)가 할당될 수 있다.
5. **백그라운드에서 실행 중인 다른 노드(테스트 노드, 예전 버전 노드 등)**
   * 동일 토픽을 퍼블리시 혹은 구독하고 있는 다른 노드가 존재하면, 네트워크 트래픽을 늘리거나 DDS 마스터(Discovery) 단계에서 혼란을 줄 수 있다. 이는 QoS 문제와 뒤섞여서 디버깅이 어려워질 수 있다.
6. **기본 QoS(Profile) 사용 시 주의**
   * ROS2에서 `rclcpp::QoS(10)` 처럼 매개변수만 지정하면, 일부 Policy는 “기본값(Default)”이 적용된다. 이때 기본값이 다른 RMW 환경과 호환되지 않는 사례가 있다. 가능한 명시적으로 `reliability()`, `durability()`, `liveliness()`, `deadline()` 등을 설정해주는 편이 안전하다.
7. **DDS Discovery 지연**
   * QoS 설정이 아무리 올바르게 맞춰져 있어도, DDS Discovery 과정이 오래 걸리거나(특히 멀티머신 환경) 일시적으로 실패하면 노드 간 연결이 늦게 맺어질 수 있다. 이 경우 QoS 문제로 보이지만, 실제로는 Discovery 혹은 네트워크 브로드캐스팅 문제일 수 있다.

#### 추가 디버깅 방법

QoS 문제가 확실시될 때, 더 심층적인 분석을 하고 싶다면 아래 방법들도 활용할 수 있다.

1. **DDS 레벨 Wireshark 분석**
   * DDS 프로토콜(RTPS 등)을 Wireshark로 캡처하여 QoS 정책 교환(Discovery Phase) 과정이 어떻게 이루어지는지 살펴볼 수 있다. 이 방식은 심층 디버깅에 유용하지만, DDS 메시지 패킷을 해석하기 위해 RTPS 프로토콜 지식이 조금 필요하다.
2. **ROS2 Daemon 재시작**
   * `$ros2 daemon stop` → `$ ros2 daemon start` 로 ROS2 데몬을 재시작하면, 로컬 Discovery 정보가 새로고침되어 예상치 못한 캐시 문제를 해결하기도 한다.
3. **QoS Matched Callback 활용**
   * 최신 ROS2 RMW 구현체 중 일부는 노드 간 QoS가 매칭되었을 때 콜백을 호출해주는 API를 제공한다. (Fast DDS는 ‘matched’ 콜백, Cyclone DDS는 유사 기능) 이를 통해 “정말 QoS가 매칭되었는지”를 런타임에서 확인 가능하다.
   * 다만, 아직 일부 구현체나 버전에서만 지원될 수 있으므로, 사용 중인 RMW와 ROS2 버전을 확인해야 한다.
4. **$ros2 param get/set$ 명령으로 동적 변경 시도**
   * 일부 노드(예: rclcpp::Node 기반 노드)에서 QoS를 파라미터화해두었다면, 런타임에 `$ ros2 param set <node_name> <parameter_name> <value>` 명령으로 조정해볼 수 있다.
   * 단, 대부분의 경우 QoS는 노드를 다시 생성해야만 적용되는 정책이 많으므로, 100% 동적 반영이 어려울 수 있다.
5. **개발 환경과 실제 운영 환경의 QoS 분리**
   * 개발 PC(유선 네트워크)에서는 Reliable QoS로 작업하지만, 실제 운영 장비(무선, 저사양 보드 등)에서는 Best Effort 또는 Keep Last Depth=1 등으로 간소화해야 할 때가 있다. 이때, 각 환경에 맞춘 QoS 설정 파일(예: YAML, INI)을 만들어서 상황별로 로드하도록 설계하면 편리하다.
