ObjectOriented 완벽 마스터
ObjectOriented의 핵심 개념과 실전 활용법
학습 항목
이미지 로딩 중...
Composite Pattern 실무 활용 가이드
트리 구조의 객체들을 단일 객체처럼 다루는 Composite Pattern의 실무 활용 방법을 학습합니다. 파일 시스템, UI 컴포넌트, 조직도 등 실제 프로젝트에서 자주 사용되는 패턴입니다.
들어가며
이 글에서는 Composite Pattern 실무 활용 가이드에 대해 상세히 알아보겠습니다. 총 12가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.
목차
- Component_인터페이스_정의
- Leaf_클래스_구현
- Composite_클래스_기본_구조
- Composite_메서드_위임
- 재귀적_Display_구현
- 클라이언트_코드_활용
- UI_컴포넌트_적용_예제
- 조직도_구조_모델링
- Iterator_패턴과_결합
- Visitor_패턴과_결합
- 성능_최적화_캐싱
- 실무_활용_체크리스트
1. Component_인터페이스_정의
개요
Composite Pattern의 기본이 되는 Component 인터페이스를 정의합니다. 모든 Leaf와 Composite가 구현해야 할 공통 메서드를 선언합니다.
코드 예제
interface Component {
getName(): string;
getSize(): number;
display(depth: number): void;
}
설명
Component 인터페이스는 Leaf와 Composite 모두가 구현해야 할 공통 연산을 정의하여 클라이언트가 일관된 방식으로 객체를 다룰 수 있게 합니다.
2. Leaf_클래스_구현
개요
트리 구조의 말단 노드인 Leaf 클래스를 구현합니다. 자식을 가질 수 없는 단일 객체를 표현합니다.
코드 예제
class File implements Component {
constructor(private name: string, private size: number) {}
getName(): string { return this.name; }
getSize(): number { return this.size; }
display(depth: number): void {
console.log(`${" ".repeat(depth)}📄 ${this.name} (${this.size}KB)`);
}
}
설명
File 클래스는 파일 시스템의 파일을 표현하며, 자식을 가지지 않고 자신의 정보만 반환하는 Leaf 노드입니다.
3. Composite_클래스_기본_구조
개요
자식 Component들을 관리하는 Composite 클래스를 구현합니다. 자식들을 추가/제거하고 일괄 처리할 수 있습니다.
코드 예제
class Folder implements Component {
private children: Component[] = [];
constructor(private name: string) {}
add(component: Component): void {
this.children.push(component);
}
remove(component: Component): void {
const index = this.children.indexOf(component);
if (index !== -1) this.children.splice(index, 1);
}
}
설명
Folder 클래스는 여러 파일과 하위 폴더를 포함할 수 있는 Composite 노드로, 자식 컴포넌트들을 배열로 관리합니다.
4. Composite_메서드_위임
개요
Composite는 자식들에게 작업을 위임하여 재귀적으로 처리합니다. 모든 자식의 결과를 합산하여 반환합니다.
코드 예제
class Folder implements Component {
// ... 이전 코드
getName(): string { return this.name; }
getSize(): number {
return this.children.reduce((sum, child) =>
sum + child.getSize(), 0
);
}
}
설명
getSize()는 모든 자식 컴포넌트의 getSize()를 호출하여 합산하므로, 폴더의 전체 크기를 재귀적으로 계산할 수 있습니다.
5. 재귀적_Display_구현
개요
Composite의 display 메서드는 자신을 출력한 후 모든 자식에게 작업을 위임하여 트리 전체를 출력합니다.
코드 예제
display(depth: number): void {
console.log(`${" ".repeat(depth)}📁 ${this.name} (${this.getSize()}KB)`);
this.children.forEach(child => {
child.display(depth + 1);
});
}
설명
depth를 증가시키며 자식들을 재귀 호출하여 들여쓰기가 적용된 트리 구조를 시각적으로 표현합니다.
6. 클라이언트_코드_활용
개요
클라이언트는 Component 인터페이스를 통해 Leaf와 Composite를 동일하게 다룹니다. 복잡한 트리 구조도 간단히 생성할 수 있습니다.
코드 예제
const root = new Folder("프로젝트");
const src = new Folder("src");
src.add(new File("index.ts", 15));
src.add(new File("app.ts", 25));
root.add(src);
root.add(new File("README.md", 5));
root.display(0);
console.log(`총 크기: ${root.getSize()}KB`);
설명
클라이언트는 Folder와 File의 차이를 의식하지 않고 Component 인터페이스를 통해 일관되게 객체를 조작할 수 있습니다.
7. UI_컴포넌트_적용_예제
개요
Composite Pattern은 UI 컴포넌트 트리 구조에도 적용할 수 있습니다. Container와 Element를 동일하게 다룹니다.
코드 예제
class Container implements UIComponent {
private children: UIComponent[] = [];
render(): string {
return `<div>${this.children.map(c =>
c.render()
).join('')}</div>`;
}
}
class Button implements UIComponent {
render(): string { return '<button>Click</button>'; }
}
설명
Container는 여러 자식 컴포넌트를 포함하고, render() 호출 시 모든 자식의 render()를 재귀적으로 호출하여 전체 UI를 생성합니다.
8. 조직도_구조_모델링
개요
회사 조직도처럼 계층적 구조를 Composite Pattern으로 모델링할 수 있습니다. 팀과 개인을 동일하게 처리합니다.
코드 예제
class Team implements Employee {
private members: Employee[] = [];
getSalary(): number {
return this.members.reduce((sum, m) =>
sum + m.getSalary(), 0
);
}
}
class Developer implements Employee {
getSalary(): number { return 5000; }
}
설명
Team은 팀원들의 급여를 합산하고, Developer는 개인 급여를 반환하므로 전체 조직의 급여를 쉽게 계산할 수 있습니다.
9. Iterator_패턴과_결합
개요
Composite Pattern에 Iterator를 추가하면 트리 구조를 다양한 방식(전위, 후위, 레벨 순회)으로 순회할 수 있습니다.
코드 예제
class Folder implements Component {
*[Symbol.iterator](): Iterator<Component> {
yield this;
for (const child of this.children) {
if ('children' in child) yield* child;
else yield child;
}
}
}
for (const item of root) console.log(item.getName());
설명
Generator 함수를 사용하여 Composite 전체를 순회할 수 있는 반복자를 구현하면, for...of 문으로 모든 노드를 간편하게 탐색할 수 있습니다.
10. Visitor_패턴과_결합
개요
Visitor Pattern을 함께 사용하면 Composite 구조를 변경하지 않고 새로운 연산을 추가할 수 있습니다.
코드 예제
interface Visitor {
visitFile(file: File): void;
visitFolder(folder: Folder): void;
}
class SizeCalculator implements Visitor {
private total = 0;
visitFile(file: File): void { this.total += file.getSize(); }
visitFolder(folder: Folder): void { /* traverse */ }
}
설명
Visitor를 사용하면 Component 클래스들을 수정하지 않고도 새로운 기능(크기 계산, 검색, 필터링 등)을 외부에서 추가할 수 있습니다.
11. 성능_최적화_캐싱
개요
Composite에서 반복적으로 계산되는 값은 캐싱하여 성능을 최적화할 수 있습니다. 자식이 변경될 때만 캐시를 무효화합니다.
코드 예제
class Folder implements Component {
private cachedSize: number | null = null;
getSize(): number {
if (this.cachedSize === null) {
this.cachedSize = this.children.reduce((sum, c) =>
sum + c.getSize(), 0
);
}
return this.cachedSize;
}
}
설명
캐싱을 통해 대규모 트리에서 반복적인 재귀 호출을 방지하여 성능을 크게 개선할 수 있습니다. add/remove 시 캐시를 null로 초기화해야 합니다.
12. 실무_활용_체크리스트
개요
Composite Pattern을 실무에 적용할 때 고려해야 할 핵심 사항들을 정리합니다.
코드 예제
// ✅ Component 인터페이스 통일
// ✅ Leaf와 Composite 명확히 구분
// ✅ 재귀 호출 최적화 (캐싱, 깊이 제한)
// ✅ 자식 관리 메서드 (add/remove/getChildren)
// ✅ 안전성 검사 (순환 참조 방지)
// ✅ 불변성 고려 (함수형 접근)
const maxDepth = 10;
if (depth > maxDepth) throw new Error('Too deep');
설명
순환 참조 방지, 깊이 제한, 캐싱 전략, 불변성 등을 고려하면 안정적이고 효율적인 Composite 구조를 구축할 수 있습니다.
마치며
이번 글에서는 Composite Pattern 실무 활용 가이드에 대해 알아보았습니다. 총 12가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.
관련 태그
#TypeScript #CompositePattern #DesignPatterns #ObjectOriented #TreeStructure