조건 노드 설계의 안티패턴 (Anti-Patterns in Condition Node Design)
1. 개요
조건 노드 설계의 안티패턴(anti-pattern)은 조건 노드를 구현할 때 흔히 발생하는 잘못된 설계 관행을 식별하고 그 문제점과 개선 방법을 제시하는 것이다. 안티패턴은 표면적으로는 동작하는 것처럼 보이나, 안전성, 성능, 유지보수성 측면에서 심각한 문제를 내포한다.
2. 안티패턴 목록
2.1 RUNNING을 반환하는 조건 노드
문제: 조건 노드에서 RUNNING을 반환하면, ReactiveSequence와 ReactiveFallback의 반응적 동작이 파괴된다.
// 안티패턴
BT::NodeStatus tick() override
{
if (!data_ready_)
return BT::NodeStatus::RUNNING; // 절대 금지
// ...
}
개선: 데이터가 준비되지 않았으면 FAILURE를 반환한다.
2.2 부작용을 포함하는 조건 노드
문제: 조건 노드에서 토픽 발행, 서비스 호출, 블랙보드 쓰기 등의 부작용이 있으면, ReactiveSequence에 의한 반복 평가에서 부작용이 다중 실행된다.
// 안티패턴
BT::NodeStatus tick() override
{
cmd_vel_pub_->publish(stop_cmd_); // 부작용!
return checkCondition() ? SUCCESS : FAILURE;
}
개선: 부작용은 액션 노드에서만 수행한다.
2.3 차단 호출을 포함하는 조건 노드
문제: 동기 서비스 호출, 긴 대기 루프 등이 tick 주기를 지연시킨다.
// 안티패턴
BT::NodeStatus tick() override
{
auto result = client_->send_request_and_wait(request); // 차단!
return evaluate(result);
}
개선: 비동기 호출과 캐싱을 사용하거나, 별도의 액션 노드로 분리한다.
2.4 과도하게 복잡한 조건 노드
문제: 단일 조건 노드에서 복수의 관련 없는 조건을 평가하면, 재사용성이 저하되고 테스트가 어려워진다.
// 안티패턴
BT::NodeStatus tick() override
{
if (battery_ok_ && gps_ok_ && wind_ok_ && temp_ok_)
return SUCCESS;
return FAILURE;
}
개선: 개별 조건을 별도의 노드로 분리하고, Sequence로 결합한다.
2.5 하드코딩된 임계값
문제: 임계값이 코드에 직접 기입되어 있으면, 변경 시 재컴파일이 필요하다.
// 안티패턴
if (battery_level > 0.2) // 매직 넘버
개선: 입력 포트를 통해 임계값을 매개변수화한다.
2.6 무효 데이터에 대한 미처리
문제: NaN, Inf, 빈 배열 등의 무효 데이터를 검증하지 않으면, 잘못된 조건 평가가 발생한다.
// 안티패턴
BT::NodeStatus tick() override
{
double value = msg->data; // NaN 검사 없음
return (value > threshold) ? SUCCESS : FAILURE;
// NaN > threshold → false, 그러나 이것이 의도된 동작인가?
}
개선: std::isfinite()로 검증 후 평가한다.
2.7 메시지 미수신 시 SUCCESS 반환
문제: 센서 데이터가 수신되지 않은 상태에서 SUCCESS를 반환하면, 안전 조건이 충족되지 않았음에도 행동이 실행된다.
// 안티패턴
BT::NodeStatus tick() override
{
if (!msg)
return BT::NodeStatus::SUCCESS; // 위험!
// ...
}
개선: 데이터 부재 시 FAILURE를 반환한다(실패 안전 원칙).
2.8 스레드 안전성 미보장
문제: 콜백 스레드와 tick 스레드 사이에서 공유 데이터에 대한 동기화 없이 접근하면 데이터 경합이 발생한다.
// 안티패턴
void callback(const MsgPtr& msg) { last_msg_ = msg; }
BT::NodeStatus tick() { return evaluate(last_msg_); }
// 동시 접근에 대한 뮤텍스 없음!
개선: std::mutex로 공유 데이터를 보호한다.
2.9 과도한 로깅
문제: 매 tick마다 상세한 로그를 출력하면 I/O 오버헤드가 발생하고, 로그 파일이 급격히 증가한다.
개선: 상태 전환 시에만 로깅하거나, DEBUG 수준으로 설정하여 필요시에만 활성화한다.
2.10 조건 노드에서 상태 머신 구현
문제: 조건 노드 내부에 복잡한 상태 머신을 구현하면, 노드의 동작이 이전 tick의 이력에 의존하여 예측이 어려워진다.
개선: 상태 추적이 필요한 경우 블랙보드를 활용하거나, 행동 트리의 구조적 메커니즘(데코레이터, 제어 노드)을 사용한다. 히스테리시스와 같이 필수적인 내부 상태는 최소한으로 유지한다.
3. 안티패턴 점검 체크리스트
| 번호 | 점검 항목 | 준수 여부 |
|---|---|---|
| 1 | RUNNING을 반환하지 않는가? | |
| 2 | 외부 상태를 변경하지 않는가? | |
| 3 | 차단 호출이 포함되지 않았는가? | |
| 4 | 단일 조건만을 평가하는가? | |
| 5 | 임계값이 매개변수화되어 있는가? | |
| 6 | 무효 데이터를 검증하는가? | |
| 7 | 데이터 부재 시 FAILURE를 반환하는가? | |
| 8 | 스레드 안전성이 보장되는가? | |
| 9 | 로깅이 적절한 수준인가? | |
| 10 | 내부 상태가 최소한으로 유지되는가? |
4. 참고 문헌
- Colledanchise, M., & Ogren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
- BehaviorTree.CPP 공식 문서. https://www.behaviortree.dev/
| 버전 | 날짜 | 변경 사항 |
|---|---|---|
| v0.1 | 2026-04-04 | 초안 작성 |