반응형
프로그램을 만들다 보면 수천, 수만 개의 객체를 생성해야 할 때가 있습니다. 예를 들어 숲을 표현하는 게임에서 나무 한 그루를 객체로 만든다면, 나무 1만 그루를 심는 순간 메모리는 순식간에 가득 차버릴 것입니다. 이때 필요한 것이 바로 플라이웨이트 패턴(Flyweight Pattern)입니다.
오늘은 중복된 객체를 공유하여 메모리 효율을 극대화하는 플라이웨이트 패턴에 대해 깊이 있게 알아보겠습니다.

1. 플라이웨이트 패턴이란?
플라이웨이트 패턴은 "가능한 한 객체를 공유하여 메모리 소비를 줄이는" 구조 패턴입니다. 모든 데이터를 객체 내부에 저장하는 대신, 공통적인 부분은 공유하고 개별적인 부분만 외부에서 주입받는 방식을 취합니다.
핵심 개념: 두 가지 상태의 분리
패턴을 이해하기 위해 가장 중요한 두 가지 용어가 있습니다.
- 내재적 상태 (Intrinsic State): 객체 내부에 고정되어 공유할 수 있는 데이터 (예: 나무의 텍스트와 모델 데이터)
- 외재적 상태 (Extrinsic State): 객체 외부에 있으며 상황에 따라 변하는 데이터 (예: 나무의 위치 좌표 x, y)
2. 플라이웨이트 패턴의 구조
이 패턴은 객체를 직접 생성하지 않고 '팩토리(Factory)'를 거치는 것이 핵심입니다.
- Flyweight: 공유 객체의 인터페이스입니다.
- ConcreteFlyweight: 실제로 공유되는 데이터(내재적 상태)를 가진 객체입니다.
- FlyweightFactory: 객체를 캐싱하고 관리합니다. 이미 있으면 기존 것을 반환하고, 없으면 새로 생성합니다.
- Client: 팩토리에 객체를 요청하고, 외재적 상태(좌표 등)를 주입하여 기능을 실행합니다.
3. Java 구현 예제: 게임 속 나무 심기
나무의 모양(Model)은 공유하고, 각 나무의 위치(X, Y)만 다르게 설정하는 예제입니다.
Step 1. Flyweight 객체 정의
Java
// 내재적 상태: 나무의 모양과 색상은 공유됩니다.
class TreeType {
private String name;
private String color;
public TreeType(String name, String color) {
this.name = name;
this.color = color;
}
public void draw(int x, int y) {
System.out.println(String.format("%s 나무(%s)를 [%d, %d] 위치에 그립니다.", name, color, x, y));
}
}
Step 2. FlyweightFactory (캐싱 관리)
Java
import java.util.HashMap;
import java.util.Map;
class TreeFactory {
private static final Map<String, TreeType> treeTypes = new HashMap<>();
public static TreeType getTreeType(String name, String color) {
String key = name + "_" + color;
if (!treeTypes.containsKey(key)) {
treeTypes.put(key, new TreeType(name, color));
System.out.println("== 새로운 나무 타입 생성: " + name + " ==");
}
return treeTypes.get(key);
}
}
Step 3. Client 실행 코드
Java
public class ForestGame {
public static void main(String[] args) {
// 1만 개의 나무를 심지만, 실제 생성되는 TreeType 객체는 단 2개!
for (int i = 0; i < 5000; i++) {
TreeFactory.getTreeType("오크나무", "녹색").draw(i, i * 2);
TreeFactory.getTreeType("단풍나무", "빨간색").draw(i * 3, i);
}
System.out.println("나무 심기 완료!");
}
}
4. 플라이웨이트 패턴의 장단점
👍 장점
- 메모리 절감: 중복 객체를 생성하지 않으므로 메모리 사용량을 획기적으로 줄입니다.
- 성능 향상: 객체 생성 횟수가 줄어들어 가비지 컬렉션(GC)의 부담이 줄어듭니다.
👎 단점
- 복잡도 증가: 상태를 분리하고 팩토리를 관리하는 로직이 추가됩니다.
- 런타임 비용: 외재적 상태를 매번 계산하거나 주입해야 하므로 약간의 CPU 연산 시간이 추가될 수 있습니다.
5. 실무에서의 활용: Java의 비밀
우리가 매일 쓰는 자바 언어 안에도 이 패턴이 숨어 있습니다.
- String Constant Pool: 동일한 문자열 리터럴은 메모리에 하나만 저장됩니다.
- Integer.valueOf(int): -128에서 127 사이의 숫자는 미리 캐싱된 객체를 반환합니다.
결론
플라이웨이트 패턴은 "객체의 양보다 질"을 추구하는 패턴입니다. 시스템 리소스가 제한적인 환경이나 성능 최적화가 필수적인 프로젝트라면 플라이웨이트 패턴은 선택이 아닌 필수입니다.
도움이 되셨다면 공감과 구독 부탁드립니다! 기술적인 질문이나 의견은 언제든 댓글로 남겨주세요.
반응형
'Mobile & App Stack > Java Software Architecture & Patterns' 카테고리의 다른 글
| 옵저버 패턴(Observer) 완벽 정리: Java 예제와 Deprecated 대안 (0) | 2025.01.01 |
|---|---|
| 전략 패턴(Strategy Pattern) 완벽 정리: if-else 지옥에서 탈출하기 (0) | 2024.12.30 |
| 브리지 패턴(Bridge Pattern) 완벽 정리: 클래스 폭발을 막는 설계의 기술 (0) | 2024.12.28 |
| 퍼사드 패턴(Facade Pattern) 완벽 정리: 복잡한 시스템을 단순하게 만드는 마법 (0) | 2024.12.27 |
| 프록시 패턴(Proxy Pattern) 완벽 정리: 제어와 성능 최적화의 핵심 (0) | 2024.12.26 |