1291.52 모듈 단위의 독립적 설계 가능 (Independent Design at the Module Level)

1291.52 모듈 단위의 독립적 설계 가능 (Independent Design at the Module Level)

1. 모듈 독립성의 개념과 공학적 의의

모듈 독립성(module independence)이란 시스템의 각 구성 모듈이 다른 모듈의 내부 구현에 의존하지 않고 독립적으로 설계, 구현, 테스트, 그리고 유지보수될 수 있는 설계 속성이다. Stevens 등(1974)이 제시한 구조적 설계(structured design) 원칙에서 모듈 독립성은 **높은 응집도(high cohesion)**와 **낮은 결합도(low coupling)**의 조합으로 규정된다. 응집도란 하나의 모듈 내부 요소들이 단일 기능적 목적에 집중하는 정도를 나타내며, 결합도란 모듈 간 상호 의존의 정도를 나타낸다.

로봇 행동 제어 시스템에서 모듈 독립성의 확보는 다음과 같은 공학적 이점을 제공한다.

  • 병렬 개발: 모듈 간 의존성이 최소화되면, 다수의 개발자가 동시에 서로 다른 모듈을 개발할 수 있다.
  • 단위 테스트: 개별 모듈을 독립적으로 테스트할 수 있으므로, 결함의 원인 식별이 용이하다.
  • 점진적 통합: 검증된 모듈을 순차적으로 통합하는 점진적 통합(incremental integration) 전략이 가능하다.
  • 유지보수 효율: 특정 모듈의 수정이 다른 모듈에 파급 효과를 발생시키지 않으므로, 수정의 안전성이 보장된다.

2. 행동 트리에서의 모듈 독립성 실현

2.1 서브트리를 모듈 단위로 활용

행동 트리(Behavior Tree, BT)에서 모듈의 자연스러운 대응 단위는 **서브트리(subtree)**이다. 트리 자료 구조에서 임의의 서브트리는 그 자체로 완전한 행동 트리이며, 서브트리의 루트 노드를 통해서만 상위 트리와 연결된다. 서브트리 내부의 모든 노드와 그 동작은 서브트리 경계 내에서 완결되며, 외부로 노출되는 정보는 오직 반환 상태(Success, Failure, Running)뿐이다.

이 구조적 특성에 의하여 각 서브트리는 다음의 조건을 자연스럽게 만족한다.

  • 기능적 응집: 서브트리는 하나의 명확한 행동 패턴(예: 비상 대응, 경로 추종, 충전 복귀)을 캡슐화한다.
  • 인터페이스 단순성: 외부와의 상호작용이 3값 반환 상태로 한정된다.
  • 구현 은닉: 서브트리 내부의 노드 구성, 제어 흐름, 세부 로직이 외부에 노출되지 않는다.

2.2 통일된 인터페이스에 의한 결합도 최소화

행동 트리의 모든 노드는 동일한 인터페이스를 준수한다.

\forall n \in \mathcal{N}: \text{output}(n) \in \{Success, Failure, Running\}

여기서 \mathcal{N}은 트리의 전체 노드 집합이다. 이 통일된 인터페이스에 의하여, 부모 노드는 자식 노드의 내부 구현을 알 필요 없이 반환 상태만으로 제어 흐름을 결정한다. 자식 노드의 입장에서도 부모 노드의 유형이나 형제 노드의 존재를 인식할 필요가 없다.

이 인터페이스의 통일성은 노드 간 결합도를 구조적으로 최소화한다. 유한 상태 머신에서 상태와 전이 규칙이 밀접하게 결합되어 있는 것과 대조적으로, 행동 트리에서는 노드 간의 유일한 결합이 부모-자식 관계를 통한 반환 상태의 전달이다.

2.3 블랙보드 포트에 의한 데이터 의존성 명시화

모듈 독립성의 완전한 달성을 위해서는 제어 흐름뿐만 아니라 데이터 흐름에서의 의존성도 관리되어야 한다. BehaviorTree.CPP 4.x에서는 각 노드의 **입력 포트(input port)**와 **출력 포트(output port)**를 명시적으로 선언하는 메커니즘을 제공한다.

static PortsList providedPorts()
{
    return {
        InputPort<std::string>("goal_pose"),
        OutputPort<nav_msgs::msg::Path>("computed_path")
    };
}

이 포트 선언에 의하여 노드의 데이터 의존성이 명시적으로 기술되며, 암묵적 의존성(implicit dependency)의 발생이 방지된다. 포트 매핑은 XML 정의에서 이루어지므로, 노드의 C++ 구현은 특정 블랙보드 키 명칭에 의존하지 않고 추상적 포트 명칭만을 사용한다. 이는 노드 수준의 모듈 독립성을 데이터 흐름 차원에서도 보장한다.

3. 독립적 설계의 실현 형태

3.1 서브트리 단위의 독립적 개발

행동 트리의 모듈 독립성에 의하여, 각 서브트리를 독립적인 개발 단위로 취급하는 워크플로우가 가능하다. 예를 들어, 자율 이동 로봇의 행동 트리를 다음과 같은 서브트리로 분해할 수 있다.

서브트리담당 기능독립 개발 가능 여부
SafetyMonitor비상 감지 및 비상 정지독립적
EnergyManagement배터리 수준 감시 및 충전 복귀독립적
NavigationCore경로 계획 및 경로 추종독립적
ObstacleAvoidance장애물 감지 및 회피 기동독립적
MissionExecution임무별 행동 시퀀스 실행독립적

각 서브트리는 독립적인 XML 파일로 정의되며, 독립적인 단위 테스트를 수행할 수 있다. 상위 트리에서는 이 서브트리를 <SubTree> 태그를 통하여 참조함으로써 통합한다.

3.2 독립적 단위 테스트

모듈 독립성의 실용적 이점 중 하나는 개별 서브트리를 독립적으로 테스트할 수 있다는 것이다. 테스트 환경에서 서브트리의 입력 포트에 테스트 데이터를 제공하고, 서브트리의 반환 상태를 검증함으로써 해당 서브트리의 정확성을 상위 트리와 무관하게 평가할 수 있다.

// 서브트리 단위 테스트 예시
BehaviorTreeFactory factory;
factory.registerNodeType<ComputePath>("ComputePath");
factory.registerNodeType<FollowPath>("FollowPath");

auto tree = factory.createTreeFromFile("navigation_subtree.xml");

// 블랙보드에 테스트 입력 설정
tree.rootBlackboard()->set("goal_pose", test_goal);

// Tick 실행 및 반환 상태 검증
NodeStatus status = tree.tickWhileRunning();
ASSERT_EQ(status, NodeStatus::SUCCESS);

이 테스트는 NavigationCore 서브트리만을 대상으로 하며, SafetyMonitor나 EnergyManagement 서브트리의 존재에 의존하지 않는다.

3.3 독립적 배포와 갱신

행동 트리의 서브트리가 별도의 XML 파일로 정의되면, 특정 서브트리만을 독립적으로 갱신하는 것이 가능하다. 로봇의 장애물 회피 알고리즘을 개선하는 경우, obstacle_avoidance.xml 파일만을 교체하면 되며, 나머지 서브트리 파일에는 어떠한 수정도 필요하지 않다.

이 독립적 배포 능력은 현장에 배치된 로봇 시스템의 부분적 갱신(partial update)을 가능하게 하며, 전체 행동 트리를 교체하는 것에 비하여 갱신의 위험도를 현저히 낮춘다.

4. 유한 상태 머신과의 비교

유한 상태 머신에서는 모듈 단위의 독립적 설계가 구조적으로 어렵다. 그 근본적 원인은 상태 간 전이의 **전역적 결합(global coupling)**에 있다.

유한 상태 머신에서 하나의 상태를 수정하면, 해당 상태로 진입하는 전이와 해당 상태에서 나가는 전이를 모두 검토하여야 한다. 전이 규칙은 원천 상태와 목표 상태를 명시적으로 참조하므로, 상태 간의 결합이 전이 규칙에 의하여 강제된다. n개의 상태를 가진 유한 상태 머신에서 하나의 상태를 독립적 모듈로 격리하기 위하여 차단하여야 할 의존성의 수는, 해당 상태와 관련된 전이의 수에 비례하며, 이는 최악의 경우 O(n)에 달한다.

반면, 행동 트리에서 하나의 서브트리를 독립적 모듈로 격리하기 위하여 관리하여야 할 인터페이스는 오직 반환 상태 하나와 명시적으로 선언된 포트 매핑뿐이다. 트리의 규모와 무관하게 모듈 인터페이스의 복잡도가 일정하게 유지된다.

\text{인터페이스 복잡도}_{FSM}(s_i) = O(\text{deg}(s_i)), \quad \text{인터페이스 복잡도}_{BT}(\mathcal{T}_i) = O(1 + \vert P_i \vert)

여기서 \text{deg}(s_i)는 상태 s_i의 전이 차수(degree), \vert P_i \vert는 서브트리 \mathcal{T}_i의 포트 수이다. 일반적으로 포트 수는 서브트리의 기능에 의하여 결정되는 상수적 값이므로, 행동 트리의 모듈 인터페이스 복잡도는 시스템 규모에 대하여 독립적이다.

5. 모듈 독립성 확보를 위한 설계 지침

5.1 서브트리 경계의 명확한 정의

모듈 독립성을 확보하기 위해서는 서브트리의 경계를 기능적으로 명확하게 정의하여야 한다. 각 서브트리는 하나의 명확한 기능적 목적(예: 네비게이션, 비상 대응, 에너지 관리)에 대응하여야 하며, 다수의 이질적 기능을 하나의 서브트리에 혼합하는 설계는 응집도를 저하시킨다.

5.2 포트 매핑의 명시적 관리

서브트리 간의 데이터 교환은 반드시 명시적 포트 매핑을 통하여 이루어져야 한다. 블랙보드의 전역 키를 통한 암묵적 데이터 공유는 서브트리 간의 은닉된 의존성(hidden dependency)을 발생시키며, 이는 모듈 독립성을 훼손한다. BehaviorTree.CPP 4.x의 블랙보드 네임스페이스 분리 기능을 활용하여 서브트리 간의 데이터 격리를 강제하는 것이 권장된다.

5.3 단일 책임 원칙의 적용

소프트웨어 공학의 **단일 책임 원칙(Single Responsibility Principle, SRP)**을 행동 트리의 각 노드와 서브트리에 적용하여야 한다. 하나의 Action 노드는 하나의 명확한 행동만을 수행하여야 하며, 하나의 서브트리는 하나의 명확한 행동 패턴만을 캡슐화하여야 한다. 이 원칙의 준수에 의하여 모듈의 기능적 응집도가 최대화되고, 모듈 간 결합도가 최소화된다.

6. 참고 문헌

  • Colledanchise, M., & Ögren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
  • Colledanchise, M., & Ögren, P. (2017). “How Behavior Trees Modularize Hybrid Control Systems and Generalize Sequential Behavior Compositions, the Subsumption Architecture, and Decision Trees.” IEEE Transactions on Robotics, 33(2), 372–389.
  • Faconti, D. (2022). BehaviorTree.CPP 4.x Documentation. https://www.behaviortree.dev/
  • Stevens, W. P., Myers, G. J., & Constantine, L. L. (1974). “Structured Design.” IBM Systems Journal, 13(2), 115–139.
  • Parnas, D. L. (1972). “On the Criteria To Be Used in Decomposing Systems into Modules.” Communications of the ACM, 15(12), 1053–1058.

버전: 2026-03-31