UE5 UObject & Reflection 시스템
개요 — 리플렉션이란
Section titled “개요 — 리플렉션이란”언리얼 엔진의 리플렉션(Reflection) 시스템은 런타임에 타입 정보를 조회하고 조작할 수 있게 해주는 핵심 기반입니다. C++은 기본적으로 런타임 타입 정보(RTTI)가 제한적이지만, UE는 Unreal Header Tool(UHT)을 통해 빌드 시점에 메타데이터를 생성하여 강력한 리플렉션 기능을 구현합니다.
리플렉션 덕분에 다음이 가능해집니다.
| 기능 | 설명 |
|---|---|
| Blueprint 연동 | C++ 클래스·함수·변수를 Blueprint에서 사용 |
| 직렬화 | UPROPERTY 변수를 파일/네트워크로 저장/전송 |
| 가비지 컬렉션 | UObject 참조 그래프를 자동 추적 |
| 에디터 통합 | Details 패널에서 변수 편집 |
| 네트워크 복제 | Replicated 속성을 자동 동기화 |
1. UObject — 모든 것의 시작
Section titled “1. UObject — 모든 것의 시작”UObject는 UE 객체 계층의 최상위 클래스입니다. 리플렉션·직렬화·GC의 혜택을 받으려면 UObject를 직접 또는 간접적으로 상속해야 합니다.
UObject ├─ UActorComponent │ └─ USceneComponent │ └─ UPrimitiveComponent ├─ AActor │ ├─ APawn → ACharacter │ └─ AGameModeBase └─ UGameInstance1.1 UObject 생성
Section titled “1.1 UObject 생성”// 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(); // 위험1.2 UObject의 핵심 함수
Section titled “1.2 UObject의 핵심 함수”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); // 실패 시 nullptrACharacter& CharRef = CastChecked<ACharacter>(SomeActor); // 실패 시 assert
// 클래스 관계 확인bool bIsChar = SomeActor->IsA<ACharacter>();bool bImplements = SomeActor->GetClass()->ImplementsInterface(UMyInterface::StaticClass());2. UCLASS 매크로
Section titled “2. UCLASS 매크로”UCLASS()는 클래스를 UE 리플렉션 시스템에 등록합니다. UHT가 이 매크로를 파싱해 메타데이터를 생성합니다.
2.1 주요 지정자(Specifier)
Section titled “2.1 주요 지정자(Specifier)”UCLASS( BlueprintType, // Blueprint 변수 타입으로 사용 가능 Blueprintable, // Blueprint에서 상속 가능 Abstract, // 직접 인스턴스화 불가 (추상 클래스) NotBlueprintable, // Blueprint 상속 금지 ClassGroup=(Combat), // 에디터 그룹 분류 meta=( BlueprintSpawnableComponent // 에디터 Add Component 목록에 표시 ))class MYGAME_API UWeaponComponent : public UActorComponent{ GENERATED_BODY() // ...};2.2 GENERATED_BODY()
Section titled “2.2 GENERATED_BODY()”GENERATED_BODY()는 UHT가 삽입하는 필수 코드 블록입니다. 생성자·리플렉션 함수·직렬화 코드 등을 자동 생성합니다. 빠뜨리면 컴파일 오류가 발생합니다.
3. UPROPERTY 매크로
Section titled “3. UPROPERTY 매크로”UPROPERTY()는 멤버 변수를 리플렉션 시스템에 등록합니다. 이 매크로 없이는 GC 추적·직렬화·에디터 노출이 불가합니다.
3.1 주요 지정자
Section titled “3.1 주요 지정자”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;};3.2 접근 지정자 정리
Section titled “3.2 접근 지정자 정리”| 지정자 | 에디터 인스턴스 | 에디터 기본값 | Blueprint 읽기 | Blueprint 쓰기 |
|---|---|---|---|---|
EditAnywhere | O | O | - | - |
EditDefaultsOnly | X | O | - | - |
EditInstanceOnly | O | X | - | - |
VisibleAnywhere | 읽기전용 | 읽기전용 | - | - |
BlueprintReadWrite | - | - | O | O |
BlueprintReadOnly | - | - | O | X |
3.3 UObject 포인터와 GC
Section titled “3.3 UObject 포인터와 GC”// 올바른 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 안전성, 미래 쿠킹 최적화 지원4. UFUNCTION 매크로
Section titled “4. UFUNCTION 매크로”UFUNCTION()은 멤버 함수를 리플렉션 시스템에 등록합니다.
4.1 주요 지정자
Section titled “4.1 주요 지정자”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);};4.2 BlueprintNativeEvent 구현
Section titled “4.2 BlueprintNativeEvent 구현”UFUNCTION(BlueprintNativeEvent, Category = "Events")void OnActorKilled();virtual void OnActorKilled_Implementation();
// MyActor.cppvoid AMyActor::OnActorKilled_Implementation(){ // Blueprint에서 재정의하지 않으면 이 C++ 구현이 실행됨 UE_LOG(LogTemp, Log, TEXT("Actor killed (C++ default)"));}5. Class Default Object (CDO)
Section titled “5. Class Default Object (CDO)”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);}6. StaticClass vs GetClass
Section titled “6. StaticClass vs GetClass”// 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>(),AActor는SpawnActor<T>()를 사용합니다.IsValid()를 사용해 null과 Pending Kill을 동시에 확인합니다.- UE5.0 이상에서는 UPROPERTY와 함께 원시 포인터 대신
TObjectPtr<T>를 사용합니다.