Skip to content

UE5 UObject & Reflection 시스템

언리얼 엔진의 리플렉션(Reflection) 시스템은 런타임에 타입 정보를 조회하고 조작할 수 있게 해주는 핵심 기반입니다. C++은 기본적으로 런타임 타입 정보(RTTI)가 제한적이지만, UE는 Unreal Header Tool(UHT)을 통해 빌드 시점에 메타데이터를 생성하여 강력한 리플렉션 기능을 구현합니다.

리플렉션 덕분에 다음이 가능해집니다.

기능설명
Blueprint 연동C++ 클래스·함수·변수를 Blueprint에서 사용
직렬화UPROPERTY 변수를 파일/네트워크로 저장/전송
가비지 컬렉션UObject 참조 그래프를 자동 추적
에디터 통합Details 패널에서 변수 편집
네트워크 복제Replicated 속성을 자동 동기화

UObject는 UE 객체 계층의 최상위 클래스입니다. 리플렉션·직렬화·GC의 혜택을 받으려면 UObject를 직접 또는 간접적으로 상속해야 합니다.

UObject
├─ UActorComponent
│ └─ USceneComponent
│ └─ UPrimitiveComponent
├─ AActor
│ ├─ APawn → ACharacter
│ └─ AGameModeBase
└─ UGameInstance
// UObject 파생 클래스는 new 대신 NewObject<T>()로 생성
UMyDataAsset* Asset = NewObject<UMyDataAsset>(GetTransientPackage(), UMyDataAsset::StaticClass());
// AActor 파생 클래스는 월드를 통해 스폰
FActorSpawnParameters Params;
AMyActor* SpawnedActor = GetWorld()->SpawnActor<AMyActor>(AMyActor::StaticClass(), Location, Rotation, Params);
// !! 절대 금지: UObject를 new로 직접 생성하면 GC 추적 불가
// UMyObject* Obj = new UMyObject(); // 위험
UMyObject* Obj = NewObject<UMyObject>();
// 타입 확인
bool bIsValid = IsValid(Obj); // null + Pending Kill 동시 확인 (권장)
UClass* Class = Obj->GetClass(); // 런타임 UClass 획득
FString Name = Obj->GetName(); // 객체 이름
// 타입 캐스팅
ACharacter* Char = Cast<ACharacter>(SomeActor); // 실패 시 nullptr
ACharacter& CharRef = CastChecked<ACharacter>(SomeActor); // 실패 시 assert
// 클래스 관계 확인
bool bIsChar = SomeActor->IsA<ACharacter>();
bool bImplements = SomeActor->GetClass()->ImplementsInterface(UMyInterface::StaticClass());

UCLASS()는 클래스를 UE 리플렉션 시스템에 등록합니다. UHT가 이 매크로를 파싱해 메타데이터를 생성합니다.

UCLASS(
BlueprintType, // Blueprint 변수 타입으로 사용 가능
Blueprintable, // Blueprint에서 상속 가능
Abstract, // 직접 인스턴스화 불가 (추상 클래스)
NotBlueprintable, // Blueprint 상속 금지
ClassGroup=(Combat), // 에디터 그룹 분류
meta=(
BlueprintSpawnableComponent // 에디터 Add Component 목록에 표시
)
)
class MYGAME_API UWeaponComponent : public UActorComponent
{
GENERATED_BODY()
// ...
};

GENERATED_BODY()는 UHT가 삽입하는 필수 코드 블록입니다. 생성자·리플렉션 함수·직렬화 코드 등을 자동 생성합니다. 빠뜨리면 컴파일 오류가 발생합니다.


UPROPERTY()는 멤버 변수를 리플렉션 시스템에 등록합니다. 이 매크로 없이는 GC 추적·직렬화·에디터 노출이 불가합니다.

UCLASS()
class MYGAME_API AMyCharacter : public ACharacter
{
GENERATED_BODY()
public:
// 에디터 노출 + 직렬화 + GC 추적
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Stats")
float MaxHealth = 100.f;
// 에디터에서 편집 불가, Blueprint 읽기 전용
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Stats")
float CurrentHealth = 0.f;
// 기본값만 에디터에서 설정 (인스턴스 편집 불가)
UPROPERTY(EditDefaultsOnly, Category = "Config")
TSubclassOf<UDamageType> DamageTypeClass;
// 네트워크 복제
UPROPERTY(Replicated, Category = "Network")
int32 Score = 0;
// 복제 + 변경 콜백
UPROPERTY(ReplicatedUsing = OnRep_Health, Category = "Network")
float ReplicatedHealth = 100.f;
UFUNCTION()
void OnRep_Health();
// GC 추적 (에디터 미노출)
UPROPERTY()
TObjectPtr<USkeletalMeshComponent> CachedMesh;
// Transient: 직렬화 제외 (세션 임시 데이터)
UPROPERTY(Transient)
TObjectPtr<UParticleSystemComponent> ActiveEffect;
};
지정자에디터 인스턴스에디터 기본값Blueprint 읽기Blueprint 쓰기
EditAnywhereOO--
EditDefaultsOnlyXO--
EditInstanceOnlyOX--
VisibleAnywhere읽기전용읽기전용--
BlueprintReadWrite--OO
BlueprintReadOnly--OX
// 올바른 UObject 포인터 선언 — UPROPERTY 없으면 GC가 수거
UPROPERTY()
TObjectPtr<UMaterialInstance> WeaponMaterial; // UE5.0+ 권장
// TWeakObjectPtr — GC 추적 안 함, 소멸 감지 가능
TWeakObjectPtr<AEnemy> LastAttacker;
if (LastAttacker.IsValid())
{
LastAttacker->ApplyEffect();
}
// TObjectPtr vs 원시 포인터
// UE5에서 UPROPERTY()와 함께 쓸 때 TObjectPtr<T> 사용 권장
// — 접근 추적, null 안전성, 미래 쿠킹 최적화 지원

UFUNCTION()은 멤버 함수를 리플렉션 시스템에 등록합니다.

UCLASS()
class MYGAME_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// Blueprint에서 호출 가능
UFUNCTION(BlueprintCallable, Category = "Combat")
void TakeDamage(float Amount);
// Blueprint에서 호출 가능, 반환값 있음, 부수효과 없음
UFUNCTION(BlueprintPure, Category = "Stats")
float GetHealthPercent() const;
// Blueprint에서 재정의 가능 (C++ 기본 구현 있음)
UFUNCTION(BlueprintNativeEvent, Category = "Events")
void OnActorKilled();
virtual void OnActorKilled_Implementation(); // C++ 구현 함수
// Blueprint에서만 구현 가능 (C++ 선언만)
UFUNCTION(BlueprintImplementableEvent, Category = "Events")
void OnItemCollected(AActor* Item);
// 네트워크: 서버에서 실행
UFUNCTION(Server, Reliable, WithValidation)
void ServerFire(FVector Direction);
void ServerFire_Implementation(FVector Direction);
bool ServerFire_Validate(FVector Direction);
// 네트워크: 모든 클라이언트에서 실행
UFUNCTION(NetMulticast, Unreliable)
void MulticastPlayEffect(FVector Location);
void MulticastPlayEffect_Implementation(FVector Location);
};
MyActor.h
UFUNCTION(BlueprintNativeEvent, Category = "Events")
void OnActorKilled();
virtual void OnActorKilled_Implementation();
// MyActor.cpp
void AMyActor::OnActorKilled_Implementation()
{
// Blueprint에서 재정의하지 않으면 이 C++ 구현이 실행됨
UE_LOG(LogTemp, Log, TEXT("Actor killed (C++ default)"));
}

CDO는 클래스당 하나씩 생성되는 원본 인스턴스로, 에디터에서 설정한 기본값이 여기 저장됩니다. 런타임 인스턴스는 CDO의 값을 복사합니다.

// CDO 접근
AMyCharacter* CDO = GetDefault<AMyCharacter>();
float DefaultHealth = CDO->MaxHealth; // 에디터 기본값
// CDO를 통해 클래스 정보 조회
UClass* CharClass = AMyCharacter::StaticClass();
UProperty* HealthProp = CharClass->FindPropertyByName(TEXT("MaxHealth"));
// 리플렉션으로 런타임 속성값 읽기/쓰기 (고급)
if (FFloatProperty* FloatProp = CastField<FFloatProperty>(HealthProp))
{
float Value = FloatProp->GetPropertyValue_InContainer(MyCharacterInstance);
FloatProp->SetPropertyValue_InContainer(MyCharacterInstance, 200.f);
}

// StaticClass: 컴파일 타임에 알려진 클래스 (템플릿 파라미터)
UClass* CharClass = AMyCharacter::StaticClass();
// GetClass: 런타임 객체의 실제 클래스
UClass* ActualClass = SomeActor->GetClass();
// 활용 예시: 특정 타입의 Actor를 월드에서 검색
TArray<AActor*> FoundActors;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), AMyCharacter::StaticClass(), FoundActors);
// 인스턴스 여부 확인
bool bIsChar = SomeActor->IsA(AMyCharacter::StaticClass());
bool bIsChar2 = SomeActor->IsA<AMyCharacter>(); // 템플릿 버전 (권장)

매크로역할
UCLASS()클래스를 UE 리플렉션에 등록
UPROPERTY()변수를 GC·직렬화·에디터에 등록
UFUNCTION()함수를 Blueprint·네트워크·리플렉션에 등록
GENERATED_BODY()UHT 자동 생성 코드 삽입 (필수)

핵심 규칙:

  • UObject 포인터를 저장할 때는 반드시 UPROPERTY() 또는 TWeakObjectPtr을 사용합니다.
  • UObject를 생성할 때는 NewObject<T>(), AActorSpawnActor<T>()를 사용합니다.
  • IsValid()를 사용해 null과 Pending Kill을 동시에 확인합니다.
  • UE5.0 이상에서는 UPROPERTY와 함께 원시 포인터 대신 TObjectPtr<T>를 사용합니다.