[이득우의 언리얼] 5. 언리얼 오브젝트 리플렉션 시스템 1

2026. 2. 3. 13:05·기타/[강의] 이득우의 언리얼 프로그래밍 Part1

프로퍼티 시스템이란? (리플렉션)

**리플렉션(Reflection)**은 프로그램이 실행 시간에 자기 자신을 조사하는 기능입니다. C++는 기본적으로 리플렉션을 지원하지 않지만, 언리얼 엔진은 자체 리플렉션 시스템을 구축하여 다음과 같은 핵심 기능들을 제공합니다:

  • 에디터의 디테일 패널
  • Serialization (저장/로드)
  • 가비지 콜렉션
  • 네트워크 리플리케이션
  • 블루프린트/C++ 커뮤니케이션

 

프로퍼티 시스템의 주요 기능

1. 자동 프로퍼티 초기화

// 예시: AEnemy 클래스
UPROPERTY(EditAnywhere, Category="Stats")
int32 Health = 100;

// 레벨에 배치하고 Health를 500으로 설정 후 저장하면
// 별도 코드 없이 자동으로 로드됨

UObject는 생성자 호출 전에 자동으로 0으로 초기화됩니다. 이후 생성자에서 커스텀 값으로 초기화할 수 있습니다.

 

2. 레퍼런스 자동 업데이트

AActor 또는 UActorComponent가 소멸되면, UPROPERTY로 마킹된 포인터나 언리얼 컨테이너(TArray 등)에 저장된 레퍼런스는 자동으로 null이 됩니다.

// UPROPERTY로 마킹된 포인터는 자동으로 null 처리됨
UPROPERTY()
AActor* MyActor;

// Raw 포인터는 자동 null 처리 안 됨 (위험!)
AActor* RawPointer;  // 사용 지양

// 가비지 컬렉션은 막지 않지만 유효성 검사가 가능한 약 포인터
TWeakObjectPtr<AActor> WeakPointer;  // 대안으로 사용 가능

 

3. 자동 Serialization

UObject가 저장될 때, 모든 UPROPERTY 값은 자동으로 읽고 쓰기가 가능합니다 (transient로 마킹되지 않은 경우).

// 예시: AEnemy 클래스
UPROPERTY(EditAnywhere, Category="Stats")
int32 Health = 100;

// 레벨에 배치하고 Health를 500으로 설정 후 저장하면
// 별도 코드 없이 자동으로 로드됨

 

4. 프로퍼티 값 자동 업데이트

CDO(Class Default Object)가 변경되면, 기존 인스턴스들의 값이 자동으로 업데이트됩니다.

작동 원리

  • 인스턴스의 값이 이전 CDO 기본값과 같으면 -> 새 CDO 값으로 업데이트
  • 인스턴스의 값이 다르면 -> 의도적으로 설정된 것으로 간주하여 보존
// 예시
// 초기 CDO Health = 100, Enemy_1 Health = 100, Enemy_2 Health = 500
// CDO Health를 150으로 변경하면
// Enemy_1 Health = 150 (자동 업데이트)
// Enemy_2 Health = 500 (보존됨)

 

5. 에디터 통합

UPROPERTY와 UFUNCTION은 별도 코드 없이 에디터에 자동으로 노출되며, 블루프린트와의 통합도 가능합니다.

 

6. 런타임 타입 정보 및 형변환

모든 UObject는 자신의 타입을 알고 있으며, 안전한 형변환이 가능합니다.

// Cast를 사용한 안전한 형변환
AMegaBoss* MegaBoss = Cast<AMegaBoss>(Enemy);
if (MegaBoss)
{
    // 형변환 성공
    MegaBoss->SpecialAttack();
}

// IsA를 사용한 타입 체크
if (Enemy->IsA(AMegaBoss::StaticClass()))
{
    // Enemy가 AMegaBoss 타입임
}

// Super를 사용한 부모 함수 호출
class AMegaBoss : public AEnemy
{
    virtual void Speak()
    {
        Say("Powering up! ");
        Super::Speak();  // 부모 클래스의 Speak 호출
    }
};

 


 

리플렉션 시스템 사용하기

1. 헤더 파일 상단에 generated.h 포함

#include "StrategyChar.generated.h"  // 반드시 마지막에 include

 

2. 매크로 사용

/** 전략 게임의 기본 캐릭터 클래스입니다. */
UCLASS(Abstract)
class AStrategyChar : public ACharacter
{
    GENERATED_BODY()  // 또는 GENERATED_UCLASS_BODY()
    
    /** 죽었을 때 얻을 수 있는 자원량입니다. */
    UPROPERTY(EditAnywhere, Category="변수")
    int32 ResourcesToGather;
    
    /** 무기를 장착하는 함수입니다. */
    UFUNCTION(BlueprintCallable, Category="Attachment")
    void SetWeaponAttachment(class UStrategyAttachment* Weapon);
    
protected:
    /** 근접 공격 애니메이션입니다. */
    UPROPERTY(EditDefaultsOnly, Category="변수")
    UAnimMontage* MeleeAnim;
    
private:
    // UPROPERTY가 없는 멤버 변수도 사용 가능
    // 단, 리플렉션 시스템에서는 보이지 않음
    uint8 MyTeamNum;
};
```

### 리플렉션 타입 계층구조
```
UField
├── UStruct
│   ├── UClass (C++ class)
│   ├── UScriptStruct (C++ struct)
│   └── UFunction (C++ function)
├── UEnum (C++ enumeration)
└── UProperty (C++ member variable)

 

클래스 정보 가져오기

// 컴파일 타임에 클래스 정보 가져오기
UClass* ClassCompile = UMyGameInstance::StaticClass();
UScriptStruct* StructInfo = FMyStruct::StaticStruct();

// 런타임에 인스턴스로부터 클래스 정보 가져오기
UClass* ClassRuntime = MyInstance->GetClass();

// 두 방식으로 얻은 클래스 정보는 정확히 동일한 객체입니다
// ClassCompile == ClassRuntime

// 클래스 이름 가져오기
FString ClassName = ClassRuntime->GetName();

 


 

Class Default Object (CDO)

Class Default Object는 언리얼 객체가 가진 기본값을 보관하는 템플릿 객체입니다.

 

CDO의 특징

  • UClass 정보에 함께 포함되어 있음
  • 엔진 초기화 과정에서 생성됨 (프로그램 75% 정도 로드되었을 타이밍)
  • 클래스당 하나만 존재

CDO 접근 방법

// 방법 1: GetClass()를 통한 접근
UMyGameInstance* DefaultObject = GetClass()->GetDefaultObject<UMyGameInstance>();
FString DefaultSchoolName = DefaultObject->SchoolName;

// 방법 2: StaticClass()를 통한 접근
UMyGameInstance* CDO = UMyGameInstance::StaticClass()->GetDefaultObject<UMyGameInstance>();

// CDO의 값 읽기
int32 DefaultHealth = CDO->Health;

 


 

리플렉션 데이터 활용

프로퍼티 반복 처리

/** 클래스의 모든 프로퍼티를 순회하는 함수입니다. */
void IterateProperties()
{
    // 모든 UPROPERTY 순회
    for (TFieldIterator<UProperty> PropIt(GetClass()); PropIt; ++PropIt)
    {
        UProperty* Property = *PropIt;
        
        // 프로퍼티 이름 출력
        UE_LOG(LogTemp, Log, TEXT("Property: %s"), *Property->GetName());
    }
}

/** 특정 클래스의 함수만 순회하는 함수입니다. */
void IterateFunctions()
{
    // 상속받은 함수 제외, 현재 클래스의 함수만 순회
    for (TFieldIterator<UFunction> FuncIt(GetClass(), EFieldIteratorFlags::ExcludeSuper); FuncIt; ++FuncIt)
    {
        UFunction* Function = *FuncIt;
        // 함수 처리
    }
}

 


 

언리얼 객체 생성

NewObject를 사용한 올바른 생성 방법

// ❌ 잘못된 방법 - 일반 C++ new 사용
UMyObject* WrongObject = new UMyObject();  // 사용 금지!

// ✅ 올바른 방법 - NewObject API 사용
UMyObject* CorrectObject = NewObject<UMyObject>(this);

// 외부 객체를 소유자로 지정
UMyObject* MyObject = NewObject<UMyObject>(Outer);

// 클래스 정보로부터 생성
UMyObject* DynamicObject = NewObject<UMyObject>(this, MyClass);

 

중요: 언리얼 오브젝트는 반드시 NewObject()API로 생성해야 합니다. 일반 C++ new 연산자를 사용하면 가비지 컬렉션, 리플렉션 등의 기능이 작동하지 않습니다.

 


 

UHT (Unreal Header Tool)의 한계

1. 조건부 컴파일 제한

// ❌ UPROPERTY에 조건부 컴파일 사용 지양
#if PLATFORM_WINDOWS
UPROPERTY()
int32 WindowsOnlyValue;  // 위험!
#endif

// ✅ WITH_EDITOR와 WITH_EDITORONLY_DATA는 예외적으로 허용
#if WITH_EDITOR
UPROPERTY()
int32 EditorOnlyValue;  // 안전
#endif

 

2. 제한된 템플릿 지원

// ✅ 지원되는 템플릿 타입
UPROPERTY()
TArray<AActor*> Actors;

UPROPERTY()
TSubclassOf<AActor> ActorClass;

// ❌ 중첩 템플릿은 지원 안 됨
UPROPERTY()
TArray<TArray<int32>> NestedArray;  // 컴파일 에러!

 

3. generated.h 위치

#include "OtherHeader.h"
#include "MyClass.generated.h"  // 반드시 마지막에 include

 


 

실전 예제

예제 1 : 동적 프로퍼티 값 변경

/** 프로퍼티 시스템을 활용한 동적 값 변경 예제입니다. */
void SetPropertyByName(UObject* Object, FName PropertyName, int32 NewValue)
{
    // 프로퍼티 찾기
    UProperty* Property = Object->GetClass()->FindPropertyByName(PropertyName);
    
    if (Property)
    {
        // int32 타입 프로퍼티인지 확인
        UIntProperty* IntProperty = Cast<UIntProperty>(Property);
        if (IntProperty)
        {
            // 값 설정
            IntProperty->SetPropertyValue_InContainer(Object, NewValue);
        }
    }
}

 

예제 2 : 리플렉션을 활용한 디버그 출력

/** 객체의 모든 프로퍼티 값을 로그로 출력하는 함수입니다. */
void DebugPrintProperties(UObject* Object)
{
    if (!Object) return;
    
    UE_LOG(LogTemp, Log, TEXT("=== %s Properties ==="), *Object->GetName());
    
    for (TFieldIterator<UProperty> PropIt(Object->GetClass()); PropIt; ++PropIt)
    {
        UProperty* Property = *PropIt;
        FString PropertyName = Property->GetName();
        FString PropertyValue;
        
        // 프로퍼티 값을 문자열로 변환
        Property->ExportTextItem(PropertyValue, Property->ContainerPtrToValuePtr<void>(Object), nullptr, Object, PPF_None);
        
        UE_LOG(LogTemp, Log, TEXT("%s = %s"), *PropertyName, *PropertyValue);
    }
}

'기타 > [강의] 이득우의 언리얼 프로그래밍 Part1' 카테고리의 다른 글

[이득우의 언리얼] 8. 언리얼의 C++ 설계 - 컴포지션  (0) 2026.02.06
[이득우의 언리얼] 7. 언리얼 C++ 설계 - 인터페이스  (0) 2026.02.04
[이득우의 언리얼] 6.언리얼 오브젝트 리플렉션 시스템 2  (0) 2026.02.04
[이득우의 언리얼] 4. 언리얼 오브젝트 시스템  (0) 2026.01.21
[이득우의 언리얼] 3. FString의 구조와 활용  (0) 2026.01.20
'기타/[강의] 이득우의 언리얼 프로그래밍 Part1' 카테고리의 다른 글
  • [이득우의 언리얼] 7. 언리얼 C++ 설계 - 인터페이스
  • [이득우의 언리얼] 6.언리얼 오브젝트 리플렉션 시스템 2
  • [이득우의 언리얼] 4. 언리얼 오브젝트 시스템
  • [이득우의 언리얼] 3. FString의 구조와 활용
Meoyoung's Development Logs
Meoyoung's Development Logs
내가 보려고 만든 블로그
  • Meoyoung's Development Logs
    이게뭐영
    Meoyoung's Development Logs
  • 전체
    오늘
    어제
    • 분류 전체보기 (229)
      • 게임잼 (3)
      • 언리얼 엔진 (53)
        • 꿀 Tip ! (7)
        • 트러블슈팅 (24)
        • 최적화 (0)
        • 캐릭터 (2)
        • VR (1)
        • Lighting (2)
        • 멀티스레드 (2)
        • 문자열 (0)
      • C++ (31)
        • 문법 정리 (8)
        • [서적] Fundamental C++ 프로그래밍 .. (5)
        • [서적] 이것이 C++이다 (11)
        • [서적] Effective C++ (7)
      • C# (1)
        • [서적] 이것이 C#이다 (1)
      • 코딩테스트 (26)
        • 프로그래머스 (6)
        • 알고리듬 (13)
        • 자료구조 (7)
      • 컴퓨터 과학 (27)
        • 운영체제 (11)
        • 데이터베이스 (0)
        • 디자인패턴 (0)
        • 자료구조 (5)
        • 네트워크 (0)
        • 컴퓨터구조 (11)
      • 면접준비 (0)
        • C++ (0)
        • 운영체제 (0)
        • 자료구조 (0)
      • 프로젝트 (25)
        • [팀프로젝트] The Fourth Descenda.. (5)
        • [개인프로젝트] FPS 구현 맛보기 (5)
        • GetOutOf (15)
      • 기타 (54)
        • [강의] 이득우의 언리얼 프로그래밍 Part1 (10)
        • [강의] 이득우의 언리얼 프로그래밍 Part3 (12)
        • [강의] 소울라이크 개발 A-Z (4)
        • [강의] Udemy-2D (5)
        • [서적] 인생 언리얼5 (4)
        • 스파르타코딩클럽 (15)
        • 객체지향프로그래밍 (2)
        • 컴퓨터회로 (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    셸정렬
    쉘정렬
    게임개발
    자료구조
    경북게임잼
    선택정렬
    알고리즘
    참가후기
    삽입정렬
    버블정렬
    게임잼
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Meoyoung's Development Logs
[이득우의 언리얼] 5. 언리얼 오브젝트 리플렉션 시스템 1
상단으로

티스토리툴바