언리얼의 레퍼런스와 메모리 관리

언리얼에서 에셋을 참조하는 방식은 크게 Hard ReferenceSoft Reference로 나눌 수 있다.

항목 Hard Reference Soft Reference
저장하는 것 실제 에셋 포인터 에셋 경로
자동 로드 자동 로드됨 자동 로드 안 됨
메모리 사용 참조 에셋까지 메모리 사용 로드 전에는 경로 정보만 사용
사용 편의성 쉽다 로드 코드가 필요하다
로딩 제어 어렵다 직접 제어 가능
실시간 성능 초기 로딩/메모리 부담 가능 비동기 로드로 분산 가능
대표 타입 UObject*, UClass*, TSubclassOf TSoftObjectPtr, TSoftClassPtr

Hard Reference

Hard Reference는 오브젝트가 에셋을 직접 참조하는 방식이다.

UCLASS()
class AMyCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    UPROPERTY(EditDefaultsOnly)
    USkeletalMesh* CharacterMesh;

    UPROPERTY(EditDefaultsOnly)
    UTexture2D* CharacterIcon;
};

위 코드에서 CharacterMesh, CharacterIcon은 모두 Hard Reference다.
Hard Reference가 걸린 오브젝트가 로드되면, 연결된 에셋도 즉시 메모리에 올라간다.

graph LR
    A[MyCharacter 로드] --> B[CharacterMesh 즉시 로드]
    A --> C[CharacterIcon 즉시 로드]
    A --> D[참조된 모든 에셋 로드]

예를 들어 캐릭터가 다음과 같은 에셋을 직접 참조한다고 가정해보자.

class AWarriorCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    UPROPERTY()
    USkeletalMesh* WarriorMesh;        // 50MB

    UPROPERTY()
    TArray<UTexture2D*> ArmorTextures; // 4MB × 10 = 40MB

    UPROPERTY()
    TArray<USoundBase*> VoiceLines;    // 2MB × 20 = 40MB

    UPROPERTY()
    UAnimationAsset* IdleAnimation;    // 10MB

    UPROPERTY()
    UAnimationAsset* WalkAnimation;    // 10MB

    UPROPERTY()
    UAnimationAsset* RunAnimation;     // 10MB
};

캐릭터 하나가 사용하는 메모리는 대략 다음과 같다.
이런 캐릭터가 10명 로드되면 단순 계산으로 약 1.6GB가 필요하다.
Hard Reference는 사용하기 쉽고, 로드된 이후에는 바로 사용할 수 있다.
하지만 참조가 많아질수록 초기 로딩 시간과 메모리 사용량이 커진다.

장점 단점
사용이 단순하다 참조 에셋이 모두 같이 로드된다
로드 후 즉시 사용 가능하다 메모리 사용량이 커질 수 있다
별도 로드 코드가 필요 없다 참조 체인이 길어지면 예상보다 많은 에셋이 로드된다
필수 에셋 관리에 적합하다 초기 로딩 시간이 길어질 수 있다

Soft Reference

Soft Reference는 에셋을 직접 들고 있는 것이 아니라, 에셋의 경로만 저장하는 방식

UCLASS()
class ALyraCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    UPROPERTY(EditDefaultsOnly)
    TSoftObjectPtr<USkeletalMesh> CharacterMesh;

    UPROPERTY(EditDefaultsOnly)
    TSoftObjectPtr<UTexture2D> CharacterIcon;
};

지금 당장 로드하지 않고, 필요할 때 가져올 수 있도록 경로만 기억한다.
Soft Reference는 에셋 경로만 가지고 있으므로, 참조 대상이 자동으로 메모리에 올라가지 않는다.

Soft Reference는 Asset Registry와 함께 동작한다.
Asset Registry는 프로젝트 내 에셋의 이름, 패키지 경로, 실제 .uasset 정보를 관리한다.

단계 의미
에셋 이름 에디터에서 보이는 에셋 이름
패키지 경로 /Game/... 형태의 경로
.uasset 실제 디스크에 존재하는 에셋 파일

Soft Reference 로드 방식

Soft Reference는 경로만 저장하므로, 실제 사용하기 전에는 로드가 필요하다.

로드 방식은 크게 두 가지다.

방식 특징
동기 로드 로드가 끝날 때까지 현재 흐름이 멈춘다
비동기 로드 백그라운드에서 로드하고, 완료 후 콜백을 실행한다

동기 로드

동기 로드는 LoadSynchronous()를 사용한다.

void ALyraCharacter::EquipCharacterMesh()
{
    if (!CharacterMesh.IsNull())
    {
        USkeletalMesh* LoadedMesh = CharacterMesh.LoadSynchronous();

        if (LoadedMesh)
        {
            GetMesh()->SetSkeletalMesh(LoadedMesh);
            UE_LOG(LogTemp, Log, TEXT("메시 로드 완료"));
        }
    }
}

동기 로드는 로드가 끝날 때까지 현재 흐름을 멈춘다.

작은 에셋이거나 로딩 화면 중이라면 문제가 적지만, 큰 에셋을 게임플레이 중 동기 로드하면 순간적인 프리즈가 발생할 수 있다.

비동기 로드

비동기 로드는 FStreamableManager를 통해 요청한다.

void ALyraCharacter::EquipCharacterMeshAsync()
{
    if (!CharacterMesh.IsNull())
    {
        FStreamableManager& Streamable = UAssetManager::GetStreamableManager();

        UE_LOG(LogTemp, Log, TEXT("메시 로드 시작"));

        Streamable.RequestAsyncLoad(
            CharacterMesh.ToSoftObjectPath(),
            FStreamableDelegate::CreateWeakLambda(this, [this]()
            {
                if (USkeletalMesh* LoadedMesh = CharacterMesh.Get())
                {
                    GetMesh()->SetSkeletalMesh(LoadedMesh);
                    UE_LOG(LogTemp, Log, TEXT("메시 로드 완료"));
                }
                else
                {
                    UE_LOG(LogTemp, Error, TEXT("메시 로드 실패"));
                }
            })
        );
    }
}

비동기 로드는 로드 요청만 보내고 게임 흐름은 계속 진행된다.

큰 에셋을 게임플레이 중 로드해야 한다면 동기 로드보다 비동기 로드가 적합하다.
다만 비동기 로드는 로드 완료 시점이 나중이므로, 콜백에서 안전성 체크가 필요하다.

FStreamableDelegate::CreateWeakLambda(this, [this]()
{
    if (!IsValid(this))
    {
        return;
    }

    if (USkeletalMesh* LoadedMesh = CharacterMesh.Get())
    {
        GetMesh()->SetSkeletalMesh(LoadedMesh);
    }
})

비동기 로드에서는 다음 상황을 고려해야 한다.

  • 로드 완료 전에 Actor가 Destroy될 수 있다.
  • 로드 완료 전에 다른 에셋으로 교체 요청이 들어올 수 있다.
  • 로드 실패 가능성이 있다.
  • 로드된 에셋을 적용할 시점이 아직 유효한지 확인해야 한다.

TSoftObjectPtr와 TSoftClassPtr

타입 용도 예시
TSoftObjectPtr<T> 에셋 파일 참조 메시, 텍스처, 사운드, 머티리얼, 애니메이션
TSoftClassPtr<T> 클래스 타입 참조 Actor Class, Widget Class, GameplayAbility Class

TSoftObjectPtr는 실제 에셋 파일을 참조할 때 사용한다.

TSoftObjectPtr<USkeletalMesh> MeshPath;
TSoftObjectPtr<UTexture2D> TexturePath;
TSoftObjectPtr<USoundBase> SoundPath;

TSoftClassPtr는 클래스 타입을 경로로 참조할 때 사용한다.

TSoftClassPtr<AWeapon> WeaponClassPath;
TSoftClassPtr<UGameplayAbility> AbilityPath;

사용 기준

상황 추천
항상 필요한 것 Hard Reference
필요할 수도 있고 아닐 수도 있는 것 Soft Reference
작고 즉시 필요한 에셋 Hard Reference 가능
크고 늦게 필요한 에셋 Soft Reference + Async Load

주의할 점

Soft Reference는 자동으로 로드되지 않는다.
사용 전에는 다음 중 하나가 필요하다.

  • LoadSynchronous()
  • RequestAsyncLoad()
  • AssetManager 기반 로딩

또한 Soft Reference도 로드하면 결국 메모리를 사용한다.

Soft Reference로 Blueprint Class를 로드했더라도, 그 Blueprint 내부에 Hard Reference가 많으면 많은 에셋이 연쇄적으로 로드될 수 있다.

graph LR
    A[SoftClassPtr로 BP_Weapon 로드] --> B[BP_Weapon 내부 Hard Reference 로드]
    B --> C[Mesh]
    B --> D[Material]
    B --> E[Texture]
    B --> F[Sound]
    B --> G[Effect]

따라서 Soft Reference를 썼다고 해서 메모리 문제가 자동으로 해결되는 것은 아니다.
로드되는 에셋 내부의 참조 구조까지 함께 확인해야 한다.

반응형

'Unreal Engine' 카테고리의 다른 글

UE MVVM Plugin  (0) 2026.07.01
Asset Manager + PrimaryAsset  (0) 2026.06.30
DataTable, DataAsset  (0) 2026.06.26
UE Input 3  (0) 2026.06.25
UE Input 2  (0) 2026.06.24