이 글은 해당 유튜브를 보고 진행한 것에 대해 공부한 것을 정리한 글입니다.
오브젝트 풀 (Object Pool)
오브젝트 풀링은 반복적으로 생성과 제거가 필요한 게임 오브젝트를 미리 생성해 비활성화 상태로 보관해두고, 필요할 때 재사용하는 기법입니다.
엄밀히 말하면 GoF 디자인 패턴에는 포함되지 않지만, 게임 개발에서는 사실상 필수적인 최적화 패턴으로 널리 쓰입니다.

주요 특징
- 빠른 재사용
- 매번 오브젝트를 생성하지 않고, "Pool"에서 준비된 상태로 대기 중인 초기화된 오브젝트를 사용하므로 속도가 빠릅니다.
- GC (가비지 콜렉션) 절감
- 반복적인 생성 / 파괴로 인한 메모리 파편화 및 GC 스파이크를 방지할 수 있습니다.
- 사전 로딩 가능
- 로딩 화면 중 필요한 수만큼 미리 생성해두면, 런타임 성능 향상에 기여합니다.
장점
- 성능 향상
- 메모리 할당 및 해제를 줄이므로 게임의 성능을 향상할 수 있습니다.
- 예측 가능한 동작
- 오브젝트 생성과 제거가 일정하므로 디버깅과 관리가 용이합니다.
- 재사용성
- 오브젝트가 파괴되지 않고 계속 사용되므로 자원 낭비를 줄일 수 있습니다.
장점
- 초기 메모리 비용
- 풀을 미리 생성하므로 초기 메모리를 선점하게 됩니다.
- 복잡도 증가
- 객체의 상태 초기화, 반환 처리 등 로직이 추가로 필요합니다.
- 메모리 낭비 가능성
- 너무 많은 오브젝트가 풀에 남아있으면 오히려 불필요한 메모리 소모로 이어질 수 있습니다.
개선 방안
정적 또는 싱글톤 패턴 적용
- 다양한 소스에서 풀링된 오브젝트를 생성해야 하는 경우에 Singleton 또는 static 풀 매니저를 사용하여 어디서든 접근하는 것이 가능합니다.
- 단, static 클래스를 사용하면 Unity Inspector를 통한 조작이 불가능하므로 주의가 필요합니다.
Dictionary 기반 다중 풀 관리
Dictionary<int, Queue<GameObject>> objectPools;
- 다양한 프리팹을 풀링할 경우, Dictionary<Key, Object Pool> 형태로 프리팹별 풀을 관리하면 효율적입니다.
- 프리팹의 InstanceID 또는 고유 이름을 Key 값으로 사용하면 됩니다.
잘못된 반환 방지
- 이미 풀에 있는 오브젝트를 다시 반환하려 하면 런타임 오류가 발생할 수 있습니다.
- 반환 전 오브젝트 상태를 체크하거나, 풀 내부에서 중복 여부를 검사해야 합니다.
최대 개수 제한 설정
- 풀에 들어갈 수 있는 최대 수량을 제한해 메모리 폭증을 방지합니다.
- 초과할 경우 Destroy( )하거나 가장 오래된 항목을 제거하는 등의 전략을 사용합니다.
구현 예시 : 기본 Object Pool
Object Pool 클래스
public class ObjectPool : MonoBehaviour
{
[SerializeField] private uint initPoolSize;
[SerializeField] private PooledObject objectToPool;
// 풀링된 오브젝트를 컬렉션에 저장
private Stack<PooledObject> stack;
private void Start()
{
SetupPool();
}
// 풀 생성(지연을 인지할 수 없을 때 호출)
private void SetupPool()
{
stack = new Stack<PooledObject>();
PooledObject instance = null;
for (int i = 0; i < initPoolSize; i++)
{
instance = Instantiate(objectToPool);
instance.Pool = this;
instance.gameObject.SetActive(false);
stack.Push(instance);
}
}
// 풀에서 첫 번째 액티브 게임 오브젝트를 반환합니다.
public PooledObject GetPooledObject()
{
// 풀이 충분히 크지 않으면 새로운 PooledObjects를 인스턴스화합니다.
if (stack.Count == 0)
{
PooledObject newInstance = Instantiate(objectToPool);
newInstance.Pool = this;
return newInstance;
}
// 그렇지 않으면 목록에서 다음 항목을 가져옵니다.
PooledObject nextInstance = stack.Pop();
nextInstance.gameObject.SetActive(true);
return nextInstance;
}
public void ReturnToPool(PooledObject pooledObject)
{
stack.Push(pooledObject);
pooledObject.gameObject.SetActive(false);
}
}
PoolObject 클래스
public class PooledObject : MonoBehaviour
{
private ObjectPool pool;
public ObjectPool Pool { get => pool; set => pool = value; }
public void Release()
{
pool.ReturnToPool(this);
}
}
구현 예시

Bullet Object Pool
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace DesignPatterns.ObjectPool
// This is an example client that uses our simple object pool.
public class ExampleGun : MonoBehaviour
{
[Tooltip("Prefab to shoot")]
[SerializeField] private GameObject projectile;
[Tooltip("Projectile force")]
[SerializeField] float muzzleVelocity = 700f;
[Tooltip("End point of gun where shots appear")]
[SerializeField] private Transform muzzlePosition;
[Tooltip("Time between shots / smaller = higher rate of fire")]
[SerializeField] float cooldownWindow = 0.1f;
[Tooltip("Reference to Object Pool")]
[SerializeField] ObjectPool objectPool;
private float nextTimeToShoot;
private void FixedUpdate()
{
// shoot if we have exceeded delay
if (Input.GetButton("Fire1") && Time.time > nextTimeToShoot && objectPool != null)
{
// get a pooled object instead of instantiating
GameObject bulletObject = objectPool.GetPooledObject().gameObject;
if (bulletObject == null)
return;
bulletObject.SetActive(true);
// align to gun barrel/muzzle position
bulletObject.transform.SetPositionAndRotation(muzzlePosition.position, muzzlePosition.rotation);
// move projectile forward
bulletObject.GetComponent<Rigidbody>().AddForce(bulletObject.transform.forward * muzzleVelocity, ForceMode.Acceleration);
// turn off after a few seconds
ExampleProjectile projectile = bulletObject.GetComponent<ExampleProjectile>();
projectile ?. Deactivate();
// set cooldown delay
nextTimeToShoot = Time.time + cooldownWindow;
// ...
}
// ...
}
UnityEngine.Pool
Unity 2021 이상 버전부터는 UnityEngine.Pool 네임스페이스가 도입되어, 직접 Object Pool을 만들 필요 없이 빌트인하여 사용할 수 있습니다.
장점
- 별도의 관리 코드 없이 효율적인 풀 관리가 가능합니다.
- ObjectPool<T>, CollectionPool<TCollection, TItem> 등의 제네릭 클래스로 확장성 높습니다.
예시
RevisedGun.cs
using UnityEngine.Pool;
public class RevisedGun : MonoBehaviour
{
// …
// Unity 2021 이상 버전에서 사용 가능한 스택 기반 ObjectPool
private IObjectPool<RevisedProjectile> objectPool;
// 이미 풀에 있는 기존 항목을 반환하려 할 때 예외를 반환
[SerializeField] private bool collectionCheck = true;
// 풀의 용량과 최대 크기를 제어하는 추가 옵션
[SerializeField] private int defaultCapacity = 20;
[SerializeField] private int maxSize = 100;
private void Awake()
{
objectPool = new ObjectPool<RevisedProjectile>(CreateProjectile,
OnGetFromPool, OnReleaseToPool, OnDestroyPooledObject,
collectionCheck, defaultCapacity, maxSize);
}
// 오브젝트 풀을 채울 항목을 만들 때 호출됨
private RevisedProjectile CreateProjectile()
{
RevisedProjectile projectileInstance = Instantiate(projectilePrefab);
projectileInstance.ObjectPool = objectPool;
return projectileInstance;
}
// 오브젝트 풀로 항목을 반환할 때 호출됨
private void OnReleaseToPool(RevisedProjectile pooledObject)
{
pooledObject.gameObject.SetActive(false);
}
// 오브젝트 풀에서 다음 항목을 검색할 때 호출됨
private void OnGetFromPool(RevisedProjectile pooledObject)
{
pooledObject.gameObject.SetActive(true);
}
// 풀링된 항목의 최대 개수를 초과할 때 호출됨(풀링된 오브젝트 파괴)
private void OnDestroyPooledObject(RevisedProjectile pooledObject)
{
Destroy(pooledObject.gameObject);
}
private void FixedUpdate()
{
// …
}
}
RevisedGun 클래스는 오브젝트 풀을 생성하고 관리하며, 풀에서 발사체를 꺼내거나 반환하는 주체입니다.
RevisedProjectile.cs
public class RevisedProjectile : MonoBehaviour
{
// …
private IObjectPool<RevisedProjectile> objectPool;
// 발사체에 ObjectPool에 대한 레퍼런스를 제공하는 공용 프로퍼티
public IObjectPool<RevisedProjectile> ObjectPool { set => objectPool = value; }
// …
}
RevisedProjectile 클래스는 발사체의 개별 동작을 담당하며, 자신이 속한 오브젝트 풀에 대한 레퍼런스를 보관합니다.
ObjectPool<T> 클래스
public class ObjectPool<T>: IDisposable, IObjectPool<T> where T : class
{
internal readonly List<T> m_List;
private readonly Func<T> m_CreateFunc;
private readonly Action<T> m_ActionOnGet;
private readonly Action<T> m_ActionOnRelease;
private readonly Action<T> m_ActionOnDestroy;
// ...
}
UnityEngine.Pool 내부 구현된 ObjectPool<T> 클래스입니다.
'Unity' 카테고리의 다른 글
| [Unity] MVC, MVP, MVVM 패턴 (0) | 2025.06.17 |
|---|---|
| [Unity] 디자인 패턴 : Singleton 패턴 (0) | 2025.06.17 |
| [Unity] 디자인 패턴 : Factory 패턴 (0) | 2025.06.16 |
| [Unity] 디자인 패턴 : Command 패턴 (0) | 2025.06.13 |
| [Unity] 디자인 패턴 : Observer 패턴 (2) | 2025.06.13 |