Class Default Object, Reflection System, Garbage Collection

김여울·2025년 10월 23일
0

내일배움캠프

목록 보기
102/114

Unreal Object

Class Default Object

[ 정의 ]

Class Default Onbject(CDO)는 언리얼 엔진 실행 시 자동으로 생성되는 특별한 UObject 인스턴스로,
각 UClass의 기본값을 보관하고 새로운 객체 인스턴스 생성 시 초기화 템플릿 역할을 합니다.

즉, 새 인스턴스가 생성될 때마다 해당 클래스의 CDO를 복사하여 기본 속성 값과 상태를 초기화합니다.

이 덕분에 모든 인스턴스가 일관된 초기 상태를 가질 수 있습니다.

// 선언부
class UClass : public UStruct
{
public:
	/** 클래스 기본 객체; 델타 직렬화 및 객체 초기화에 사용된다. */
	TObjectPtr<UObject> ClassDefaultObject;
}

// 예시
// CDO 가져오기
UMyObject* DefaultObj = UMyObject::StaticClass()->GetDefaultObject<UMyObject>();
int DefaultHealth = DefaultObj->Health;

델타 직렬화
객체의 현재 값과 기본값(CDO)를 비교하여, 변경된 값만 저장하거나 전송하는 최적화된 직렬화 방식입니다.

이렇게 하면 클래스의 기본 인스턴스(CDO)에 접근하여 속성 값을 확인하거나,
객체를 만들지 않고도 기본 데이터를 참조할 수 있습니다.

[ CDO의 주요 역할 및 장점 ]

CDO는 클래스의 기본값 저장소이자 데이터 최적화와 구성 확장의 중심 요소로,
메타데이터 관리, 네트워크 트래픽 절감, 저장 공간 절약, 설정 기반의 객체 구성 등
다양한 엔진 기능을 호율적으로 지원합니다.

  1. 기본 속성 값 및 동작 제공

    • CDO는 클래스의 기본 속성 값을 저장하며, 새 객체 생성 시 해당 값을 복사해 인스턴스를 초기화합니다.
    • 덕분에 새 인스턴스는 null이 아닌 일관된 초기값을 가집니다.
    • 즉, 클래스의 속성 템플릿으로서 인스턴스 생성 과정을 단순화합니다.
    • 또한 클래스의 기본 함수 정보를 포함해, 객체를 만들지 않고도 정적 함수 호출이 가능합니다.
    • 이를 통해 불필요한 인스턴스화를 줄이고 성능 오버헤드를 최소화할 수 있습니다.
  2. 메타데이터 정보 저장

    • CDO는 표시 이름, 키워드, 카테고리 등 클래스의 메타데이터 정보를 저장합니다.
    • 해당 정보는 각 인스턴스에 별도로 저장할 필요가 없으므로 중복되고 유지 관리가 어렵지만, CDO에 하나의 사본만 저장하면 되므로 리소스를 최대한 절약할 수 있습니다.
    • 메타데이터 정보는 에디터와 런타임에서 사용할 수 있으며,
      에디터에서 객체의 모양, 속성 표시를 제어하고 에디터 관련 기능을 사용자 지정할 수 있습니다.
  3. 데이터 전송 저장 (네트워크 복제)

    • 네트워크 동기화 시 서버는 클라이언트에게 객체의 유형(UClass)만 전달합니다.
    • 그 외의 속성들은 CDO의 기본값을 기반으로 로컬에서 복원하므로, 모든 속성을 전송할 필요가 없어 네트워크 트래픽을 크게 절감할 수 있습니다.
  4. 데이터 저장 공간 절약 (직렬화 최적화)

    • 동일한 클래스의 여러 객체를 파일에 저장할 때, 각 개체의 전체 데이터를 저장하는 대신 CDO의 기본값과의 차이(델타)만 기록해 공간 차지를 최소화할 수 있습니다.

      CDO를 기준으로 한 델타(차이) 저장

    • 이 방식은 저장 용량을 최소화하고, 로딩 시에도 효율적인 데이터 복원을 가능하게 합니다.

  5. 데이터 구성 가능성

    • 언리얼 엔진은 메타데이터와 설정(Config) 파일을 통해 CDO의 속성을 동적으로 변경할 수 있습니다.
    • 이를 통해 동일한 클래스를 사용하더라도, 설정 값만 바꿔서 다양한 초기 상태나 구성을 가진 객체를 손쉽게 생성할 수 있습니다.
    • 즉, CDO는 유연한 객체 구성 시스템의 기반 역할을 합니다.

Reflection System

[ 정의 ]

리플렉션(Reflection) 또는 프로퍼티 시스템(Property System)은
런타임(동적) 중에 클래스의 구조와 메타데이터를 검사하고 조작할 수 있는 기능을 말합니다.

이 시스템은 언리얼 엔진의 핵심 기능들의 근본적인 기반 메커니즘으로 작동합니다.
대표적으로 다음 기능들이 리플렉션을 통해 구현됩니다.

  • 에디터 디테일 패널 (Details Panel) 표시
  • 시리얼라이제이션 (Serialization) : 데이터 저장 및 불러오기
  • 가비지 컬렉션 (Garbage Collection)
  • 네트워크 리플리케이션 (Network Replication) : 서버의 변경 사항을 클라이언트에 동기화
  • 직렬화 / 역직렬화 (Serialization / Deserialization)
  • 블루프린트 ↔ C++ 연동 (Communication)

[ 장점 ]

  • 에디터에서 실시간으로 값 조정 및 테스트가 가능합니다.
  • 블루프린트와 C++ 간의 연동이 원활해집니다.

이를 통해 디자이너가 프로그래머의 C++ 로직을 직관적으로 활용할 수 있고,
개발 효율과 협업 생산성을 크게 향상됩니다.

[ 리플렉션 시스템을 사용하기 위해 필요한 설정 ]

리플렉션을 사용하는 모든 언리얼 C++ 클래스의 헤더 파일에는 반드시

#include "FileName.generated.h"

포함해야 합니다.

또한, 리플렉션 시스템에 클래스나 멤버를 등록하기 위해서는 다음 매크로를 사용합니다.

  • 클래스 선언: UCLASS
  • 구조체 선언: USTRUCT
  • 클래스/구조체 본문: GENERATED_BODY
  • 열거형: ENUM
  • 멤버 함수: UFUNCTION
  • 멤버 변수: UPROPERTY
    → 모든 멤버 변수를 UPROPERTY 로 선언할 필요는 없지만,
    그렇지 않으면 언리얼 시스템(GC, 리플리케이션 등)의 관리를 받지 못합니다.

[ 제공하는 기능 ]

리플렉션 시스템은 런타임 중에도 클래스와 객체를 동적으로 제어할 수 있는 기능을 제공합니다.

  • 클래스 정보를 검색하거나 인스턴스 생성
  • 멤버 변수의 값을 동적으로 읽고 수정
  • 접근 지시자(private/protected)와 관계없이 함수 호출
기능설명
클래스 탐색FindClass(TEXT("MyClass"))
변수 정보 조회FindPropertyByName(TEXT("Health"))
변수 값 읽기/쓰기FProperty::GetValue_InContainer/SetValue_InContainer
함수 탐색 및 호출FindFunctionByName() + ProcessEvent()
// 사용 예시
// 리플렉션을 이용하여 객체 속성 접근 및 변경
FProperty* NewProperty = UMyObject::StaticClass()FindPropertyByName(TEXT(”Name”));

//프로퍼티 값을 가져올 수 있음.
NewProperty→GetValue_InContainer(클래스 객체, &프로퍼티 자료형);

//프로퍼티 값을 바꿀 수 있음.
NewProperty→SetValue_InContainer(클래스 객체, &프로퍼티 자료형);

//리플렉션을 이용하여 특정 함수 실행
UFunction* DoFunc = MyObject->GetClass()->FindFunctionByName(TEXT("ChangeName"));
MyObject->ProcessEvent(DoFunc, nullptr);

언리얼 오브젝트의 주요 기능

언리얼 오브젝트(UObject)는 C++ 언어의 한계를 보완하고,

C#이나 Java 같은 현대적 객체지향 언어의 기능을 포함한 확장된 객체 시스템을 제공합니다.

즉, 단순한 C++ 클래스가 아닌 언리얼 엔진 전용 런타임 기능의 기반 클래스입니다.

[ 클래스 및 타입 관리 ]

  • 클래스 기본 객체 (Class Default Object, CDO)
    : 클래스의 기본값과 타입 정보를 저장하여, 새 객체 생성 시 초기화를 지원
  • 리플렉션 (Reflection)
    : UPROPERTYUFUNTION 등을 통해 런타임 중 클래스 정보와 프로퍼티를 탐색·조작
  • 향상된 구조체 (Enhanced Struct)
    : 일반 C++ 구조체보다 확장되어, 리플렉션과 직렬화(Serialization) 를 지원

[ 객체 지향 프로그래밍 지원 ]

  • 인터페이스 (Interface)
    : 객체 지향 언어가 제공하는 인터페이스 개념(객체가 반드시 구현해야 하는 기능을 명시)을 지원
  • 향상된 열거형 (Enhanced Enum)
    : C++의 단순 enum보다 발전된 형태로, 리플렉션과 에디터 표시가 가능
  • 델리게이트 (Delegate)
    : 객체 간 느슨한 결합(Loose Coupling)을 가능하게 하여 의존성을 낮추고,
    이벤트 기반 통신 구조를 구현

[ 메모리 및 수명 관리 ]

  • 가비지 컬렉션 (Garbage Collection)
    : 엔진이 UObject의 수명 주기와 메모리를 자동 관리하여, 더 이상 참조되지 않는 객체를 자동으로 정리
  • 레퍼런스 자동 업데이트 및 초기화
    :
    참조 변경 시 자동 업데이트되고, 생성 시 기본 프로퍼티가 자동 초기화 됨

[ 데이터 관리 및 네트워크 연동 ]

  • 직렬화(Serialization)
    : 메모리 상에 있는 객체나 데이터 구조를 바이트 단위로 쪼개 나열하는 과정,
    저장/전송/로드 가능
  • 네트워크 리플리케이션
    : 서버에서 발생한 객체의 상태를 클라이언트와 자동으로 동기화
  • 디폴트 프로퍼티 변경 자동 반영
    : 클래스의 기본 프로퍼티가 수정되면, 기존 인스턴스에도 자동으로 업데이트됨
  • 자동 에디터 통합(Editor Integration)
    :
    UObject는 언리얼 에디터와 완전히 통합되어,
    에디터 내에서 실시간 값 수정, 미리보기, 직렬화 등이 가능

Garbage Collection

언리얼 엔진의 가비지 컬렉션(GC)은 더 이상 참조되지 않는 UObject를 자동으로 탐색하고 메모리에서 해제하는 시스템입니다.

즉, 프로그래머가 직접 delete 하지 않아도, 엔진이 주기적으로 참조 관계를 분석하여 불필요한 객체를 정리합니다.

[ 동작 원리 ]

  • 언리얼의 GC는 참조 추적(Reference Tracking) 방식으로 동작합니다.
  • 엔진은 모든 UObject 의 참조 관계를 그래프 형태로 분석하고,
    도달 가능한 객체(Reachable Object)는 유지하며
    도달 불가능한 객체(Unreachable Object)를 찾아 메모리에서 제거합니다.
  • 즉, 어떤 객체도 참조하지 않는 UObject는 자동으로 삭제됩니다.
  • 만약 특정 참조가 GC에 의해 제거되길 원하지 않는다면
    해당 참조를 TWeakObjectPtr 로 선언해 약한 참조(Weak Reference)로 관리해야 합니다.

[ Actor와 GC의 관계 ]

  • 액터(AActor)는 Destroy() 를 호출해 삭제 요청을 하지만,
    실제 메모리에서 해제되는 시점은 모든 참조가 해제되고 GC가 수행될 때입니다.
  • Destroy() 가 호출되더라도 레벨에서 제거되었더라도 해당 액터에 대한 모든 레퍼런스가 해제될 때까지 가비지 컬렉션되지 않는다.
  • 즉,
    Destory() 호출 -> 레벨에서 제거 -> 참조 해제 완료 -> GC 실행 시 해제
    순서로 처리됩니다.

[ IsValid() 함수의 역할 ]

  • IsValid() 는 포인터가 nullptr 이거나 GC로 이미 파괴된 객체인지 확인합니다.
  • 하지만 대부분의 경우 OnDestroy 이벤트에서 포인터를 정리하는 것이 바람직하며,
    IsValid() 는 보조적인 안전검사용으로만 사용하는 것이 좋습니다.

[ UObject 생성 시 주의사항 ]

UObject 는 반드시 NewObject<> 로 생성해야 엔진의 GC 관리 대상이 됩니다.

생성 방식설명GC 관리 여부
new일반 C++ 객체 생성. 엔진의 GC에 등록되지 않아 메모리 누수 위험
NewObject<T>()언리얼 엔진이 관리하는 객체 생성. 자동으로 GC 대상에 등록됨
// ❌ 잘못된 예시 (GC 관리 안 됨)
MyObj* Obj = new MyObj();

// ✅ 올바른 예시 (GC 관리 대상)
UMyObj* Obj = NewObject<UMyObj>();

0개의 댓글