# 다양한 QoS 프로필 이해(신뢰성, 내구성 등)

ROS2의 QoS(Quality of Service)는 노드 간 통신에서 트래픽 흐름, 데이터 전달 보장, 메모리 사용 전략 등을 제어하기 위한 핵심 수단이다. QoS 설정에 따라 메시지 누락 방지, 과거 데이터 보존, 시스템 자원 효율성 등이 달라진다. 이러한 설정은 DDS(Data Distribution Service)의 QoS 정책을 그대로 혹은 약간의 변형을 거쳐 ROS2에 적용하는 구조이며, 퍼블리셔와 서브스크라이버가 모두 같은 QoS 설정에 합의해야 원활한 통신이 가능하다.

ROS2에서 대표적으로 다루는 QoS 프로필은 다음과 같다.

* 신뢰성(Reliability)
* 내구성(Durability)
* 히스토리(History)
* 뎁스(Depth)
* 데드라인(Deadline)
* 리비니스(Liveliness)
* 리소스 제한(ResourceLimits)
* 기타 정책들(Ownership, Lifespan 등)

이 중에서도 통신 신뢰성과 과거 데이터 보존은 시스템 설계 시 매우 중요한 고려 요소다. 이번 절에서는 신뢰성(Reliability)과 내구성(Durability)을 먼저 자세히 살펴본다.

#### 신뢰성(Reliability)

QoS 설정에서 가장 중요하게 다루는 것 중 하나가 '신뢰성'이다. 메시지 손실을 허용할지, 허용하지 않을지를 설정함으로써 시스템 성능 및 네트워크 부하에 큰 영향을 준다. ROS2에서는 DDS 표준에 따라 ‘Best Effort’와 ‘Reliable’ 두 가지를 제공한다.

**Best Effort**

* 가능한 한 빠르게 메시지를 전달하되, 전달 보장은 하지 않는다.
* 네트워크 혼잡, 일시적인 트래픽 스파이크 등으로 인해 메시지가 손실될 수 있다.
* 센서 데이터나 고주파수 토픽(예: 3D 이미지 등)에서 메시지 누락을 일부 허용할 수 있을 때 유리하다.

**Reliable**

* 수신 측이 메시지를 놓치지 않도록 지속해서 재전송 및 확인을 수행한다.
* 네트워크 부하가 증가하며 레이턴시도 늘어날 수 있다.
* 제어 명령이나 매우 중요한 데이터(예: 고가의 로봇 장비를 제어하는 토픽)에서 필수적으로 요구된다.

신뢰성이 'Reliable'일 경우, DDS 레이어에서는 메시지마다 ACK(수신 확인)을 요청하고, 수신이 불확실하면 재전송을 수행한다. 이 과정을 단순화해보면 다음과 같은 확률 모델로 접근할 수 있다.

* 메시지가 전송될 때, 각 전송 시도의 성공 확률이 $p$라 하자.
* 동일 메시지의 전송 시도 횟수를 $n$이라 하면, 적어도 한 번 성공할 확률은

$$
P(\text{success at least once}) = 1 - (1 - p)^n
$$

* 여기서 $p$가 네트워크 상태, DDS의 혼잡 완화 알고리즘 등에 의해 동적으로 변할 수 있다. 신뢰성이 높아질수록 결과적으로 $n$을 증가시켜 $(1 - p)^n$을 매우 작게 만든다.

물론 실제 DDS 구현에서는 단순 반복 전송보다 복잡한 시간 지연 알고리즘, 적응형 전송(Adaptive transmission) 기법 등을 적용하여, 필요한 최소 횟수의 전송으로 신뢰성을 높이도록 설계한다.

#### 내구성(Durability)

내구성(Durability)은 "퍼블리셔가 데이터를 보냈을 때, 현재 수신자가 없더라도 데이터를 어디까지 보존할 것인가?"에 대한 QoS 정책이다. 만약 새로 구독하는 노드가 해당 데이터를 나중에라도 필요로 한다면, 내구성 설정을 통해 과거 데이터를 볼 수 있다. DDS 표준은 여러 모드를 정의하지만, ROS2에서 주로 사용하는 것은 크게 다음과 같다.

1. **Volatile**
   * 순간적인 데이터 전달만을 보장하며, 수신자가 없을 때 퍼블리셔가 보낸 데이터는 폐기된다.
   * 새롭게 서브스크라이버가 구독을 시작하더라도, 구독을 시작하기 전의 데이터는 받을 수 없다.
   * 센서 스트리밍 데이터와 같이 과거 데이터가 그다지 중요하지 않은 경우 적합하다.
2. **Transient Local**
   * 퍼블리셔가 데이터를 버퍼(주로 히스토리+뎁스)로 유지한다.
   * 만약 새로 서브스크라이버가 구독을 시작하면, 퍼블리셔에 저장되어 있던 과거 데이터(뎁스 범위 내)를 전달한다.
   * 시스템이 중단되지 않는 이상, 퍼블리셔가 해당 데이터를 일정 기간 보유해 주어야 하므로 메모리나 리소스 사용량이 증가할 수 있다.

DDS 표준에는 이외에도 'Transient'나 'Persistent' 모드가 있으나, ROS2의 구현에서 전적으로 지원되지 않거나 활용 빈도가 낮다. 일반적으로 ROS2에서 내구성을 설정할 때는 ‘Volatile’ 혹은 ‘Transient Local’을 지정하고, 퍼블리셔가 얼마나 많은 과거 데이터를 보유할지(History/Depth)와 함께 결정한다.

예를 들어, 아래 의사 코드는 C++에서 rclcpp::QoS를 이용해 Volatile 모드와 Reliable 신뢰성을 설정하는 모습이다.

```cpp
#include <rclcpp/rclcpp.hpp>

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

  // QoS 설정: Volatile Durability + Reliable Reliability
  auto qos = rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_default))
                .reliable()
                .durability_volatile();

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

  // ... 퍼블리싱 로직 ...

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

***

#### 히스토리(History)와 뎁스(Depth)

내구성 정책에서 다룬 것처럼, 퍼블리셔는 과거 데이터를 일정 개수 혹은 무제한으로 유지할 수 있다. 이를 제어하는 두 가지 QoS가 히스토리(History)와 뎁스(Depth)다.

* **History**
  * ROS2에서는 크게 `KEEP_LAST`와 `KEEP_ALL`을 제공한다.
  * `KEEP_LAST`는 '마지막 N개 데이터만 유지'하며, 이 N값을 뎁스(Depth)로 설정한다.
  * `KEEP_ALL`는 '퍼블리셔가 송신한 모든 데이터를 유지'한다. 이 경우 메모리 사용량이 지속적으로 증가한다.
* **Depth**
  * `KEEP_LAST` 설정일 때 유지해야 할 데이터 수를 지정한다.
  * 예를 들어 depth = 10으로 설정했다면, 마지막 10개의 메시지를 보관한다.
  * `KEEP_ALL`일 경우 depth는 무의미하게 되지만, 내부 구현에 따라 특정 메모리 제한이 걸릴 수 있다.

히스토리와 뎁스 설정은 주로 'Transient Local' 내구성 정책과 함께 사용되어, 새로 구독하는 노드에 과거 데이터 일부 또는 전부를 제공한다. 그러나 `KEEP_ALL`은 장기 실행에서 메모리 관리가 까다롭다는 점에 유의해야 한다.

#### 데드라인(Deadline)

데드라인(Deadline) QoS는 퍼블리셔가 일정 주기 안에 새로운 데이터를 반드시 보낼 것이라는 약속을 의미한다. 서브스크라이버 측에서는 해당 주기 내에 새로운 데이터를 수신하지 못하면, 데드라인 미스(Deadline Miss)를 알리는 콜백 등을 통해 오류를 감지할 수 있다.

* ROS2에서 `deadline(seconds, nanoseconds)` 형태로 설정할 수 있다.
* 퍼블리셔가 $T\_d$ 이하의 주기로 메시지를 퍼블리싱해야 하며, 서브스크라이버는 적어도 $T\_d$마다 새 데이터를 기대한다.
* 만약 $T\_d$ 시간 동안 새 메시지를 받지 못하면, 노드 로직 상에서 별도의 에러 처리나 상태 전환을 수행할 수 있다.

데드라인 QoS는 실시간성(real-time)이 중요한 로봇 제어나 센서 피드백에 자주 활용된다. 예를 들어, 50Hz(주기 0.02초)로 퍼블리셔가 데이터를 송신해야 하는 경우, 데드라인을 0.02초 이하로 설정해 놓으면 데이터 누락이나 지연을 즉시 감지할 수 있다.

***

#### 리비니스(Liveliness)

리비니스(Liveliness)는 DDS에서 ‘참여자(Participant)’ 또는 ‘퍼블리셔(Publisher)’가 여전히 활성(alive) 상태임을 보장해 주는 QoS 정책이다. ROS2에서는 주로 다음과 같은 리비니스 모드가 제공된다.

1. **Automatic**
   * DDS 레이어가 자동으로 노드의 생존 신호(Heartbeat)를 전송한다.
   * 노드가 실제로는 비정상 종료되었음에도, DDS와 제대로 동기화가 안 된 상태로 인해 한동안 ‘활성 상태’로 잘못 간주될 수도 있다.
   * 일반적인 센서 스트림이나 로깅처럼 단순 퍼블리셔-서브스크라이버 구조에 자주 쓰인다.
2. **ManualByParticipant**
   * 리비니스를 관리하는 주체가 ‘Participant(프로세스 단위)’ 관점으로 설정된다.
   * 노드(프로세스)가 주기적으로 생존 신호를 직접 보낸다.
   * 하나의 프로세스가 여러 개의 노드를 포함할 때, 그 프로세스 전체가 라이브리니스를 유지할지 여부를 DDS 레벨에서 판단한다.
   * 프로세스가 비정상 종료되면 곧바로 리비니스가 끊긴다.
3. **ManualByTopic**
   * 노드(퍼블리셔)가 퍼블리싱하는 특정 토픽마다 직접 Heartbeat를 보내 라이브리니스를 유지한다.
   * 퍼블리셔가 활성 상태임을 매우 정밀하게 제어하고 싶은 경우 사용한다.
   * 예: 로봇 제어 노드에서 특정 토픽은 매우 중요한 주기로 송신해야 하고, 이 토픽이 사라지면 곧바로 로봇 동작을 중단해야 하는 경우.

라이브리니스가 유효한지 판단하기 위해 ROS2는 ‘Lease Duration(임대 기간)’이라는 시간을 설정한다. 퍼블리셔가 해당 시간 내에 Heartbeat를 보내지 않으면, 서브스크라이버는 퍼블리셔가 더 이상 활성 상태가 아니라고 판단한다.

수식적으로 임대 기간을 $T\_l$이라 하고, 퍼블리셔가 Heartbeat 주기를 $t\_h$로 유지한다고 하자. Heartbeat가 정상적으로 유지되려면,

$$
t\_h < T\_l
$$

이어야 한다. 만약 $t\_h$가 $T\_l$보다 길어져버리면, 서브스크라이버 측에서 ‘퍼블리셔 사망(liveliness lost)’ 이벤트가 트리거될 수 있다.

라이브리니스 QoS는 로봇 시스템에서 실시간성이나 안정적인 노드 감시가 필요한 경우에만 주로 활용되고, 일반적인 애플리케이션에서는 ‘Automatic’ 모드로 충분한 경우가 많다.

#### 리소스 제한(ResourceLimits)

리소스 제한(ResourceLimits)은 DDS 내부적으로 노드가 사용할 수 있는 버퍼 크기, 큐 크기, 샘플 수 등을 제한하여, 메모리 사용량이 폭주하는 것을 방지하기 위한 QoS 정책이다. ROS2에서 이 설정을 잘못하면 메시지가 정상적으로 퍼블리싱되지 못하거나, 구독이 끊기는 등의 문제가 발생할 수 있다.

* **max\_samples**, **max\_instances**, **max\_samples\_per\_instance**와 같은 파라미터를 지정할 수 있다.
* ‘무제한(unlimited)’으로 설정할 수도 있지만, 실제 시스템에서는 메모리 고갈을 피하기 위해 적당한 상한값을 두는 것이 안전하다.
* 예: 메모리가 제한된 임베디드 시스템에서 한 번에 처리할 수 있는 메시지 샘플의 최대 개수를 1000으로 제한.

리소스 제한은 메시지를 ‘얼마나 많이 받아놓을 수 있는가?’에 대한 정책과도 직결된다. 퍼블리셔의 송출 속도가 서브스크라이버가 처리 가능한 속도를 초과할 때, 내부 버퍼가 가득 차면 더 이상 메시지를 수용하지 못하고 누락 혹은 블로킹이 발생할 수 있다.

#### 오너십(Ownership)

DDS 표준에는 Ownership이라는 QoS 정책이 존재하며, ‘단일 오너십(Exclusive Ownership)’과 ‘공유 오너십(Shared Ownership)’이 있다. ROS2에서는 기본적으로 ‘Shared Ownership’만 지원한다.

* **Exclusive Ownership**
  * 동일 토픽에 대해 여러 퍼블리셔가 있을 때, 단 하나의 퍼블리셔만이 메시지에 대한 “우선권”을 갖는다.
  * 우선권은 퍼블리셔마다 부여되는 Strength 값에 따라 결정된다.
  * ROS2에서는 내부적으로 전부 지원되는 것은 아니며, 일부 DDS 벤더에서만 동작할 수 있다.
* **Shared Ownership**
  * 동일 토픽을 퍼블리시하는 모든 퍼블리셔가 동등한 지위를 가진다.
  * 로봇 분산 환경에서 여러 센서가 같은 토픽을 퍼블리시할 수 있으며, 서브스크라이버가 알아서 골고루 수신할 수 있다.

오너십 QoS는 복잡한 DDS 네트워크에서 특정 퍼블리셔만 메시지를 유일하게 제공해야 하는 환경에서 유용하지만, 일반적인 ROS2 애플리케이션에서 자주 쓰이진 않는다.

#### 라이프스팬(Lifespan)

라이프스팬(Lifespan)은 “메시지를 퍼블리셔가 전달한 후, 얼마나 오래 살아있을 것인가?”를 결정하는 QoS 정책이다. 특정 시간 이후 자동으로 메시지가 무효화될 수 있다.

* 예: 메시지가 생성된 시점으로부터 1초 후, 서브스크라이버가 해당 메시지를 더 이상 유효한 데이터로 간주하지 않는다.
* ‘실시간 시계’ 기반으로 측정하며, 메시지가 너무 오래된 경우(유효 기간 초과)는 수신 측에서 폐기할 수 있다.
* 여러 센서 데이터가 있을 때, 타이밍에 맞춰 수신하지 못하면 의미가 없다고 판단하는 시스템에 적합하다(예: 카메라 프레임의 유효 시간이 100ms인 경우).

라이프스팬은 ‘과거 데이터가 필요 없어지는 시점’을 QoS 레벨에서 강제해 줄 수 있으므로, 메모리나 네트워크 트래픽 최적화에 도움이 된다.

#### 기타 QoS 정책들

DDS 표준에는 이외에도 LatencyBudget, TransportPriority 등 다양한 정책이 존재하지만, ROS2에서 전부 적극적으로 활용되지는 않는다. 시스템 요구사항, 통신 환경, DDS 벤더별 구현 차이에 따라 적용할 수 있으나, 일반적으로는 신뢰성(Reliability), 내구성(Durability), 히스토리(History), 뎁스(Depth), 리비니스(Liveliness) 정도만으로도 대부분의 로봇 애플리케이션을 커버할 수 있다.
