PX4 MAVSDK – C++ Programming [10편] 사용자 정의 로깅 및 통합 테스트(gtest)
안녕하세요! 마케팅팀 에이든(Aiden)입니다. 지난 연재들에서는 오프보드(Offboard) 모드를 비롯하여 드론을 실제로 제어하는 다양한 방법론을 다루었습니다.
하지만 대학 연구실이나 기업에서 실제 로보틱스 소프트웨어를 개발하다 보면, 코드를 작성하는 시간보다 버그를 잡고 시스템의 안정성을 검증하는 시간이 훨씬 더 오래 걸린다는 사실을 깨닫게 됩니다. 드론의 특성상 사소한 논리 오류 하나가 추락이라는 치명적인 하드웨어 손상으로 이어질 수 있기 때문입니다.
그래서 이번 10편에서는 자율 비행 시스템의 신뢰성을 극대화하기 위해 필수적으로 알아야 할 두 가지 핵심 기술, 사용자 정의 로깅(Custom Logging)과 Google C++ 테스트 프레임워크(gtest)를 활용한 통합 테스트 자동화에 대해 상세히 알아보겠습니다.

1. MAVSDK 로깅 시스템의 이해
드론이 비행하는 동안 내부에서 어떤 데이터가 오가고 있는지, 통신 지연이나 패킷 손실은 없는지 파악하려면 로그(Log)를 확인해야 합니다. MAVSDK 코어 및 플러그인은 작업 중에 유용한 로그 메시지를 지속적으로 출력하도록 설계되어 있습니다. 기본적으로 이 메시지들은 표준 출력(stdout), 즉 콘솔 화면에 그대로 출력됩니다.
로그 메시지는 그 중요도에 따라 4가지 레벨(Level)로 나뉩니다.
- Debug: 내부 진행 상황이나 상태를 상세히 보고하기 위한 메시지입니다. (참고로 릴리스(Release) 빌드에서는 표시되지 않습니다.)
- Info: 일반적인 진행 상황과 상태를 알리는 정보성 메시지입니다.
- Warn: 명령이 거부되는 등 차량이 예상대로 작동하지 않을 때 발생하는 경고 메시지입니다.
- Err: 통신 링크 문제나 재시도 실패 등 SDK 동작과 관련된 심각한 오류를 보고합니다.

사용자 정의 로깅 (Custom Logging Callback)
콘솔 화면에 로그가 쏟아지는 것은 실시간 디버깅에는 좋지만, 연구 데이터를 사후 분석하거나 서버에 로그를 전송해야 할 때는 불편합니다. MAVSDK는 사용자가 직접 콜백 함수를 등록하여 로깅 시스템의 동작을 재정의(Override)할 수 있는 기능을 제공합니다.
mavsdk::log::subscribe 함수를 사용하면 로그 메시지를 외부 파일로 저장하거나, 중요하지 않은 메시지(예: Debug)의 출력을 필터링하여 무시할 수 있습니다.
[사용자 정의 로깅 C++ 예제 코드]
#include <iostream>
#include <fstream>
#include <mavsdk/mavsdk.h>
#include <mavsdk/log_callback.h>
using namespace mavsdk;
int main() {
// 로그를 저장할 파일 스트림 열기
std::ofstream log_file("drone_flight_log.txt", std::ios::app);
// 사용자 정의 로그 콜백 등록 (람다 함수 활용)
mavsdk::log::subscribe([&log_file](mavsdk::log::Level level,
const std::string& message,
const std::string& file,
int line) {
// 1. 모든 로그를 파일에 기록 (사용자 정의 로직)
if (log_file.is_open()) {
log_file << "[" << file << ":" << line << "] " << message << std::endl;
}
// 2. 콘솔에는 경고(Warn) 및 에러(Err)만 출력하도록 필터링
if (level == mavsdk::log::Level::Warn || level == mavsdk::log::Level::Err) {
std::cerr << "⚠️ [중요 알림]: " << message << '\n';
}
});
std::cout << "사용자 정의 로깅이 시작되었습니다.\n";
// 드론 연결 및 제어 로직 생략...
return 0;
}
이 코드를 적용하면, 현재 콜백으로 등록된 람다 함수가 실행되며, 두 번째 호출 시에는 이전 콜백을 덮어쓰게 됩니다. 만약 기본 로깅 동작(stdout 출력)으로 다시 돌아가고 싶다면 nullptr과 함께 subscribe를 호출하여 구독을 취소하면 됩니다.
🚨 연구원을 위한 멀티스레드 로깅 주의사항
콜백은 내부적으로 여러 스레드에서 무작위로 호출될 수 있습니다. 성능 저하를 막기 위해 MAVSDK 측에서는 로그 스트림에 대한 동기화(뮤텍스 락 등)를 별도로 처리하지 않습니다. 따라서 외부 파일에 기록하는 로깅 시스템이 스레드로부터 안전(Thread-safe)하지 않다면, 연구자가 직접 std::mutex 등을 사용하여 동기화를 구현해야 데이터가 깨지지 않습니다. 또한, 이 콜백 안에서 장시간 걸리는 무거운 작업을 수행하면 통신 지연이 발생할 수 있으므로 가능한 한 빨리 반환되도록 코드를 가볍게 작성해야 합니다.
플러그인 및 테스트 내부 로깅 API
만약 여러분이 MAVSDK 코어를 수정하거나 새로운 통합 테스트 코드를 작성할 때 콘솔 로깅이 필요하다면, MAVSDK에서 제공하는 내부 매크로 API를 사용할 수 있습니다.
이 API는 타임스탬프, 로그 메시지 유형(예: Error), 그리고 메시지가 발생한 파일명과 줄 번호를 자동으로 메시지 앞뒤에 붙여줍니다.
#include "integration_test_helper.h"
#include "log.h"
void my_test_function() {
LogDebug() << "이것은 디버그 메시지입니다. 진행 상황 모니터링용입니다."; // [2]
LogInfo() << "이것은 정보 메시지입니다."; // [2]
LogWarn() << "차량이 명령대로 동작하지 않습니다!"; // [2]
LogErr() << "통신 링크가 끊어졌습니다."; // [2]
}
출력 예시: [04:55:08|Error] This is an error message (test_file.cpp:26)
또한, MAVLink 메시지의 원시 패킷 데이터(Raw Packet) 수신 및 전송 내역을 모두 추적하고 싶다면, 소스 코드의 src/core/system.cpp 파일 상단에 #define MESSAGE_DEBUGGING 1 매크로를 설정하여 재빌드하면 됩니다 (디버그 빌드에서만 활성화됨).
자동화된 안정성 검증: Google Test (gtest) 연동
수백 줄의 자율 비행 코드를 작성한 후, 매번 드론의 전원을 켜고 조종기를 들고 나가서 테스트할 수는 없습니다. MAVSDK C++ 라이브러리는 코드의 무결성을 검증하기 위해 전 세계적으로 가장 널리 쓰이는 Google C++ 테스트 프레임워크(gtest)를 기본적으로 통합하고 있습니다.
새로운 코드가 라이브러리에 커밋될 때마다 이 테스트들이 실행되며, 테스트를 통과해야만 메인 브랜치에 코드가 병합(Merge)될 수 있습니다.
1) 단위 테스트 (Unit Tests) 실행
단위 테스트는 통신 모듈이나 거리 계산 알고리즘과 같은 개별 함수의 논리적 오류를 검증합니다. 프로젝트를 빌드한 후 다음 명령어로 모든 단위 테스트를 실행할 수 있습니다.
build/default/src/core/core_tests2) 통합 테스트 (Integration Tests)와 SITL 연동
단위 테스트가 논리를 검증한다면, 통합 테스트는 “이륙 명령을 내렸을 때 가상 드론이 실제로 고도를 높이는가?”와 같이 전체 시스템의 유기적인 동작을 검증합니다. 통합 테스트는 실제 기체를 대상으로 실행하면 예기치 못한 동작으로 인해 위험할 수 있으므로, 반드시 SITL(Software In The Loop) 가상 시뮬레이터 환경에서 실행해야 합니다.

[SITL 기반 통합 테스트 실행 절차] 통합 테스트를 실행하려면 먼저 PX4 Gazebo 시뮬레이션이 백그라운드에서 실행되고 있어야 합니다. 터미널 두 개를 열어 다음 절차를 따릅니다.
- 터미널 1 (시뮬레이터 수동 실행): 만약 그래픽 화면(3D 뷰어) 없이 터미널 백그라운드에서 가볍게 시뮬레이터만 돌리고 싶다면
HEADLESS=1옵션을 사용합니다. (이는 원격 서버나 CI/CD 파이프라인에서 매우 유용합니다.) - 터미널 2 (통합 테스트 실행): 이제 작성된 통합 테스트 러너를 실행합니다.
3) 테스트 필터링 및 디버깅 팁
테스트 케이스가 많아질 경우 특정 기능만 테스트하고 싶을 수 있습니다. 이때 gtest의 강력한 필터링 기능을 사용합니다.
- 전체 테스트 목록 확인:
- 특정 테스트(예: 원격 측정 건강 상태 확인)만 단일 실행:
연구원 여러분이 논문 구현을 위해 새로운 회피 알고리즘을 작성하셨다면, 알고리즘 플러그인에 대한 단위 테스트와 SITL 연동 통합 테스트를 이런 방식으로 자동화해 두어야 합니다. 이렇게 하면 코드를 대규모로 리팩토링하더라도 기존 기능이 깨지지 않았음을 1분 만에 증명할 수 있습니다.
이번 10편에서는 자율 비행 소프트웨어의 눈이 되어주는 사용자 정의 로깅 시스템 구축 방법과, 코드의 신뢰성을 담보하는 gtest 기반 통합 테스트 자동화 워크플로우를 알아보았습니다.
로깅에서 동기화(Thread-safe) 처리를 철저히 하고 콜백을 가볍게 유지하는 것, 그리고 실기체 비행 전 SITL 기반의 자동화 테스트 프레임워크를 구축하는 것은 취미용 코딩과 프로페셔널한 연구 개발을 가르는 가장 결정적인 차이점입니다.
이로써 MAVSDK C++의 환경 구축부터 제어 API, 로깅 및 테스트까지 프로그래밍에 필요한 거의 모든 실무적 요소를 다루었습니다.
다음 [11편: MAVSDK vs MAVROS vs uXRCE-DDS 완벽 비교]에서는 학계에서 드론 시스템 통신 프레임워크를 선택할 때 가장 많이 고민하는 3가지 기술을 학술적인 데이터(지연 시간, 리소스 점유율)를 바탕으로 심층 비교 분석해 보겠습니다.

작성자: 에이든(Aiden), 쿼드(QUAD) 드론연구소 마케팅팀
기고일: 2026.03.20
