언리얼 엔진

[언리얼 엔진] UObject, 언리얼 객체 시스템의 기반 정리

dhlee-dev 2026. 6. 5. 22:05

언리얼 엔진에서 C++ 클래스를 만들다 보면
UObject가 여러 클래스들의 기반이 되는 최상위 클래스로 존재한다는 것을 볼 수 있다.

예를 들어 월드에 배치할 수 있는 AActor나,
Actor에 부착해서 기능을 분리하는 UActorComponentUObject를 기반으로 만들어진 클래스이다.

처음에는 UObject를 단순히 언리얼에서 사용하는 기본 클래스 정도로 생각했다.
하지만 공부해보니 UObject를 상속받으면서
언리얼 엔진의 여러 기능을 사용할 수 있다는 것을 알게 되었다.

대표적으로 리플렉션, 가비지 컬렉션, 직렬화, 블루프린트 연동, CDO 같은 개념들이 있다.

이전에 리플렉션, GC, CDO 같은 개념을 따로 정리한 적이 있기 때문에
이번 글에서는 이 기능들이 UObject와 어떻게 연결되는지 간단히 정리해보려고 한다.


UObject란?

UObject는 언리얼 엔진의 많은 C++ 클래스와 블루프린트 객체가 기반으로 사용하는 기본 클래스이다.

언리얼의 모든 C++ 클래스가 반드시 UObject를 상속받는 것은 아니다.

하지만 언리얼 엔진의 리플렉션 시스템에 등록되고 GC, 직렬화, 블루프린트 연동 같은 엔진 기능을 사용하려면,
대부분 UObject 계열 클래스를 사용하게 된다.

즉, UObject는 단순한 C++ 클래스라기보다 언리얼 엔진이 객체를 추적하고 관리하기 위한 기반 클래스라고 볼 수 있다.


UObject와 AActor의 차이

AActorUObject를 상속받는 클래스이다.
하지만 UObjectAActor는 사용 목적이 다르다.

UObject는 씬에 직접 배치되지 않는 데이터나 로직 중심의 객체에 가깝다.
반면 AActor는 레벨에 직접 배치하거나 런타임에 스폰할 수 있는 월드 객체이다.

예를 들어 캐릭터, 몬스터, 아이템, 트리거처럼
게임 월드 안에 실제로 존재해야 하는 객체는 보통 AActor를 기반으로 만든다.

AActor는 월드에 존재하는 객체이기 때문에
위치, 회전, 스폰, 제거, BeginPlay, Tick 같은 라이프사이클을 가진다.

반면 UObject는 월드에 직접 배치되는 객체가 아니기 때문에 기본적으로 위치나 회전을 가지지 않고,
BeginPlay, Tick 같은 Actor 라이프사이클도 기본적으로 제공되지 않는다.

여기서 UActorComponentUObject를 상속받는 예시이다.

UActorComponent는 직접 월드에 배치되는 객체는 아니지만,
Actor에 부착되어 체력 관리, 스탯 관리, 전투 로직처럼 특정 기능을 담당하는 컴포넌트로 사용할 수 있다.

즉, UObject 계열 클래스라고 해서 모두 월드에 배치되는 것은 아니며,
월드에 직접 존재하는 객체가 필요할 때는 AActor를 사용하고,
데이터나 로직 중심의 객체가 필요할 때는 UObject 계열 클래스를 사용할 수 있다.

정리하면 다음과 같다.

구분 UObject AActor
역할 데이터 및 로직 객체 월드에 존재하는 객체
레벨 배치 불가능 가능
생성 방식 주로 NewObject 주로 SpawnActor
위치 / 회전 없음 있음
라이프사이클 Actor 라이프사이클 없음 BeginPlay, Tick, Destroy
네트워크 복제 기본 단위는 아님 Replication의 기본 단위

즉, 월드에 배치되어야 하거나 위치 정보가 필요한 객체라면 AActor,
엔진의 객체 관리 기능은 필요하지만 월드에 직접 존재할 필요가 없다면
UObject 또는 UObject 기반 클래스를 고려할 수 있다.


UObject와 연결되는 엔진 기능

UObject 계열 클래스는 언리얼 엔진의 여러 시스템과 연결된다.

대표적으로 다음과 같은 기능이 있다.

  • 리플렉션
  • 가비지 컬렉션
  • 직렬화
  • 블루프린트 연동
  • CDO

이번 글에서는 UObject와 어떤 관계가 있는지만 간단히 정리하려고 한다.


UObject와 리플렉션

리플렉션은 언리얼 엔진이 클래스, 변수, 함수 정보를 런타임과 에디터에서 인식할 수 있도록 하는 시스템이다.

언리얼은 UCLASS, UPROPERTY, UFUNCTION 같은 매크로를 통해
클래스와 멤버 정보를 수집하고, 이를 엔진 시스템에서 활용한다.

UObject 계열 클래스는 이 리플렉션 시스템과 연결되기 때문에
에디터 노출, GC 참조 추적, 직렬화, 블루프린트 연동 같은 기능을 사용할 수 있다.

즉, UObject는 언리얼 엔진이 객체 정보를 추적하고 활용할 수 있도록 하는 기반이라고 볼 수 있다.


UObject와 GC(Garbage Collection)

언리얼의 GC는 UObject 계열 객체를 대상으로 동작한다.

GC는 루트셋에서 시작해 UPROPERTY로 추적 가능한 UObject 참조를 따라가고(Mark),
더 이상 참조되지 않는 객체를 제거한다(Sweep).

여기서 중요한 점은 일반 C++ 포인터는 GC가 자동으로 추적하지 못할 수 있다는 점이다.
따라서 UObject를 멤버로 참조할 때는 대부분 UPROPERTY를 사용해 GC가 참조 관계를 알 수 있도록 해야 한다.

또한 언리얼은 GC 비용을 줄이기 위해
객체 탐색을 병렬로 처리하거나, 여러 틱에 나누어 처리하는 점진적 GC 같은 방식도 제공한다.


직렬화와 UObject

직렬화는 오브젝트나 데이터 구조를 저장하거나 전송할 수 있는 형태로 변환하고,
다시 복원할 수 있게 하는 기능이다.

언리얼에서는 UObjectUPROPERTY를 기반으로
에디터, 에셋, 세이브, 네트워크 복제 등 다양한 시스템에서 직렬화를 사용한다.

예를 들어 UPROPERTY로 등록한 값은 언리얼 엔진이 저장하거나 복원할 수 있는 대상으로 인식할 수 있다.

즉, UPROPERTY는 단순히 에디터 노출을 위한 표시가 아니라
엔진이 해당 변수를 어떻게 관리할지 알려주는 정보라고 볼 수 있다.


블루프린트 연동과 UObject

UObject 계열 클래스는 UCLASS, UPROPERTY, UFUNCTION 지정자를 통해 블루프린트와 연동할 수 있다.

예를 들어 변수를 블루프린트에서 읽거나 수정하고 싶다면
UPROPERTYBlueprintReadOnly 또는 BlueprintReadWrite를 사용할 수 있다.

UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 Health;

함수를 블루프린트에서 호출하고 싶다면 UFUNCTIONBlueprintCallable을 사용할 수 있다.

UFUNCTION(BlueprintCallable)
void Heal(int32 Amount);

즉, 언리얼에서 C++ 코드와 블루프린트가 자연스럽게 연동되는 것도
리플렉션 시스템과 UObject 기반 구조 덕분이라고 볼 수 있다.


UObject와 CDO

CDOUCLASS 기반 클래스마다 존재하는 기본 객체이다.

새로운 인스턴스를 만들 때는 이 CDO에 저장된 기본값을 기준으로 초기화될 수 있다.

따라서 생성자에서 설정한 기본값은 단순히 한 번 실행되는 초기화 코드가 아니라,
클래스의 기본 객체인 CDO에도 반영된다는 점을 이해해야 한다.


USTRUCT와 UENUM도 리플렉션이 가능하다

언리얼 리플렉션 시스템은 UObject 계열 클래스에만 제한되는 것은 아니다.

USTRUCTUENUM도 언리얼 리플렉션 시스템에 등록할 수 있다.
하지만 USTRUCTUENUMUObject를 상속받는 객체는 아니다.
따라서 UObject처럼 자체적으로 GC 대상이 되거나 UCLASS 기반 클래스처럼 CDO를 가지는 것은 아니다.

즉, 리플렉션이 가능하다는 것과 UObject 계열 객체라는 것은 구분해서 봐야 한다.


정리

UObject는 언리얼 엔진의 많은 C++ 클래스와 블루프린트 객체가 기반으로 사용하는 기본 클래스이다.
UObject는 단순한 C++ 클래스라기보다 언리얼 엔진이 객체를 추적하고 관리하기 위한 기반 클래스라고 볼 수 있다.

AActorUObject를 상속받지만 레벨에 배치되거나 스폰될 수 있는 월드 객체라는 점에서 차이가 있다.

UObject 계열 클래스는 리플렉션 시스템과 연결되기 때문에
GC, 직렬화, 블루프린트 연동 같은 엔진 기능을 사용할 수 있다.

또한 UCLASS 기반 클래스는 CDO라는 기본 객체를 가지며,
새 인스턴스를 생성할 때 기본값의 기준으로 사용될 수 있다.

다만 USTRUCTUENUM은 리플렉션은 가능하지만,
UObject를 상속받는 객체는 아니므로 UCLASS 기반 클래스와는 차이가 있다.


마무리

이번에 UObject에 대해 정리하면서
언리얼 C++이 일반 C++ 클래스와 다르게 동작하는 이유를 조금 더 이해할 수 있었다.

처음에는 UObject를 단순히 언리얼에서 사용하는 기본 클래스 정도로 생각했다.

하지만 공부해보니 UObject를 기반으로
리플렉션, GC, 직렬화, 블루프린트 연동, CDO 같은 언리얼 엔진의 객체 시스템이 연결되어 있다는 것을 알게 되었다.

앞으로 언리얼 C++ 코드를 작성할 때는 이 클래스가 단순 C++ 객체인지, UObject 계열 객체인지,
그리고 엔진의 리플렉션과 GC 대상인지도 함께 고려해야겠다.