Unreal TIL 37์ผ์ฐจ - Reflection, UHT, UBT, CDO

yysยท2026๋…„ 4์›” 16์ผ

TIL

๋ชฉ๋ก ๋ณด๊ธฐ
30/77

๐Ÿ”— ํ•™์Šต ๋‚ ์งœ ์ธ์ฆ(์ฃผ์ œ์™€ ๋ฌด๊ด€)

์˜ค๋Š˜ ํ•œ ๋‚ด์šฉ


  • C++ ์ฝ”๋“œ์นดํƒ€
  • ์–ธ๋ฆฌ์–ผ C++ ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ ๋ถ„์„
  • AI Model ์บ๋ฆญํ„ฐ ์ ์šฉ ๋ฐ ๋กœ์ง ๊ตฌํ˜„

๋ฆฌํ”Œ๋ ‰์…˜


๋ฆฌํ”Œ๋ ‰์…˜(Reflection)์€ ํ”„๋กœ๊ทธ๋žจ์ด ์‹คํ–‰ ์ค‘(Runtime)์— ์ž๊ธฐ ์ž์‹ ์˜ ๊ตฌ์กฐ์™€ ์†์„ฑ์„ ๊ด€์ฐฐํ•˜๊ณ  ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ ์„ ์˜๋ฏธํ•œ๋‹ค.

์‚ฌ์‹ค ํ•ด๋‹น ๋‚ด์šฉ๋งŒ ๋ณด๋Š” ๊ฑด ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์šฐ๋ฏ€๋กœ, ์•„๋ž˜ ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด์„œ ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

์ฝ”๋“œ ์ „๋ฌธ์€ ์•„๋‹ˆ์ง€๋งŒ, ์•„๋ž˜์™€ ๊ฐ™์ด ํ‘œ์ค€ C++๋กœ ๋™์ž‘ํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

class MyPlayer {
public:
    int Health = 100;
};

์ด๋•Œ, ๋Ÿฐํƒ€์ž„์—์„œ Health๋ผ๋Š” ์ด๋ฆ„์„ ์‚ฌ์šฉํ•ด์„œ Health์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์„๊นŒ?
ํ‘œ์ค€ C++์—์„œ๋Š”, ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋กœ๋งŒ ๋ฉ”๋ชจ๋ฆฌ์— ์ ‘๊ทผํ•˜๊ธฐ ๋•Œ๋ฌธ์— Health๋ผ๋Š” ์ด๋ฆ„์„ ํ†ตํ•ด์„œ ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์•Œ ์ˆ˜ ์—†๋‹ค.

์–ธ๋ฆฌ์–ผ C++์—์„œ๋Š” ํŠน์ • ๋งˆ์ปค๋งŒ ๋‹ฌ์•„์ฃผ๋ฉด ์–ธ๋ฆฌ์–ผ์˜ UHT๊ฐ€ ์ฝ”๋“œ๋ฅผ ํ›‘์–ด๋ณด๊ณ , ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ ์‚ฌ์ „์„ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

class MyPlayer : public UObject {
    GENERATED_BODY()
public:
    UPROPERTY()
    int Health = 100;
};

๊ทธ๋ž˜์„œ ๋ฆฌํ”Œ๋ ‰์…˜์€ ๋Ÿฐํƒ€์ž„์—์„œ Health๋ผ๋Š” ์ด๋ฆ„์„ ์‚ฌ์šฉํ•ด์„œ ์ฃผ์†Œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์•Œ ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์ค€๋‹ค.

์ด๋Ÿฌํ•œ ์ด์ ์„ ํ™œ์šฉํ•ด์„œ, ๋ฆฌํ”Œ๋ ‰์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด์ค€๋‹ค.

  • ๋Ÿฐํƒ€์ž„์œผ๋กœ, ๊ฐ์ฒด ํƒ€์ž… ์ •๋ณด๋ฅผ ํ™•์ธ
  • ์—๋””ํ„ฐ์™€ ์—ฐ๋™๋˜์–ด, ๋งˆํ‚น๋œ ๋ณ€์ˆ˜(UPROPERTY()๊ฐ€ ์žˆ๋Š” ๋ณ€์ˆ˜)๊ฐ€ Detail Panel์— ํ‘œ์‹œ
  • ๊ฐ์ฒด ์ฐธ์กฐ ๊ด€๊ณ„๋ฅผ ์ถ”์ ํ•จ์œผ๋กœ์จ, ๋ฉ”๋ชจ๋ฆฌ ์ž๋™ ๊ด€๋ฆฌ

๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜


ํ‘œ์ค€ C++๊ณผ ๋‹ฌ๋ฆฌ, ์–ธ๋ฆฌ์–ผ C++์—์„œ๋Š” ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.
ํ‘œ์ค€ C++์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น(new)/ํ•ด์ œ(delete)๋ฅผ ์ˆ˜๋™์œผ๋กœ ํ•ด์ค˜์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜(Memory Leak), ๋Œ•๊ธ€๋ง ํฌ์ธํ„ฐ(Dangling Pointer), ์ด์ค‘ ํ•ด์ œ(Double-Free)ํ˜„์ƒ์ด ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ๋‹ค.

์Šค๋งˆํŠธ ํฌ์ธํ„ฐ์˜ ๊ฒฝ์šฐ์™€ ์ฐจ์ด์ ์ด๋ผ๊ณ  ํ•œ๋‹ค๋ฉด, ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๊ฑธ๋ฆด ์ผ์ด ์—†์ง€๋งŒ ๊ฐ์ฒด๋ฅผ ์ฒญ์†Œํ•˜๋Š” ํƒ€์ด๋ฐ์„ ์•Œ ์ˆ˜ ์—†๋‹ค๋Š” ์ ์ด๋‹ค.

๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์˜ ๋™์ž‘ ์›๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. GameInstance๋‚˜ Wolrd ๋“ฑ. ์ „์—ญ์œผ๋กœ ๋Œ์•„๊ฐ€๋Š” ํ•ต์‹ฌ ๊ฐ์ฒด๋ฅผ ๋ฃจํŠธ ์…‹(Root Set)์— ๋„ฃ๊ณ  ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ์˜ ์ถœ๋ฐœ์ ์œผ๋กœ ์ธ์‹ํ•œ๋‹ค.
  2. ์ดํ›„ ์ˆœ์ฐจ์ ์œผ๋กœ UObject๋ฅผ ์ƒ์†ํ•˜๊ณ  UPROPERTY()๊ฐ€ ๋ถ™์–ด์žˆ๋Š” ๊ฐ์ฒด๋“ค์„ ํ•˜๋‚˜์”ฉ ํƒ์ƒ‰ํ•˜๊ณ  ๋งˆํ‚นํ•œ๋‹ค.
  3. ๋ชจ๋‘ ๋ฐฉ๋ฌธ ํ–ˆ์„ ๋•Œ, ๋งˆํ‚น๋˜์ง€ ์•Š์€ ๊ฐ์ฒด๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด ์‚ญ์ œ(Sweep)ํ•œ๋‹ค.

๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์ด ํ• ๋‹น๋œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ญ์ œํ•  ๋•Œ ์–ด๋–ค ์ผ์ด ์ผ์–ด๋‚˜๋Š” ์ง€ ๋‹ค์Œ ์ฝ”๋“œ๋กœ ํ™•์ธํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

// TestObject.h
UCLASS()
class ADVANCEDPROJECT_API UTestObject : public UObject
{
	GENERATED_BODY()
	
public:
	void InitObject(FString InName);

private:

	FString MemoName;
};

// TestObject.cpp
void UTestObject::InitObject(FString InName)
{
	MemoName = InName;
}

// MyGCObserver.h
UCLASS()
class ADVANCEDPROJECT_API AMyGCObserver : public AActor
{
	GENERATED_BODY()

protected:
	virtual void BeginPlay() override;

public:	
	virtual void Tick(float DeltaTime) override;

	UPROPERTY()
	class UTestObject* SafeObject;

	class UTestObject* DangerObject; // ๋ฆฌํ”Œ๋ ‰์…˜ ์‹œ์Šคํ…œ ๋ฏธ๋“ฑ๋ก, GC ์ฒ˜๋ฆฌ ๋Œ€์ƒ
};

// MyGCObserver.cpp
void AMyGCObserver::BeginPlay()
{
	Super::BeginPlay();
	
	SafeObject = NewObject<UTestObject>();
	SafeObject->InitObject(TEXT("SafeObject"));

	DangerObject = NewObject<UTestObject>();
	DangerObject->InitObject(TEXT("DangerObject"));
}

// Called every frame
void AMyGCObserver::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	bool bSafeIf = SafeObject != nullptr;
	bool bSafeValid = SafeObject->IsValid();

	bool bDangerIf = DangerObject != nullptr;
	bool bDangerValid = DangerObject->IsValid();
}

์‹คํ–‰ํ•ด๋ณด๋ฉด ์•Œ๊ฒ ์ง€๋งŒ, ์—ฌ๊ธฐ์„œ DangerObject๋Š” GC์— ์˜ํ•ด์„œ ๋ฉ”๋ชจ๋ฆฌ ์ œ๊ฑฐ ๋Œ€์ƒ์ธ๋ฐ, ์ œ๊ฑฐ ๋˜๋”๋ผ๋„ bDangerIf์™€ bDangerValid ๋ชจ๋‘ true๋กœ ์œ ์ง€๋œ๋‹ค.

๊ทธ ์ด์œ ๋Š” ๋ฆฌํ”Œ๋ ‰์…˜ ์‹œ์Šคํ…œ์„ ๋“ฑ๋กํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ DangerObject์˜ ์กด์žฌ๋ฅผ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋ฆฌํ”Œ๋ ‰์…˜ ์‹œ์Šคํ…œ์— ๋“ฑ๋ก๋œ SafeObject์˜ ๊ฒฝ์šฐ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•ด์ œ๋˜๋ฉด ํ•ด๋‹น ๊ฐ’์„ nullptr๋กœ ๋ณ€๊ฒฝํ•˜์ง€๋งŒ, DangerObject๋Š” ํ•ด์ œ๋œ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ๊ณ„์† ์ง€๋‹ˆ๋Š” Dangling Pointer ํ˜„์ƒ์„ ์ดˆ๋ž˜ํ•œ๋‹ค.

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— nullptr, IsValid()๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ƒํƒœ๋ฅผ ์ฒดํฌํ•˜๊ณ  ์ ‘๊ทผํ•˜๋ ค๊ณ  ํ•˜๋ฉด ํฌ๋ž˜์‹œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

IsValid()์˜ ๋Œ€์ฒด์žฌ๋กœ๋Š” IsValidLowLevel()์„ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ, ํ•ด๋‹น ํฌ์ธํ„ฐ๊ฐ€ ์œ ํšจํ•œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ๋Š”์ง€ ๊นŠ๊ฒŒ ํŒŒ๊ณ ๋“ค์–ด์•ผ ํ•œ๋‹ค.

void AMyGCObserver::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	bool bSafeIf = SafeObject != nullptr;
	bool bSafeValid = SafeObject->IsValidLowLevel();

	bool bDangerIf = DangerObject != nullptr;
	bool bDangerValid = DangerObject->IsValidLowLevel();
}

ํ•˜์ง€๋งŒ IsValidLowLevel()๋Š” ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์œ ํšจํ•œ์ง€๋งŒ ์ฒดํฌํ•  ๋ฟ, ์—ฌ์ „ํžˆ DangerObject๋Š” Dangling Pointer ํ˜„์ƒ์„ ๋ง‰์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์•ฝํ•œ ์ฐธ์กฐ ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ(TWeakObjectPtr)๋กœ ๋จผ์ € ์„ ์–ธํ•ด์ค˜์•ผ ํ•œ๋‹ค.

์ด๋Ÿฌ๋ฉด, ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์‚ญ์ œ๋˜๋”๋ผ๋„ DangerObject ๋˜ํ•œ nullptr๋กœ ์„ธํŒ…์ด ๋œ๋‹ค.

TWeakObjectPtr<class UTestObject> DangerObject;

UBT, UHT


UBT(Unreal Build Tool)์€ ํ”„๋กœ์ ํŠธ์— ์–ด๋–ค ๋ชจ๋“ˆ์ด ์žˆ๊ณ , ์–ด๋–ค ํ”Œ๋žซํผ(Windows, Android, PS5 ๋“ฑ)์œผ๋กœ ๋นŒ๋“œํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

์†”๋ฃจ์…˜์„ ์—ด์–ด๋ณด๋ฉด .Target.cs, .Build.cs ํŒŒ์ผ์ด ์žˆ๋Š”๋ฐ, ํ•ด๋‹น ํŒŒ์ผ๋“ค์„ ๋ฐ”ํƒ•์œผ๋กœ ์ „์ฒด์ ์ธ ๋นŒ๋“œ ๊ณ„ํš์„ ์„ธ์šฐ๋Š” ๊ฒŒ UBT์˜ ์—ญํ• ์ด๋‹ค.

UHT(Unreal Header Tool)์€ C++์˜ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์—ฌ๋Ÿฌ ์–ธ๋ฆฌ์–ผ ๋งคํฌ๋กœ(UPROPERTY(), UFUNCTION() ๋“ฑ) ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋ฉฐ ์—ฌ๋Ÿฌ ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ๋“ค์„ ์—๋””ํ„ฐ์— ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ๋„๋ก .generated.hํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์ค€๋‹ค.

๊ทธ๋ž˜์„œ ์–ธ๋ฆฌ์–ผ์˜ ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ์˜ ํ๋ฆ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. UBT๊ฐ€ ํ”„๋กœ์ ํŠธ์˜ ๋นŒ๋“œ ๊ณผ์ •์„ ์ด๊ด„ํ•œ๋‹ค.
  2. UHT๊ฐ€ ์–ธ๋ฆฌ์–ผ์˜ ๋งคํฌ๋กœ, ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ ๋“ฑ์„ ์ˆ˜์ง‘ํ•˜์—ฌ ์—๋””ํ„ฐ์— ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ๋„๋ก .generated.h๋ฅผ ์ œ๊ณตํ•ด์ค€๋‹ค.
  3. C++ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ปดํŒŒ์ผ์„ ์ง„ํ–‰ํ•˜๊ณ  ๋ง์ปค๊ฐ€ ์ปดํŒŒ์ผ๋œ ์˜ค๋ธŒ์ ํŠธ ํŒŒ์ผ์„ ์กฐ๋ฆฝํ•œ๋‹ค.

CDO


CDO(Class Default Object)๋Š” UObject ํด๋ž˜์Šค๋งˆ๋‹ค ํ•œ ๊ฐœ์”ฉ ์ง€๋‹ˆ๋Š” ๊ธฐ๋ณธ๊ฐ’ ๋ณด๊ด€์†Œ์ด๋‹ค.

ํด๋ž˜์Šค ๋‹น 1๊ฐœ์˜ CDO๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, CDO๋Š” ์—”์ง„์ด ์‹คํ–‰๋˜๋Š” ์‹œ์ ์—์„œ ์ƒ์„ฑ๋œ๋‹ค.

CDO๊ฐ€ ์—†๋Š” ํ‘œ์ค€ C++๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ 100๊ฐœ ์ƒ์„ฑํ•œ๋‹ค๊ณ  ํ•˜๋ฉด, 100๊ฐœ์˜ ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ ์†๋„๊ฐ€ ์กฐ๊ธˆ ๋А๋ฆฌ์ง€๋งŒ, ์–ธ๋ฆฌ์–ผ์€ ์ธ์Šคํ„ด์Šค์— CDO ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋ณต์‚ฌ๋งŒ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์†๋„๊ฐ€ ์กฐ๊ธˆ์ด๋‚˜๋งˆ ๋นจ๋ผ์ง„๋‹ค.

๋˜ํ•œ ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์ „์†ก ์‹œ, ์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค์˜ ์ •๋ณด๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ์ „๋‹ฌํ•  ํ•„์š” ์—†์ด CDO์˜ ๋‚ด์šฉ๊ณผ ์ฐจ์ด์ ๋งŒ ์ „๋‹ฌํ•˜๋ฉด ๋˜๋ฏ€๋กœ ๋Œ€์—ญํญ์ด ์ค„์–ด๋“ ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ, ๊ฒŒ์ž„์ด ์‹คํ–‰๋˜๊ณ  ์žˆ๋Š” ์ƒํ™ฉ์—์„œ CDO๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฑด ์„ธ์ด๋ธŒ ํŒŒ์ผ์ด๋‚˜ ์—ฌ๋Ÿฌ ์‹œ์Šคํ…œ๋“ค๊ณผ์˜ ํ˜ธํ™˜์„ฑ์„ ์ €ํ•ดํ•˜๋Š” ํ–‰๋™์ด๊ธฐ ๋•Œ๋ฌธ์— CDO๋ฅผ ๊ฒŒ์ž„ ์‹คํ–‰ ์ค‘์—๋Š” ์ˆ˜์ •ํ•˜๋ฉด ์•ˆ๋œ๋‹ค.

profile
๊ฒŒ์ž„ ๊ฐœ๋ฐœ ์ง€๋ง์ƒ

0๊ฐœ์˜ ๋Œ“๊ธ€