1292.21 반환 상태와 노드 생명주기의 관계
1. 노드 생명주기의 개요
행동 트리(Behavior Tree)에서 노드의 생명주기(lifecycle)는 노드가 생성된 이후부터 반복적으로 거치는 상태 전이의 순환적 패턴을 의미한다. 각 노드는 비활성 상태에서 시작하여, tick의 수신에 의해 활성화되고, 작업의 완료 또는 외부 중단에 의해 다시 비활성 상태로 복귀한다. 이 순환적 구조에서 반환 상태(Return Status)는 생명주기의 각 단계를 구분하고, 단계 간 전이를 유발하는 핵심 메커니즘이다.
BehaviorTree.CPP 라이브러리에서는 이 생명주기를 콜백 함수(callback function)의 체계를 통해 구현하며, 각 콜백은 특정 반환 상태와 직접적으로 대응한다 (Faconti, BehaviorTree.CPP Documentation, 2024).
2. 생명주기 단계와 반환 상태의 대응
2.1 비활성 단계 (Idle Phase)
비활성 단계는 노드가 Idle 상태에 있는 기간이다. 이 단계에서 노드는 어떠한 작업도 수행하지 않으며, 어떠한 자원도 점유하지 않는다. 노드는 다음의 두 가지 경로를 통해 비활성 단계에 진입한다.
- 초기 진입: 행동 트리가 생성된 직후 모든 노드는 비활성 단계에 놓인다.
- 종결 후 복귀: 노드가 Success, Failure, 또는 Skipped를 반환한 후 자동으로 비활성 단계로 복귀한다. 또한 Running 상태에서 halt를 수신한 후에도 비활성 단계로 복귀한다.
비활성 단계의 노드는 tick을 수신하기를 대기하는 수동적(passive) 상태에 있다.
2.2 초기화 단계 (Initialization Phase)
초기화 단계는 Idle 상태의 노드가 tick을 수신하여 실행을 시작하는 시점에 해당한다. BehaviorTree.CPP에서는 이 시점에 onStart 콜백이 호출된다. onStart 콜백은 노드의 내부 상태를 초기화하고, 필요한 자원을 할당하며, 작업의 실행을 개시하는 역할을 수행한다.
onStart 콜백은 반드시 Success, Failure, Running 중 하나를 반환하여야 한다. 반환 값에 따라 노드는 다음과 같은 생명주기 단계로 진입한다.
- Success 반환: 작업이 단일 tick 내에 성공적으로 완료되었으므로, 즉시 종결 단계로 진입한다.
- Failure 반환: 작업이 단일 tick 내에 실패로 판정되었으므로, 즉시 종결 단계로 진입한다.
- Running 반환: 작업이 아직 진행 중이므로, 실행 지속 단계로 진입한다.
2.3 실행 지속 단계 (Execution Phase)
실행 지속 단계는 노드가 Running 상태에 머무는 기간이다. 이 단계에서 노드는 이전 tick에서 개시된 작업을 계속 수행하며, 매 tick마다 onRunning 콜백이 호출된다. onRunning 콜백은 작업의 진행 상황을 확인하고, 완료 여부를 판정하는 역할을 수행한다.
onRunning 콜백 역시 Success, Failure, Running 중 하나를 반환한다.
- Running 반환: 작업이 아직 진행 중이므로, 실행 지속 단계가 유지된다. 이는 자기 전이(self-transition)에 해당한다.
- Success 반환: 작업이 성공적으로 완료되었으므로, 종결 단계로 진입한다.
- Failure 반환: 작업이 실패로 판정되었으므로, 종결 단계로 진입한다.
2.4 종결 단계 (Termination Phase)
종결 단계는 노드가 Success 또는 Failure를 반환한 직후의 단계이다. 이 단계에서 행동 트리의 실행 엔진은 다음의 처리를 수행한다.
- 반환 상태를 부모 노드에게 전달한다.
- 노드의 내부 상태를 Idle로 재설정한다.
- 할당된 자원이 있으면 해제한다.
종결 단계는 매우 짧은 과도 단계(transient phase)이며, 별도의 콜백이 호출되지 않는다. 노드는 종결 단계를 거쳐 즉시 비활성 단계로 복귀한다.
2.5 중단 단계 (Halt Phase)
중단 단계는 Running 상태의 노드가 외부로부터 halt 요청을 수신하였을 때 진입하는 단계이다. 이 단계에서 onHalted 콜백이 호출되며, 진행 중이던 작업의 정리(cleanup)와 자원 해제가 수행된다.
중단 단계는 정상적인 종결(Success/Failure)과 구분되는 비정상적 종결 경로이다. halt는 부모 노드가 자식의 실행을 강제로 종료할 필요가 있을 때 발생하며, 이는 다음과 같은 상황에서 발생한다.
- Sequence 노드에서 형제 노드가 Failure를 반환하여 Sequence 전체가 실패하는 경우, Running 상태의 자식에 halt가 전달된다.
- Fallback 노드에서 형제 노드가 Success를 반환하여 Fallback 전체가 성공하는 경우, Running 상태의 자식에 halt가 전달된다.
- 반응형 노드에서 조건의 재평가 결과가 변경되어 현재 실행 중인 자식을 중단해야 하는 경우.
3. 콜백과 반환 상태의 관계 구조
BehaviorTree.CPP의 StatefulActionNode 클래스에서 정의하는 세 가지 핵심 콜백과 반환 상태의 관계를 체계적으로 정리하면 다음과 같다.
| 콜백 | 호출 시점 | 노드의 이전 상태 | 허용된 반환 값 |
|---|---|---|---|
| onStart | 최초 tick 수신 시 | Idle | Success, Failure, Running |
| onRunning | 후속 tick 수신 시 | Running | Success, Failure, Running |
| onHalted | halt 요청 수신 시 | Running | 없음 (void) |
onStart와 onRunning의 구분은 노드가 “새로운 실행을 시작하는 것“과 “기존 실행을 계속하는 것“을 명확히 분리하는 역할을 한다. 이 분리를 통해 노드 개발자는 초기화 로직과 진행 로직을 독립적으로 구현할 수 있다 (Faconti, 2024).
4. 반환 상태에 의한 생명주기 순환
노드의 생명주기는 반환 상태에 의해 구동되는 순환적 패턴을 형성한다. 이 순환은 다음과 같이 표현된다.
Idle ──[tick]──→ onStart() ──→ Running ──[tick]──→ onRunning() ──→ Running
▲ │ │
│ │ (Success/Failure) │ (Success/Failure)
│ ▼ ▼
└──────────── 종결 ◄────────────────────────────────────────────── 종결
▲
│
└──── Running ──[halt]──→ onHalted() ─────────────────────────────┘
이 순환에서 핵심적인 관찰은 다음과 같다.
- 모든 실행은 Idle에서 시작한다: 노드는 항상 비활성 상태에서 실행을 개시하며, 이전 실행의 잔여 상태가 다음 실행에 영향을 미치지 않는다.
- 모든 종결은 Idle로 귀결된다: Success, Failure, halt에 의한 종결은 모두 Idle 상태로의 복귀를 수반한다.
- Running은 유일한 지속 상태이다: 복수의 tick에 걸쳐 유지될 수 있는 상태는 Running뿐이다.
5. 노드 유형별 생명주기 패턴
5.1 동기 액션 노드의 생명주기
동기 액션 노드(Synchronous Action Node)는 단일 tick 내에 실행이 완료되므로, Running 상태를 거치지 않는다. 따라서 생명주기는 다음의 단순한 패턴을 따른다.
Idle \xrightarrow{tick} Success/Failure \xrightarrow{auto} Idle
이 패턴에서는 onStart 콜백만 호출되며, onRunning과 onHalted 콜백은 호출되지 않는다.
비동기 액션 노드의 생명주기
비동기 액션 노드(Asynchronous Action Node)는 복수의 tick에 걸쳐 실행되므로, Running 상태를 포함하는 완전한 생명주기를 따른다.
Idle \xrightarrow{tick} Running \xrightarrow{tick} \cdots \xrightarrow{tick} Running \xrightarrow{tick} Success/Failure \xrightarrow{auto} Idle
Running 상태의 지속 기간은 작업의 물리적 실행 시간에 의해 결정되며, 이 기간 동안 매 tick마다 onRunning 콜백이 호출된다.
5.2 조건 노드의 생명주기
조건 노드(Condition Node)는 동기 액션 노드와 동일한 생명주기 패턴을 따른다. 조건의 평가는 즉각적이므로 Running 상태를 거치지 않으며, 매 tick에서 Success 또는 Failure를 즉시 반환한다.
6. 자원 관리와 생명주기의 연관
반환 상태에 의해 구동되는 생명주기는 자원 관리(resource management)와 밀접하게 연관된다. 자원의 할당과 해제는 생명주기의 특정 단계와 대응시켜야 하며, 이를 통해 자원 누수(resource leak)를 방지할 수 있다.
| 생명주기 단계 | 자원 관리 동작 | 대응 콜백 |
|---|---|---|
| 초기화 (Idle → Running) | 자원 할당 | onStart |
| 정상 종결 (Running → Success/Failure) | 자원 해제 | onStart/onRunning 반환 시 |
| 비정상 종결 (Running → Idle via halt) | 자원 해제 | onHalted |
특히 비동기 액션 노드에서 하드웨어 인터페이스, 네트워크 소켓, 작업 스레드 등의 자원을 할당한 경우, halt에 의한 중단 시에도 반드시 자원이 해제되어야 한다. onHalted 콜백의 구현은 이러한 자원 해제의 보장을 위한 필수적 안전 장치이다 (Faconti, 2024).
7. 생명주기 불변 조건
반환 상태와 생명주기의 관계에서 유지되어야 하는 불변 조건(invariant)은 다음과 같다.
- 초기화 유일성: onStart 콜백은 Idle 상태에서만 호출되어야 하며, Running 상태에서 호출되어서는 안 된다. 이 규칙의 위반은 노드의 내부 상태가 의도치 않게 재초기화되는 결과를 초래한다.
- 진행 연속성: onRunning 콜백은 Running 상태에서만 호출되어야 하며, Idle 상태에서 호출되어서는 안 된다. 이 규칙의 위반은 초기화되지 않은 상태에서의 진행 로직 실행을 초래한다.
- 종결 완전성: 노드가 Success, Failure를 반환하거나 halt를 수신한 후에는 반드시 Idle 상태로 복귀하여야 한다. 이 규칙은 생명주기의 순환적 완결성을 보장한다.
- 자원 무결성: Idle 상태의 노드는 어떠한 외부 자원도 점유하지 않아야 한다. 이 규칙은 비활성 노드에 의한 자원 점유가 없음을 보장한다.
참고 문헌
- Colledanchise, M. & Ogren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
- Faconti, D. (2024). BehaviorTree.CPP Documentation. https://www.behaviortree.dev/