플라이웨이트 패턴이란?
많은 수의 객체를 관리하기 위해, 연관된 객체끼리 데이터를 공유하게 하면서 메모리를 절약하는 디자인 패턴이다.
주요 특징을 정리하면 다음과 같다.
- 동일하거나 유사한 객체를 공유
- 상태를 **내부 상태(공유)**와 **외부 상태(비공유)**로 분리
- 팩토리 또는 풀을 통해 객체를 중앙 관리
자세한 건 코드와 함께 이해해 보자.
코드에는 많은 클래스가 등장하므로, 코드 시나리오와 각 클래스의 역할을 간단히 소개하겠다.
어떤 숲에 10,000그루의 나무를 심어야 한다.
나무의 속성(이름, 색깔, 질감)은 몇 개 되지 않지만, 나무의 위치는 각 나무마다 다르다.
그러므로 공유 가능한 나무의 속성은 내부 상태, 나무의 위치는 외부 상태로 분리한다.
- TreeType 클래스 : 내부 상태
- Tree 클래스 : 외부 상태
또한, 객체 생성의 책임은 또다른 클래스에서 이루어지는데,
- TreeFactory 클래스 : 내부 상태의 객체 생성 책임
- Forest 클래스 : 외부 상태의 객체 생성 책임
뒤에서 보기 좋게 표를 만들어두었으니 코드를 본 뒤 정리해보자.
플라이웨이트 패턴
// 공유 가능한 속성 (내부 상태)
class TreeType {
constructor(name, color, texture) {
this.name = name;
this.color = color;
this.texture = texture;
}
display(x, y) {
console.log(`${this.name} 나무를 (${x}, ${y}) 위치에 그림. 색: ${this.color}`);
}
}
// 공유 객체를 관리하는 팩토리
class TreeFactory {
constructor() {
this.treeTypes = {};
}
getTreeType(name, color, texture) {
const key = `${name}_${color}_${texture}`;
if (!this.treeTypes[key]) { // 내부 상태 객체 생성 책임 -> 팩토리 메소드
this.treeTypes[key] = new TreeType(name, color, texture);
} // 내부 상태를 key로 구분해 캐싱 & 재사용 -> 등록형 팩토리
return this.treeTypes[key];
}
}
// 외부 상태 포함한 실제 나무 객체
class Tree {
constructor(x, y, treeType) {
this.x = x;
this.y = y;
this.treeType = treeType; // 내부 상태 공유
}
draw() {
this.treeType.display(this.x, this.y);
}
}
// 숲을 관리하는 Forest 클래스
class Forest {
constructor() {
this.trees = [];
this.factory = new TreeFactory();
}
plantTree(x, y, name, color, texture) {
const type = this.factory.getTreeType(name, color, texture);
const tree = new Tree(x, y, type); // 외부 상태 객체 생성 책임
this.trees.push(tree);
}
draw() {
this.trees.forEach(tree => tree.draw());
}
}
// 사용 예시
const forest = new Forest();
forest.plantTree(1, 2, '소나무', '초록', '거칠다');
forest.plantTree(3, 4, '소나무', '초록', '거칠다');
forest.plantTree(5, 6, '자작나무', '흰색', '매끈하다');
forest.draw();
실행 결과
소나무 나무를 (1, 2) 위치에 그림. 색: 초록
소나무 나무를 (3, 4) 위치에 그림. 색: 초록
자작나무 나무를 (5, 6) 위치에 그림. 색: 흰색
계층 구조 다이어그램

클래스별 역할
| TreeType | 🔹 내부 상태(Flyweight) | 공통 데이터(name, color, texture)만 가짐. 공유됨 |
| Tree | 🔹 외부 상태(Concrete Object) | 위치 정보(x, y)와 공유된 TreeType을 조합 |
| TreeFactory | 🔹 등록형 팩토리 (Flyweight Factory) + 팩토리 메서드 |
내부 상태를 캐싱하고, 동일한 TreeType 재사용. getTreeType()이 팩토리 메서드 역할 |
| Forest | 🔹 클라이언트(Client) | TreeFactory와 Tree를 조합해 실제 객체 생성, 외부 상태 관리 |
이점
- 메모리 절약: 10,000개의 Tree 객체를 만들더라도, TreeType은 2~3개면 충분
- 객체 수 줄이기: 공통 속성은 한 번만 정의해서 객체 수 최소화
- 성능 최적화: 대량 처리할 때 유리
객체의 생성에 주목해보면,
내부 상태의 객체 생성은 팩토리에서 하고, 외부 상태의 객체 생성은 Forest에서 한다.
이렇게 객체의 생성 책임이 나뉨으로써 유지보수와 확장성이 높아진다.
실제로 OOP(객체 지향 프로그래밍) 설계원칙 중에는,
하나의 클래스가 하나의 책임만 가지도록 하는 SRP(단일 책임 원칙)이 있다.
객체 지향 설계를 위해서, 객체 생성 책임은 공통 vs 개별로 나눠서 위임하는게 효율적이며,
오늘 살펴본 플라이웨이트 패턴이 그 대표적인 예시라고 볼 수 있다.
'프론트엔드 > TAVE-15기' 카테고리의 다른 글
| [React Native] npm 패키지 의존성 충돌(query-string) 해결하기 (0) | 2025.09.04 |
|---|---|
| 웨더타고 - TAVE 15기 연합프로젝트 프론트엔드 회고 (2) | 2025.08.03 |
| [디자인 패턴] 팩토리 패턴(Factory Pattern) - 심플 팩토리, 등록형 팩토리, 팩토리 메소드 (0) | 2025.04.10 |
| [디자인 패턴] 싱글톤 패턴(Singleton Pattern) (0) | 2025.03.31 |