플라이웨이트 flyweight 패턴 - 자바스크립트 예제
요약
플라이웨이트(Flyweight) 패턴을 자바스크립트 코드와 함께 알아봅니다. 정보처리기사 대비 문제가 포함되어있습니다.
플라이웨이트 패턴 요약
패턴 종류 | 핵심 키워드 |
---|---|
플라이웨이트 (Flyweight) | 객체 공유 , 가상 인스턴스 제공, 메모리 사용량 줄입니다. |

플라이웨이트 (Flyweight) 패턴
플라이웨이트 패턴은 많은 수의 객체를 효율적으로 지원하기 위해 객체의 공유를 통해 메모리 사용량을 줄이는 디자인 패턴입니다. '플라이웨이트'는 권투의 경량급을 의미하며, 이름처럼 객체를 가볍게 만들어 메모리 부담을 최소화하는 데 목적이 있습니다.
이 패턴은 객체의 상태를 두 가지로 분리합니다.
- 내재적 상태 (Intrinsic State): 객체 내부에서 관리되며, 여러 컨텍스트에서 공유될 수 있는 불변(immutable)의 상태입니다. 플라이웨이트 객체 내에 저장됩니다. (예: 나무의 모델, 텍스처, 색상)
- 외재적 상태 (Extrinsic State): 각 객체마다 달라질 수 있으며, 클라이언트가 관리하고 필요할 때 플라이웨이트 객체에 전달하는 상태입니다. (예: 나무의 위치(x, y 좌표), 크기)
기본 구조
- Flyweight: 공유될 객체들의 인터페이스를 정의합니다.
- ConcreteFlyweight:
Flyweight
인터페이스를 구현하고, 내재적 상태를 저장합니다. 이 객체는 공유되어 재사용됩니다. - FlyweightFactory: 플라이웨이트 객체들을 생성하고 관리하는 팩토리입니다. 클라이언트가 요청한 플라이웨이트가 이미 존재하면 기존 객체를 반환하고, 없으면 새로 생성하여 풀(pool)에 저장합니다.
- Client: 외재적 상태를 관리하고,
FlyweightFactory
를 통해 플라이웨이트 객체를 받아 필요한 작업을 수행합니다.
예시: 게임에서 수많은 나무 렌더링하기
수천, 수만 그루의 나무로 숲을 표현해야 하는 게임을 만든다고 가정해 봅시다. 각 나무마다 모델, 텍스처 등 무거운 데이터를 가진 객체를 생성한다면 엄청난 메모리가 소모될 것입니다. 플라이웨이트 패턴을 사용하면 이 문제를 효과적으로 해결할 수 있습니다.
먼저, 공유될 나무 모델(TreeModel
)을 정의합니다.
// Flyweight: 공유될 나무 모델
class TreeModel {
constructor(mesh, texture) {
// 내재적 상태 (공유 가능, 불변)
this.mesh = mesh; // 3D 모델 데이터
this.texture = texture; // 표면 질감 데이터
console.log(
`[모델 로딩] ${this.mesh}와 ${this.texture}를 로드했습니다. (메모리 소모 큼)`
);
}
// 외재적 상태를 받아 화면에 그리는 메서드
draw(x, y, scale) {
console.log(
`(${x}, ${y}) 위치에 ${scale} 크기로 '${this.mesh}' 나무를 그립니다.`
);
}
}
이제 TreeModel
객체를 관리하는 TreeModelFactory
를 만듭니다.
// FlyweightFactory: 나무 모델 팩토리
class TreeModelFactory {
constructor() {
this.models = {};
}
getModel(mesh, texture) {
const key = `${mesh}-${texture}`;
if (!this.models[key]) {
this.models[key] = new TreeModel(mesh, texture);
} else {
console.log(`[팩토리] 기존 모델 재사용: ${key}`);
}
return this.models[key];
}
getModelCount() {
return Object.keys(this.models).length;
}
}
클라이언트(게임 엔진) 코드에서 팩토리를 통해 나무를 생성하고 화면에 그립니다.
// Client: 게임 엔진
class Forest {
constructor() {
this.trees = [];
this.factory = new TreeModelFactory();
}
// 숲에 나무 심기
plantTree(x, y, scale, mesh, texture) {
const model = this.factory.getModel(mesh, texture);
// 외재적 상태와 공유 모델을 함께 저장
this.trees.push({ x, y, scale, model });
}
// 숲 전체를 그리기
render() {
this.trees.forEach(tree => {
tree.model.draw(tree.x, tree.y, tree.scale);
});
}
}
// 실행
const forest = new Forest();
console.log("--- 숲 생성 시작 ---");
forest.plantTree(10, 20, 1.0, "소나무", "pine_texture.png");
forest.plantTree(50, 80, 1.2, "참나무", "oak_texture.png");
forest.plantTree(100, 30, 1.0, "소나무", "pine_texture.png");
forest.plantTree(200, 150, 1.1, "소나무", "pine_texture.png");
console.log("--- 숲 생성 완료 ---");
console.log("
--- 숲 렌더링 시작 ---");
forest.render();
console.log("--- 숲 렌더링 완료 ---");
console.log(`
총 생성된 나무 모델 객체 수: ${forest.factory.getModelCount()}`);
출력 결과:
--- 숲 생성 시작 ---
[모델 로딩] 소나무와 pine_texture.png를 로드했습니다. (메모리 소모 큼)
[모델 로딩] 참나무와 oak_texture.png를 로드했습니다. (메모리 소모 큼)
[팩토리] 기존 모델 재사용: 소나무-pine_texture.png
[팩토리] 기존 모델 재사용: 소나무-pine_texture.png
--- 숲 생성 완료 ---
--- 숲 렌더링 시작 ---
(10, 20) 위치에 1 크기로 '소나무' 나무를 그립니다.
(50, 80) 위치에 1.2 크기로 '참나무' 나무를 그립니다.
(100, 30) 위치에 1 크기로 '소나무' 나무를 그립니다.
(200, 150) 위치에 1.1 크기로 '소나무' 나무를 그립니다.
--- 숲 렌더링 완료 ---
총 생성된 나무 모델 객체 수: 2
숲에 4그루의 나무를 심었지만, 실제로 메모리를 많이 차지하는 TreeModel
객체는 '소나무'와 '참나무' 단 2개만 생성되었습니다. '소나무' 모델은 한 번 생성된 후 계속 재사용되었습니다. 각 나무의 고유한 정보(위치, 크기 등)는 외재적 상태로 클라이언트가 직접 관리합니다.
이처럼 플라이웨이트 패턴은 공유 가능한 상태(내재적 상태)를 가진 객체를 재사용하여 메모리 사용량을 획기적으로 줄일 수 있습니다.
'가상 인스턴스'는 무슨 의미일까?
"여러 개의 가상 인스턴스를 제공해서 메모리를 절감한다"는 표현은 플라이웨이트 패턴의 핵심을 잘 나타내지만, '가상 인스턴스'라는 용어 때문에 헷갈릴 수 있습니다.
'고무 도장' 비유를 통해 쉽게 이해해 봅시다.
수천 페이지 분량의 문서에 '소나무' 그림을 계속 찍어야 한다고 상상해 보세요.
- 플라이웨이트 패턴 미적용: 나무 1,000그루를 찍으려면, 실제 '소나무' 도장 1,000개가 필요합니다. 도장을 보관할 엄청나게 큰 공간(메모리)이 필요합니다.
- 플라이웨이트 패턴 적용:
- '소나무' 모양의 고무 도장 딱 하나만 만듭니다. 이 도장이 바로 플라이웨이트 객체이며, '소나무'라는 본질적인 모양(내재적 상태)을 가집니다.
- 나무를 찍을 때마다 이 유일한 '소나무' 도장을 재사용합니다.
- 대신, 나무마다 달라지는 정보, 즉 어디에(x, y 좌표), 어떤 크기로 찍을지는 도장을 찍는 순간에 외부에서 알려줍니다. 이것이 바로 외재적 상태입니다.
여기서 '가상 인스턴스' 란, 우리 눈에 보이는 각각의 '소나무' 그림을 의미합니다. 실제 메모리에는 '소나무' 모양을 가진 객체(도장)는 단 하나만 존재하지만, [공유되는 실제 객체 1개] + [각기 다른 외부 상태(좌표, 크기)] 의 조합으로 마치 여러 개의 독립된 객체가 있는 것처럼 '보여주는' 것입니다.
게임 예제에서
TreeModel
객체는 단 2개('소나무', '참나무')만 생성되었지만, 화면에는 4그루의 나무, 즉 4개의 '가상 인스턴스'가 보이는 것과 같습니다.
플라이웨이트 패턴 중요 키워드
- 객체 공유와 가상 인스턴스 제공을 통해 메모리 사용량을 줄입니다.
- 객체의 상태를 내재적 상태(공유 가능) 와 외재적 상태(공유 불가) 로 분리합니다.
- 팩토리를 사용하여 공유 객체를 관리합니다.
- 게임, 문서 편집기 등 대량의 객체를 효율적으로 다뤄야 할 때 매우 유용합니다.
정처기 실기 대비 문제
문제 | 다수의 객체를 생성할 경우, 모두가 갖는 본질적인 요소를 클래스 화하여 공유함으로써 메모리를 절약하고, '클래스의 경량화'를 목적으로 하는 디자인 패턴은? |
보기 | |
답변 | |
정답 | 정답 보기 |