PART 1 ยท ๊ฐ•์˜ 4/4

Serialization๊ณผ GC

Archive ์‹œ์Šคํ…œ, UPROPERTY ํ”Œ๋ž˜๊ทธ, SaveGame ์ง๋ ฌํ™”๊ฐ€ GC์™€ ์–ด๋–ป๊ฒŒ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š”์ง€ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค

SECTION 01

Archive ์‹œ์Šคํ…œ ๊ฐœ์š”

FArchive๋ฅผ ํ†ตํ•œ ์ง๋ ฌํ™”์˜ ๊ธฐ๋ณธ ์›๋ฆฌ์™€ GC ๊ด€๋ จ ์•„์นด์ด๋ธŒ

FArchive์˜ ์—ญํ• 

FArchive๋Š” UE์˜ ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™” ์ถ”์ƒ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. GC์™€ ๊ด€๋ จ๋œ ํŠน์ˆ˜ ์•„์นด์ด๋ธŒ์ธ FArchiveUObject๋Š” UObject ์ฐธ์กฐ๋ฅผ ์ง๋ ฌํ™”ํ•  ๋•Œ GC๊ฐ€ ์ด ์ฐธ์กฐ๋ฅผ ์ถ”์ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

C++ // FArchive ๊ธฐ๋ณธ ์‚ฌ์šฉ void UMyObject::Serialize(FArchive& Ar) { Super::Serialize(Ar); // UPROPERTY๊ฐ€ ์•„๋‹Œ ๋ฐ์ดํ„ฐ์˜ ์ˆ˜๋™ ์ง๋ ฌํ™” Ar << CustomData; // UObject ์ฐธ์กฐ ์ง๋ ฌํ™” (GC ์ฐธ์กฐ ์ž๋™ ๋“ฑ๋ก) Ar << ReferencedObject; } // GC ๊ด€๋ จ ์ฃผ์š” ์•„์นด์ด๋ธŒ ํƒ€์ž… FArchiveUObject // UObject ์ฐธ์กฐ ์ง๋ ฌํ™” ๊ธฐ๋ณธ FReferenceCollectorArchive // GC ์ฐธ์กฐ ์ˆ˜์ง‘ ์ „์šฉ FArchiveSaveTaggedProperties // ํƒœ๊ทธ๋œ ํ”„๋กœํผํ‹ฐ๋งŒ ์ €์žฅ FArchiveCountMem // ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ๊ณ„์‚ฐ
์ง๋ ฌํ™”์™€ GC ์ฐธ์กฐ์˜ ๊ด€๊ณ„

์ง๋ ฌํ™” ๊ณผ์ •์—์„œ UObject ์ฐธ์กฐ๋ฅผ operator<<๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉด, FArchiveUObject๊ฐ€ ํ•ด๋‹น ์ฐธ์กฐ๋ฅผ ์ธ์‹ํ•˜์—ฌ ๋กœ๋“œ ์‹œ ์˜ฌ๋ฐ”๋ฅธ ๊ฐ์ฒด๋กœ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” GC์˜ ์ฐธ์กฐ ๊ทธ๋ž˜ํ”„์™€๋Š” ๋ณ„๊ฐœ์ด์ง€๋งŒ, ์—ญ์ง๋ ฌํ™” ์‹œ GC ์ฐธ์กฐ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ณต์›ํ•˜๋Š” ๋ฐ ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

SECTION 02

UPROPERTY ์ง๋ ฌํ™” ํ”Œ๋ž˜๊ทธ

ํ”„๋กœํผํ‹ฐ ์ง€์ •์ž๊ฐ€ ์ง๋ ฌํ™”์™€ GC์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ

์ง๋ ฌํ™” ๊ด€๋ จ UPROPERTY ์ง€์ •์ž

์ง€์ •์ž ์ง๋ ฌํ™” GC ์ถ”์  ์„ค๋ช…
UPROPERTY() ์ž๋™ ์ถ”์ ๋จ ๊ธฐ๋ณธ - ์ €์žฅ ๋ฐ GC ๋ชจ๋‘ ํ™œ์„ฑ
Transient ์ œ์™ธ ์ถ”์ ๋จ ์ €์žฅ ์•ˆ ๋จ, GC๋Š” ์ •์ƒ ์ถ”์ 
DuplicateTransient ์ž๋™ ์ถ”์ ๋จ ๋ณต์ œ ์‹œ์—๋งŒ ์ œ์™ธ
NonTransactional ์ž๋™ ์ถ”์ ๋จ Undo/Redo ํŠธ๋žœ์žญ์…˜ ์ œ์™ธ
SaveGame ์„ธ์ด๋ธŒ ํฌํ•จ ์ถ”์ ๋จ ์„ธ์ด๋ธŒ ๊ฒŒ์ž„ ์ง๋ ฌํ™”์— ํฌํ•จ
SkipSerialization ์™„์ „ ์ œ์™ธ ์ถ”์ ๋จ ๋ชจ๋“  ์ง๋ ฌํ™”์—์„œ ์ œ์™ธ
ํ•ต์‹ฌ: ์ง๋ ฌํ™”์™€ GC๋Š” ๋…๋ฆฝ์ 

Transient์ด๋‚˜ SkipSerialization์€ ์ง๋ ฌํ™”๋งŒ ์ œ์™ธํ•  ๋ฟ, GC ์ฐธ์กฐ ์ถ”์ ์€ ์—ฌ์ „ํžˆ ํ™œ์„ฑ์ž…๋‹ˆ๋‹ค. UPROPERTY()๋กœ ์„ ์–ธํ•œ UObject* ์ฐธ์กฐ๋Š” ์–ด๋–ค ์ง๋ ฌํ™” ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๋“  GC๊ฐ€ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ UPROPERTY๊ฐ€ ์—†์œผ๋ฉด ์ง๋ ฌํ™”๋„ GC ์ถ”์ ๋„ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

C++ UCLASS() class UGameState : public UObject { GENERATED_BODY() // ์ €์žฅ + GC ์ถ”์  UPROPERTY(SaveGame) int32 PlayerLevel; // GC ์ถ”์ ๋งŒ, ์ €์žฅ ์•ˆ ๋จ UPROPERTY(Transient) UTexture2D* CachedTexture; // ์ €์žฅ + GC ์ถ”์  (์„ธ์ด๋ธŒ์—๋„ ํฌํ•จ) UPROPERTY(SaveGame) TArray<UItem*> Inventory; };
SECTION 03

SaveGame ์ง๋ ฌํ™”์™€ GC

์„ธ์ด๋ธŒ ๊ฒŒ์ž„ ์‹œ์Šคํ…œ์—์„œ UObject ์ฐธ์กฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•

USaveGame๊ณผ ๊ฐ์ฒด ์ฐธ์กฐ

USaveGame ํด๋ž˜์Šค๋Š” ๊ฒŒ์ž„ ์ƒํƒœ๋ฅผ ๋””์Šคํฌ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ UObject ์ฐธ์กฐ๋ฅผ ์ง์ ‘ ์ €์žฅํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค - ๊ฐ์ฒด ํฌ์ธํ„ฐ๋Š” ์„ธ์…˜ ๊ฐ„ ์œ ํšจํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

C++ UCLASS() class UMySaveGame : public USaveGame { GENERATED_BODY() // ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•: ๊ฐ’ ๋ฐ์ดํ„ฐ ์ €์žฅ UPROPERTY(SaveGame) int32 Score; // ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•: ์†Œํ”„ํŠธ ์ฐธ์กฐ๋กœ ์—์…‹ ๊ฒฝ๋กœ ์ €์žฅ UPROPERTY(SaveGame) TSoftObjectPtr<UStaticMesh> EquippedMesh; // ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•: ๊ตฌ์กฐ์ฒด๋กœ ๋ฐ์ดํ„ฐ ์ €์žฅ UPROPERTY(SaveGame) TArray<FItemSaveData> SavedItems; // ์ž˜๋ชป๋œ ๋ฐฉ๋ฒ•: UObject* ์ง์ ‘ ์ €์žฅ // UPROPERTY(SaveGame) // UItem* EquippedItem; // ๋กœ๋“œ ์‹œ ๋Œ•๊ธ€๋ง! }; // ์„ธ์ด๋ธŒ/๋กœ๋“œ ์‚ฌ์šฉ๋ฒ• UMySaveGame* SaveObj = NewObject<UMySaveGame>(); SaveObj->Score = CurrentScore; UGameplayStatics::SaveGameToSlot(SaveObj, "Slot1", 0); // SaveObj๋Š” ์ดํ›„ GC ๋Œ€์ƒ (์ฐธ์กฐ ์œ ์ง€ ํ•„์š” ์‹œ UPROPERTY์— ์ €์žฅ)
TSoftObjectPtr์˜ GC ํŠน์„ฑ

TSoftObjectPtr์€ ์•ฝํ•œ ์ฐธ์กฐ์ด๋ฏ€๋กœ ๋Œ€์ƒ ์—์…‹์ด GC๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ์‚ฌ์šฉ ์ „์— LoadSynchronous()๋‚˜ ๋น„๋™๊ธฐ ๋กœ๋“œ๋กœ ์—์…‹์„ ๋กœ๋“œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋กœ๋“œ๋œ ์—์…‹์„ ์œ ์ง€ํ•˜๋ ค๋ฉด ๋ณ„๋„์˜ ๊ฐ•ํ•œ ์ฐธ์กฐ(UPROPERTY UObject*)๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

SECTION 04

ํŒจํ‚ค์ง€ ๋กœ๋”ฉ๊ณผ GC ์ƒํ˜ธ์ž‘์šฉ

์—์…‹ ๋กœ๋”ฉ/์–ธ๋กœ๋”ฉ ์‹œ GC ๋™์ž‘๊ณผ ์ฐธ์กฐ ํ•ด๊ฒฐ

ํŒจํ‚ค์ง€ ๋กœ๋“œ ์‹œ GC ๊ณ ๋ ค์‚ฌํ•ญ

์—์…‹ ํŒจํ‚ค์ง€๊ฐ€ ๋กœ๋“œ๋˜๋ฉด ๋‚ด๋ถ€์˜ ๋ชจ๋“  UObject๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  GUObjectArray์— ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค. ์—ญ์ง๋ ฌํ™” ๊ณผ์ •์—์„œ FObjectImport๋ฅผ ํ†ตํ•ด ์™ธ๋ถ€ ์ฐธ์กฐ๊ฐ€ ํ•ด๊ฒฐ๋˜๋ฉฐ, ์ด ์ฐธ์กฐ๋“ค์ด GC ๊ทธ๋ž˜ํ”„์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.

๋กœ๋”ฉ ํ๋ฆ„ // ํŒจํ‚ค์ง€ ๋กœ๋“œ ์‹œ GC ๊ด€๋ จ ํ๋ฆ„ LoadPackage() โ”œโ”€โ”€ CreateExport() // UObject ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ โ”‚ โ””โ”€โ”€ NewObject() // GUObjectArray ๋“ฑ๋ก โ”œโ”€โ”€ Serialize() // ํ”„๋กœํผํ‹ฐ ์—ญ์ง๋ ฌํ™” โ”‚ โ””โ”€โ”€ ResolveImport() // ์™ธ๋ถ€ ์ฐธ์กฐ ํ•ด๊ฒฐ โ”œโ”€โ”€ PostLoad() // ๋กœ๋“œ ํ›„ ์ดˆ๊ธฐํ™” โ””โ”€โ”€ ConditionalPostLoad() // ์กฐ๊ฑด๋ถ€ ํ›„์ฒ˜๋ฆฌ // ํŒจํ‚ค์ง€ ์–ธ๋กœ๋“œ ์‹œ UnloadPackage() โ”œโ”€โ”€ ๋‚ด๋ถ€ ๊ฐ์ฒด๋“ค์˜ ์ฐธ์กฐ ํ•ด์ œ โ”œโ”€โ”€ MarkAsGarbage() // ๊ฐ€๋น„์ง€ ๋งˆํ‚น โ””โ”€โ”€ ๋‹ค์Œ GC ์‚ฌ์ดํด์—์„œ ์‹ค์ œ ์ˆ˜์ง‘ // ๊ฐ•์ œ GC์™€ ํŒจํ‚ค์ง€ ์–ธ๋กœ๋“œ CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS); // ๋˜๋Š” GEngine->ForceGarbageCollection(true);
Soft/Lazy ์ฐธ์กฐ์™€ ํŒจํ‚ค์ง€ ๋กœ๋”ฉ

FSoftObjectPath์™€ TSoftObjectPtr๋Š” ์—์…‹ ๊ฒฝ๋กœ๋งŒ ์ €์žฅํ•˜๋ฏ€๋กœ ๋Œ€์ƒ ํŒจํ‚ค์ง€๋ฅผ ์ฆ‰์‹œ ๋กœ๋“œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” GC์—๋„ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์•„ ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์ ์ธ ์—์…‹ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ์‚ฌ์šฉ ์‹œ์ ์— ๋น„๋™๊ธฐ ๋กœ๋“œ๋ฅผ ์š”์ฒญํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

SUMMARY

ํ•ต์‹ฌ ์š”์•ฝ

์ด ๊ฐ•์˜์—์„œ ๋ฐฐ์šด ๋‚ด์šฉ
  • FArchive ์‹œ์Šคํ…œ์€ ์ง๋ ฌํ™”์™€ GC ์ฐธ์กฐ๋ฅผ ๋ชจ๋‘ ๊ด€๋ฆฌํ•˜๋ฉฐ, FArchiveUObject๊ฐ€ UObject ์ฐธ์กฐ๋ฅผ ํŠน๋ณ„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค
  • Transient, SkipSerialization ๋“ฑ์˜ ํ”Œ๋ž˜๊ทธ๋Š” ์ง๋ ฌํ™”๋งŒ ์ œ์–ดํ•˜๋ฉฐ, GC ์ถ”์ ์—๋Š” ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค
  • SaveGame์—์„œ UObject ํฌ์ธํ„ฐ๋ฅผ ์ง์ ‘ ์ €์žฅํ•˜๋ฉด ์•ˆ ๋˜๋ฉฐ, TSoftObjectPtr๋‚˜ ๊ตฌ์กฐ์ฒด ๋ฐ์ดํ„ฐ๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
  • ํŒจํ‚ค์ง€ ๋กœ๋“œ ์‹œ ์—ญ์ง๋ ฌํ™”๋ฅผ ํ†ตํ•ด GC ์ฐธ์กฐ ๊ทธ๋ž˜ํ”„๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ณต์›๋ฉ๋‹ˆ๋‹ค
  • TSoftObjectPtr๋Š” ์•ฝํ•œ ์ฐธ์กฐ์ด๋ฏ€๋กœ ๋Œ€์ƒ์ด GC๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์‚ฌ์šฉ ์ „ ๋ช…์‹œ์  ๋กœ๋“œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค
PRACTICE

๋„์ „ ๊ณผ์ œ

๋ฐฐ์šด ๋‚ด์šฉ์„ ์ง์ ‘ ์‹ค์Šตํ•ด๋ณด์„ธ์š”

์‹ค์Šต 1: ์ง๋ ฌํ™” ํ”Œ๋ž˜๊ทธ์™€ GC ์ƒํ˜ธ์ž‘์šฉ ์‹คํ—˜

UPROPERTY์— Transient, DuplicateTransient, SaveGame ํ”Œ๋ž˜๊ทธ๋ฅผ ๊ฐ๊ฐ ์ ์šฉํ•œ ํ”„๋กœํผํ‹ฐ๋“ค์ด ํฌํ•จ๋œ UObject๋ฅผ ๋งŒ๋“ค๊ณ , FArchive๋ฅผ ํ†ตํ•ด ์ง๋ ฌํ™”ํ•  ๋•Œ ์–ด๋–ค ํ”„๋กœํผํ‹ฐ๊ฐ€ ํฌํ•จ๋˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

์‹ค์Šต 2: SaveGame ์ง๋ ฌํ™”์™€ ์ฐธ์กฐ ๋ณด์กด

TSoftObjectPtr๊ณผ ํ•˜๋“œ ๋ ˆํผ๋Ÿฐ์Šค(UPROPERTY UObject*)๋ฅผ ๋ชจ๋‘ ํฌํ•จํ•œ SaveGame ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ณ , FMemoryWriter/FMemoryReader๋กœ ์ €์žฅ/๋กœ๋“œ ์‚ฌ์ดํด์„ ๊ตฌํ˜„ํ•˜์—ฌ GC ํ›„ ์ฐธ์กฐ ์œ ํšจ์„ฑ์„ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

์‹ฌํ™” ๊ณผ์ œ: ์ปค์Šคํ…€ FArchive๋กœ ์ฐธ์กฐ ์ถ”์ 

FArchiveUObject๋ฅผ ์ƒ์†๋ฐ›์•„ ์ง๋ ฌํ™” ๊ณผ์ •์—์„œ ๋ฐœ๊ฒฌ๋˜๋Š” ๋ชจ๋“  UObject ์ฐธ์กฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š” ์ปค์Šคํ…€ ์•„์นด์ด๋ธŒ๋ฅผ ๊ตฌํ˜„ํ•˜์„ธ์š”. ์ด๋ฅผ ํ†ตํ•ด ํŠน์ • ๊ฐ์ฒด์˜ ์ง๋ ฌํ™” ์ฐธ์กฐ ๊ทธ๋ž˜ํ”„๋ฅผ ์ƒ์„ฑํ•˜๊ณ  GC ์ฐธ์กฐ ์ถ”์ ๊ณผ ๋น„๊ตํ•˜์„ธ์š”.