학습일지/언리얼

오브젝트 풀링(Object Pooling)

Tsukino Ren 2026. 5. 29. 21:15

오늘 배운 개념

오브젝트 풀링(Object Pooling)은 자주 생성되고 삭제되는 객체를 미리 생성해두고 재사용하는 메모리 및 성능 최적화 기법이다.

게임에서는 총알, 이펙트, 몬스터, UI 같은 객체가 반복적으로 생성되고 제거된다.
이때 매번 Spawn과 Destroy를 반복하지 않고, 비활성화 후 다시 재사용하는 방식으로 동작한다.


왜 사용하는가

게임에서 객체 생성과 삭제는 생각보다 비용이 크다.

객체를 생성할 때는:

  • 메모리 할당
  • 컴포넌트 초기화
  • 등록 처리

등이 필요하다.

삭제 시에도:

  • 메모리 정리
  • 참조 제거
  • Garbage Collection 처리

과정이 발생한다.

이 작업이 반복되면 프레임 드랍이나 순간적인 렉(Stutter)이 발생할 수 있다.

오브젝트 풀링은 이런 비용을 줄이기 위해 사용한다.


어떤 문제를 해결하는가

Spawn / Destroy 반복 문제

총알처럼 짧은 시간에 대량 생성되는 객체는 Spawn과 Destroy 비용이 매우 커질 수 있다.

예시:

  • 총알 연사
  • 폭발 이펙트
  • 몬스터 웨이브
  • 데미지 숫자 UI

이런 상황에서 성능 저하가 발생한다.


Garbage Collection 부하 문제

언리얼 엔진은 UObject 기반이라 삭제된 객체를 Garbage Collector가 정리한다.

하지만 객체가 너무 자주 생성되고 삭제되면:

  • GC 호출 증가
  • CPU 부하 증가
  • 프레임 끊김

문제가 발생할 수 있다.


프레임 안정성 문제

실시간 게임에서는 평균 FPS보다 프레임 안정성이 더 중요하다.

Spawn 순간마다 CPU 사용량이 튀면:

  • 프레임 드랍
  • 입력 지연
  • 끊기는 느낌

이 발생할 수 있다.

오브젝트 풀링은 이런 순간적인 비용을 줄여준다.


내부 동작 원리

기본 구조는 다음과 같다.

게임 시작
↓
객체 여러 개 미리 생성
↓
비활성 상태로 저장
↓
필요 시 활성화해서 사용
↓
사용 종료 후 비활성화
↓
다시 풀(Pool)로 반환

즉:

Create → Destroy

구조가 아니라

Create → Reuse

구조로 동작한다.

핵심은 "삭제하지 않고 재사용"하는 것이다.


실제 사용 예시

총알 시스템

기존 방식:

발사
→ Bullet Spawn
→ 충돌
→ Destroy

문제:

  • 연사 시 객체 생성 비용 증가
  • GC 부하 증가

오브젝트 풀링 방식:

게임 시작 시 총알 100개 생성
↓
발사 시 비활성 총알 가져오기
↓
활성화 후 사용
↓
충돌 시 비활성화
↓
다시 Pool로 반환

장점:

  • Spawn 비용 감소
  • 프레임 안정화
  • GC 감소

파티클 / 이펙트

폭발 효과나 히트 이펙트는 짧은 시간 동안 자주 생성된다.

따라서:

  • Explosion Pool
  • Hit Effect Pool

형태로 관리하는 경우가 많다.


UI 시스템

언리얼에서 Widget 생성도 비용이 발생한다.

예시:

  • 데미지 텍스트
  • 채팅 로그
  • 알림 메시지

등도 재사용 가능한 구조로 만들 수 있다.


언리얼에서의 일반적인 구현 방식

보통 Pool Manager를 만들어 관리한다.

ObjectPoolManager
 ├── Bullet Pool
 ├── Effect Pool
 └── Enemy Pool

주로 사용하는 자료구조:

  • TArray
  • Queue
  • ActorComponent

등이 있다.


주요 함수 예시

GetObjectFromPool()
ReturnObjectToPool()

Get 처리 흐름

비활성 객체 탐색
→ 활성화
→ 위치 및 상태 초기화
→ 반환

Return 처리 흐름

사용 종료
→ 비활성화
→ Pool로 반환

장점

성능 향상

가장 큰 장점이다.

  • Spawn 비용 감소
  • Destroy 비용 감소
  • GC 감소

효과를 얻을 수 있다.


프레임 안정화

실시간 게임에서 매우 중요하다.

특히:

  • FPS 게임
  • 탄막 게임
  • 모바일 게임

에서 효과가 크다.


메모리 관리 효율 증가

객체 수를 제한할 수 있다.

예시:

총알 최대 200발

처럼 관리 가능하다.


단점

메모리 사용량 증가

객체를 미리 생성하기 때문에 사용하지 않아도 메모리를 차지한다.


초기 설계가 복잡함

단순 Spawn / Destroy보다 구현 난이도가 높다.

필요한 작업:

  • 활성 상태 관리
  • 초기화 처리
  • 반환 로직 관리

상태 초기화 실수 가능

재사용 구조라 이전 상태가 남을 수 있다.

예시:

  • Velocity 초기화 안됨
  • HP 남아있음
  • 파티클 상태 유지

이런 버그가 자주 발생한다.


헷갈리기 쉬운 부분

비활성화와 삭제는 다르다

SetActorHiddenInGame(true);
SetActorEnableCollision(false);
SetActorTickEnabled(false);

는 단순 비활성화다.

메모리에서 제거된 것이 아니다.


Destroy를 안 한다고 메모리 누수가 아니다

오브젝트 풀링은 의도적으로 객체를 유지하는 구조다.

즉:

  • 메모리 누수
  • 재사용 구조

는 서로 다른 개념이다.


모든 객체를 풀링할 필요는 없다

생성 빈도가 낮은 객체는 굳이 풀링하지 않아도 된다.

예시:

  • 보스 몬스터
  • 컷신 오브젝트
  • 드물게 생성되는 Actor

실무에서 중요한 이유

실무 게임 개발에서는 프레임 안정성이 매우 중요하다.

특히:

  • 모바일 게임
  • 대규모 전투
  • 멀티플레이
  • 탄막 슈팅

에서는 Spawn / Destroy 비용이 치명적일 수 있다.

그래서 실제 프로젝트에서는:

  • Bullet Pool
  • Effect Pool
  • UI Pool

등을 거의 기본적으로 사용한다.


실무에서 자주 하는 추가 최적화

Tick 비활성화

풀에 반환 시:

SetActorTickEnabled(false);

처리해 불필요한 Tick 비용을 제거한다.


Collision 비활성화

SetActorEnableCollision(false);

처리로 충돌 계산 비용을 줄인다.


Lazy Expansion

초기에는 일정 개수만 생성하고 부족할 때만 추가 생성하는 방식이다.

장점:

  • 메모리 절약
  • 초기 로딩 부담 감소

헷갈리는 개념 비교

Object Pooling vs Garbage Collection

Object Pooling:

  • 객체 재사용 목적
  • 생성 / 삭제 최소화

Garbage Collection:

  • 사용하지 않는 객체 메모리 정리

서로 목적이 다르다.


Object Pooling vs Singleton

Object Pooling:

  • 여러 객체 관리

Singleton:

  • 객체 하나만 유지

완전히 다른 패턴이다.


마무리

오브젝트 풀링은:

"자주 사용하는 객체를
삭제하지 않고 재사용해
성능을 최적화하는 기법"

 

핵심은 단순히 "미리 만들어둔다"가 아닌

  • 왜 Spawn 비용이 큰지
  • 왜 GC가 문제인지
  • 왜 프레임이 튀는지

까지 이해하는 것이중요하다는 것 같다. 게임 개발에서는 매우 자주 사용되는 핵심 최적화 기법 중 하나 라고 한다.