데코레이터 노드 설계의 안티패턴 (Anti-Patterns in Decorator Node Design)

데코레이터 노드 설계의 안티패턴 (Anti-Patterns in Decorator Node Design)

1. 개요

데코레이터 노드 설계의 안티패턴은 데코레이터를 구현할 때 발생하는 잘못된 설계 관행을 식별하고 문제점과 개선 방법을 제시한다.

2. 안티패턴 목록

2.1 RUNNING 상태의 변환

문제: 자식의 RUNNINGSUCCESSFAILURE로 변환하면 비동기 실행 추적과 halt 메커니즘이 파괴된다.
개선: RUNNING은 항상 그대로 전달한다.

2.2 halt()에서 내부 상태 초기화 누락

문제: 카운터, 타이머 등이 halt 시 리셋되지 않으면 다음 실행에 잔여 상태가 영향을 미친다.
개선: halt()에서 모든 내부 상태를 초기화하고 기반 클래스의 halt()를 호출한다.

2.3 tick() 직접 호출

문제: child_node_->tick() 대신 executeTick()을 사용하지 않으면 노드의 상태 관리 로직이 누락된다.
개선: 항상 child_node_->executeTick()을 사용한다.

2.4 과도한 데코레이터 중첩

문제: 4단계 이상의 데코레이터 중첩은 가독성을 심각하게 저하시킨다.
개선: 3단계를 초과하면 서브트리로 분리한다.

2.5 ForceSuccess로 안전 행동 감싸기

문제: 안전에 중요한 행동의 실패를 ForceSuccess로 은폐하면 안전 보호가 무력화된다.
개선: 안전 행동에 ForceSuccess를 적용하지 않는다.

2.6 Timeout 없는 무한 반복

문제: Repeat(-1) 또는 KeepRunningUntilFailure를 Timeout 없이 사용하면 영원히 종료되지 않을 수 있다.
개선: 반드시 Timeout 또는 외부 조건에 의한 종료 메커니즘과 결합한다.

2.7 이중 Inverter

문제: Inverter(Inverter(C))는 항등 변환으로 무의미하다.
개선: 이중 Inverter를 제거한다.

2.8 Retry + ForceSuccess 내부

문제: Retry(ForceSuccess(Action))에서 ForceSuccess가 FAILURE를 차단하여 Retry가 항상 즉시 성공한다.
개선: ForceSuccess를 Retry 외부에 배치하거나 제거한다.

2.9 자식 미실행 시 halt 누락

문제: 전처리에서 자식을 skip할 때, 이전에 RUNNING이었던 자식의 halt를 호출하지 않으면 리소스 누수가 발생한다.
개선: 자식을 skip하기 전에 haltChild()를 호출한다.

2.10 부작용을 포함하는 데코레이터

문제: 데코레이터가 토픽 발행, 서비스 호출 등의 부작용을 포함하면 예측 불가능한 동작이 발생할 수 있다.
개선: 부작용은 액션 노드에서만 수행한다.

3. 점검 체크리스트

번호점검 항목
1RUNNING을 변환하지 않는가?
2halt()에서 내부 상태를 초기화하는가?
3executeTick()을 사용하는가?
4중첩 깊이가 3단계 이내인가?
5안전 행동에 ForceSuccess를 적용하지 않았는가?
6무한 반복에 종료 메커니즘이 있는가?
7이중 Inverter가 없는가?
8자식 skip 시 haltChild()를 호출하는가?

4. 참고 문헌

  • Colledanchise, M., & Ogren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
  • BehaviorTree.CPP 공식 문서. https://www.behaviortree.dev/

버전날짜변경 사항
v0.12026-04-05초안 작성