언리얼의 레퍼런스와 메모리 관리
언리얼에서 에셋을 참조하는 방식은 크게 Hard Reference와 Soft 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 |