Unity
[디자인 패턴] - 복합체 패턴(Composite)
CCS_Cheese
2026. 3. 15. 16:39
문제 상황 (Problem)
쇼핑몰 주문 시스템이나 파일 시스템을 개발한다고 가정해 보겠습니다.
상황:
- 상품(Product) 은 가격을 가지고 있습니다.
- 박스(Box)는 여러 개의 상품을 담을 수 있고, 또 다른 작은 박스도 담을 수 있습니다.
- 클라이언트는 "최종 가격"을 계산하고 싶어 합니다.
발생하는 문제:
- 만약 패턴을 쓰지 않는다면, 클라이언트는 "이것이 상품인가? 아니면 박스인가?"를 일일이 확인해야 합니다.
- 박스라면 내부를 다시 순회해야 하고, 상품이라면 가격을 가져와야 합니다. 박스 안에 박스가 또 있다면 코드는 중첩된 if-else문이나 복잡한 재귀 호출로 지저분해집니다.
- 새로운 종류의 '묶음 상품'이 추가되면 클라이언트 코드를 또 수정해야 합니다.
해결 방법 (Solution)
복합체 패턴은 "전체-부분(Whole-Part)" 관계를 가진 객체들을 트리 구조로 정의하고, 공통 인터페이스를 사용하여 이들을 똑같이 취급함으로써 문제를 해결합니다.
- Component (공통 인터페이스): 상품과 박스가 공통으로 가질 메서드(예: getPrice())를 정의합니다.
- Leaf (단일 객체): 더 이상 하위 요소가 없는 기본 객체입니다. (예: 상품)
- Composite (복합 객체): 자식 요소들을 리스트로 관리하며, Component의 메서드를 구현할 때 자식들에게 작업을 위임합니다. (예: 박스)
클래스 다이어그램 (Class Diagram)

상세 클래스 정의
Namespace Implement
// 1. Component: 공통 인터페이스
public abstract class ItemComponent
{
public abstract int GetPrice();
}
모든 클래스는 ItemComponent를 상속받아 GetPrice()라는 함수를 재정의 합니다. Client - 사용자는 ItemComponent의 GetPrice로 최종 계산 가격을 가져올 수 있는 구조를 따릅니다.
Namespace Leaf (개별 상품)
public class Product : ItemComponent
{
private string name;
private int price;
public Product(string name, int price)
{
this.name = name;
this.price = price;
}
public override int GetPrice()
{
return this.price;
}
}
실제 개별 상품의 정의부, GetPrice()를 재정의하여, 값을 가져올 수 있게 정의 합니다.
Namespace Composite (박스) - 상품들의 복합체
public class Box : ItemComponent
{
private List<ItemComponent> items = new List<ItemComponent>();
public void Add(ItemComponent item)
{
items.Add(item);
}
public void Remove(ItemComponent item)
{
items.Remove(item);
}
// 핵심 로직: 자식들의 가격을 모두 더함 (재귀적 위임)
public override int GetPrice()
{
int total = 0;
foreach (var item in items)
{
total += item.GetPrice();
}
return total;
}
}
Box (Compositor)는 각 개별 상품(Product)을 가지고, 마찬가지로 ItemComponent를 상속받아, GetPrice()를 오버라이드 하여, 값을 가져올 수 있는 구조를 따릅니다.
Namespace Client(User Side)
class Program
{
static void Main(string[] args)
{
// 상품 생성 (Leaf)
Product p1 = new Product("노트북", 1000);
Product p2 = new Product("마우스", 50);
Product p3 = new Product("충전기", 30);
// 작은 박스 생성 및 상품 담기 (Composite)
Box smallBox = new Box();
smallBox.Add(p2);
smallBox.Add(p3);
// 큰 박스 생성 (상품 + 작은 박스 담기)
Box bigBox = new Box();
bigBox.Add(p1); // 상품 추가
bigBox.Add(smallBox); // 박스 안에 박스 추가
// 클라이언트는 이것이 박스인지 상품인지 몰라도 가격 계산 가능
Console.WriteLine($"총 주문 가격: {bigBox.GetPrice()}원");
// 결과: 1000 + (50 + 30) = 1080원
}
}
Client에서는 ItemComponent를 통해 정의된 GetPrice를 통해, 개별 상품의 값을 가져오거나, 복합체(Box)에 개별 상품들을 추가하여, 값을 가져올 수 있어, ItemComponent라는 하나의 interface를 통해 값을 가져온다는 처리를 진행하게 됩니다.