컴포지트 패턴 (Composite Pattern)

한눈에 보기

개별 객체와 객체 묶음을 같은 인터페이스로 다루어 트리 구조를 표현하는 구조 패턴이다. 파일과 폴더 예시 코드로 재귀 구조를 설명하고 투명성과 안전성의 트레이드오프를 다룬다.

최종 업데이트 2026.09.17

컴포지트 패턴(Composite Pattern)은 개별 객체와 객체들의 묶음을 같은 인터페이스로 다룰 수 있게 하여 트리 구조를 자연스럽게 표현하는 구조 디자인 패턴이다. 컴포지트 패턴을 적용하면 클라이언트는 지금 다루는 대상이 잎 하나인지 수백 개의 자식을 가진 그릇인지 구분하지 않고 동일한 코드로 처리할 수 있다. 파일 시스템의 파일과 폴더, UI의 위젯과 컨테이너가 대표적인 적용 사례다.

패턴의 의도

부분과 전체가 계층을 이루는 구조는 어디에나 있다. 폴더 안에 파일과 하위 폴더가 있고, 조직 안에 팀과 팀원이 있으며, 화면 안에 패널과 버튼이 있다. 이런 구조를 다룰 때 "이 객체가 단일 객체인가, 묶음인가"를 매번 조건문으로 검사하면 클라이언트 코드가 분기로 뒤덮이고, 새로운 종류의 구성 요소가 추가될 때마다 분기를 고쳐야 한다.

컴포지트 패턴의 의도는 부분과 전체의 계층 구조에서 단일 객체(Leaf)와 복합 객체(Composite)를 하나의 공통 타입으로 추상화하여, 클라이언트가 재귀적인 트리 구조를 균일하게 다루도록 만드는 것이다. 복합 객체에 연산을 요청하면 복합 객체는 자식들에게 같은 연산을 재귀적으로 전파하고, 잎에 도달하면 실제 작업이 수행된다.

구조와 참여 객체

  • 컴포넌트(Component): 잎과 복합 객체가 공유하는 공통 인터페이스 또는 추상 클래스다. 공통 연산을 선언한다.
  • 리프(Leaf): 자식을 갖지 않는 말단 객체다. 실제 작업 대부분이 여기서 수행된다.
  • 컴포지트(Composite): 자식 컴포넌트들을 컬렉션으로 보관하는 복합 객체다. 연산 요청을 받으면 자식들에게 위임하고 결과를 합쳐 돌려준다. 자식 추가와 삭제 메서드도 제공한다.
  • 클라이언트(Client): 컴포넌트 인터페이스만 사용해 트리 전체를 조작한다.

설계 시 자식 관리 메서드(add, remove)를 어디에 둘지 선택해야 한다. 컴포넌트에 두면 잎과 복합 객체를 완전히 동일하게 다룰 수 있어 투명성이 높지만, 잎에서 의미 없는 메서드가 노출되어 안전성이 떨어진다. 반대로 컴포지트에만 두면 타입 안전성은 좋아지지만 클라이언트가 형을 구분해야 하는 순간이 생긴다. 이 투명성과 안전성의 트레이드오프는 컴포지트 패턴의 단골 논점이다.

Java 코드 예시

파일과 디렉터리의 전체 크기를 계산하는 예제다. 디렉터리의 크기는 포함된 모든 항목 크기의 합이라는 재귀 구조가 그대로 드러난다.

// 컴포넌트: 공통 인터페이스
public interface FileSystemNode {
    String getName();
    long getSize();
}

// 리프: 단일 파일
public class FileLeaf implements FileSystemNode {
    private final String name;
    private final long size;

    public FileLeaf(String name, long size) {
        this.name = name;
        this.size = size;
    }

    @Override
    public String getName() { return name; }

    @Override
    public long getSize() { return size; }
}

// 컴포지트: 디렉터리
public class Directory implements FileSystemNode {
    private final String name;
    private final List<FileSystemNode> children = new ArrayList<>();

    public Directory(String name) { this.name = name; }

    public void add(FileSystemNode node) { children.add(node); }

    @Override
    public String getName() { return name; }

    @Override
    public long getSize() {
        long total = 0;
        for (FileSystemNode child : children) {
            total += child.getSize();  // 재귀적으로 자식에게 위임
        }
        return total;
    }
}

// 클라이언트: 파일인지 디렉터리인지 구분하지 않는다
Directory root = new Directory("project");
root.add(new FileLeaf("README.md", 1200));
Directory src = new Directory("src");
src.add(new FileLeaf("Main.java", 3400));
root.add(src);
System.out.println(root.getSize());  // 4600

클라이언트는 getSize를 한 번 호출할 뿐이고, 트리를 순회하는 재귀 로직은 컴포지트 내부에 숨어 있다. 깊이가 얼마든 코드가 달라지지 않는다는 점이 이 패턴의 힘이다.

장단점과 사용 시점

장점은 명확하다. 클라이언트 코드가 단일 객체와 복합 객체를 구분하는 조건문에서 해방되고, 새로운 리프나 컴포지트 타입을 추가해도 기존 코드를 수정할 필요가 없어 개방 폐쇄 원칙을 지킨다. 재귀 구조가 패턴 안에 캡슐화되므로 임의 깊이의 트리도 간결하게 처리된다.

단점은 공통 인터페이스를 지나치게 일반화하게 될 수 있다는 점이다. 잎과 복합 객체의 성격이 크게 다르면 어느 한쪽에는 어색한 메서드가 인터페이스에 들어가고, 런타임 타입 검사가 다시 필요해질 수 있다. 또한 트리에 순환 참조가 생기지 않도록 주의해야 하며, 구조가 평면적인 도메인에 억지로 적용하면 불필요한 복잡도만 늘어난다.

데이터가 본질적으로 트리 계층을 이루고, 부분과 전체에 같은 연산을 적용해야 할 때가 적기다. 조직도 급여 합산, UI 컴포넌트 렌더링, 메뉴와 하위 메뉴, 그래픽 편집기의 도형 그룹화가 전형적인 사례다. 실제 프레임워크에서도 흔히 발견되는데, Swing의 JComponent가 자식 컴포넌트를 재귀적으로 그리는 구조나, HTML 문서 객체 모델(DOM)에서 요소 노드가 자식 노드를 품는 구조가 모두 컴포지트다.

유사 패턴과 비교

패턴구조목적의 차이
컴포지트(Composite)트리(1개가 여러 자식을 가짐)부분과 전체를 동일하게 다룬다
데코레이터(Decorator)사슬(1개가 1개를 감쌈)기능을 동적으로 덧붙인다
이터레이터(Iterator)순회 로직 분리내부 구조를 감추고 순회 방법을 제공한다
비지터(Visitor)연산을 별도 객체로 분리구조는 두고 새 연산을 추가한다

데코레이터는 자식이 정확히 하나인 컴포지트로 볼 수 있을 만큼 구조가 닮았지만, 목적이 다르다. 컴포지트는 집합의 표현이 목적이고 데코레이터는 기능 확장이 목적이다. 실무에서는 컴포지트 트리를 이터레이터로 순회하거나 비지터로 새 연산을 추가하는 식으로 함께 쓰이는 경우가 많다.

면접에서 이렇게 물어본다

  • 컴포지트 패턴이 해결하려는 문제를 조건문 분기의 관점에서 설명해 보라.
  • 자식 관리 메서드를 컴포넌트에 둘 때와 컴포지트에만 둘 때의 트레이드오프(투명성과 안전성)를 설명해 보라.
  • 파일 시스템 크기 계산을 컴포지트 패턴으로 설계한다면 클래스 구조를 어떻게 잡을 것인가.
  • 컴포지트 패턴과 데코레이터 패턴은 구조가 비슷한데 목적이 어떻게 다른가.
  • 컴포지트 트리에서 순환 참조가 생기면 어떤 문제가 발생하고 어떻게 방지할 수 있는가.
디자인 패턴 문서 전체 보기

이 개념, 내 코드에서는 어떻게 물어볼까요

데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.

공고 넣고 시작하기