언리얼/언리얼기초

[간식의 언리얼 노트] DataAsset vs PrimaryDataAsset, 뭐가 다른 거야?

듀부두부 2026. 2. 25. 20:03

들어가며

언리얼에서 데이터 중심 설계를 하다 보면 반드시 마주치는 두 클래스가 있습니다. UDataAssetUPrimaryDataAsset.

"둘 다 데이터 담는 거 아냐?" 저도 처음엔 그렇게 생각했는데, Asset Manager와의 연동 여부에서 결정적인 차이가 납니다.

이 글은 언리얼 엔진의 기본 구조를 알고 있는 분들을 대상으로 합니다.


UDataAsset

기본 개념

UDataAssetUObject를 상속받는 단순한 데이터 컨테이너입니다.

UCLASS()
class UWeaponData : public UDataAsset
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere)
    FString WeaponName;

    UPROPERTY(EditAnywhere)
    float Damage;

    UPROPERTY(EditAnywhere)
    UTexture2D* Icon;
};

Content Browser에서 우클릭 → Miscellaneous → Data Asset으로 생성할 수 있습니다.

특징

  • 단순한 데이터 홀더: 값을 담고 에디터에서 편집하는 용도
  • 하드 레퍼런스: 직접 UPROPERTY로 참조
  • Asset Manager와 무관: 로딩/언로딩을 직접 관리
  • 소규모 데이터에 적합

사용법

// 하드 레퍼런스로 직접 참조
UPROPERTY(EditAnywhere)
UWeaponData* WeaponData;

// 사용
void AMyCharacter::Attack()
{
    float damage = WeaponData->Damage;
    UE_LOG(LogTemp, Log, TEXT("Weapon: %s, Damage: %f"), *WeaponData->WeaponName, damage);
}

간단하죠. 에디터에서 슬롯에 끌어다 놓으면 끝입니다. 하지만 이 방식은 에셋이 많아지면 문제가 생깁니다. 하드 레퍼런스이기 때문에, 참조하는 쪽이 로드될 때 데이터도 함께 메모리에 올라옵니다.


UPrimaryDataAsset

기본 개념

UPrimaryDataAssetUDataAsset을 상속받으면서 Asset Manager 시스템과 통합됩니다.

UCLASS()
class UItemData : public UPrimaryDataAsset
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    FString ItemName;

    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    int32 MaxStackSize;

    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    TSoftObjectPtr<UTexture2D> Icon;  // 소프트 레퍼런스!
};

핵심: GetPrimaryAssetId()

UPrimaryDataAssetGetPrimaryAssetId()를 자동으로 구현합니다. 이게 Asset Manager가 에셋을 식별하고 관리하는 핵심 키입니다.

// PrimaryDataAsset이 자동으로 해주는 것
FPrimaryAssetId UPrimaryDataAsset::GetPrimaryAssetId() const
{
    // 클래스 이름 + 에셋 이름으로 고유 ID 생성
    return FPrimaryAssetId(GetClass()->GetFName(), GetFName());
}
// 결과 예시: "ItemData:Sword_01"

일반 UDataAsset에는 이 함수가 없습니다. Asset Manager가 에셋을 찾으려면 이 ID가 필요한데, UDataAsset은 ID 자체가 없으니 관리 대상이 될 수 없는 겁니다.

Asset Manager로 비동기 로딩

// Asset Manager를 통한 비동기 로딩
void AMyGameMode::LoadItem(FPrimaryAssetId ItemId)
{
    UAssetManager& Manager = UAssetManager::Get();

    Manager.LoadPrimaryAsset(ItemId, {},
        FStreamableDelegate::CreateUObject(this, &AMyGameMode::OnItemLoaded, ItemId));
}

void AMyGameMode::OnItemLoaded(FPrimaryAssetId ItemId)
{
    UAssetManager& Manager = UAssetManager::Get();
    UItemData* Item = Cast<UItemData>(Manager.GetPrimaryAssetObject(ItemId));

    if (Item)
    {
        UE_LOG(LogTemp, Log, TEXT("Loaded: %s"), *Item->ItemName);
    }
}

하드 레퍼런스 없이, 필요할 때만 로딩하고 쓸 수 있습니다.


비교 정리

  UDataAsset UPrimaryDataAsset
상속 UObject UDataAsset
Asset Manager 연동
GetPrimaryAssetId() 없음 자동 구현
비동기 로딩 직접 구현해야 함 Asset Manager 지원
에셋 탐색/스캔 수동 Asset Manager가 자동 스캔
번들/쿠킹 규칙 수동 관리 규칙 기반 자동
레퍼런스 방식 주로 하드 레퍼런스 소프트 레퍼런스 권장
적합한 상황 간단한 설정 데이터 게임 콘텐츠 (아이템, 스킬 등)

언제 뭘 써야 하나?

UDataAsset이 적합한 경우

  • 게임 설정값 (밸런스 테이블, 난이도 설정)
  • 항상 메모리에 올라가 있어도 되는 작은 데이터
  • 특정 클래스에서만 직접 참조하는 데이터
  • 에셋 수가 적고 (10개 미만) Asset Manager까지 쓸 필요 없는 경우

UPrimaryDataAsset이 적합한 경우

  • 아이템, 스킬, 캐릭터 등 게임 콘텐츠 데이터
  • 수십~수백 개의 에셋을 체계적으로 관리해야 할 때
  • 비동기 로딩이 필요할 때 (오픈 월드, 대규모 인벤토리)
  • 에셋 번들링/쿠킹 규칙이 필요할 때

판단 기준

에셋이 10개 이상? → PrimaryDataAsset
비동기 로딩 필요? → PrimaryDataAsset
그 외 간단한 설정? → DataAsset

PrimaryDataAsset 사용 시 필수 설정

Asset Manager가 에셋을 스캔하려면 DefaultGame.ini에 경로를 알려줘야 합니다.

[/Script/Engine.AssetManagerSettings]
+PrimaryAssetTypesToScan=(PrimaryAssetType="ItemData",AssetBaseClass=/Script/MyGame.UItemData,bHasBlueprintClasses=True,Directories=((Path="/Game/Data/Items")))

이 설정이 없으면 Asset Manager가 에셋을 찾지 못합니다. PrimaryDataAsset을 만들어 놓고 "왜 안 되지?" 하는 경우 대부분 이 설정을 빠뜨린 겁니다.

프로젝트 설정 > Game > Asset Manager에서 UI로도 설정할 수 있습니다.


흔한 실수

1. PrimaryDataAsset인데 DefaultGame.ini 설정을 안 함

위에서 말한 그대로입니다. LoadPrimaryAsset()이 null을 반환한다면 가장 먼저 확인하세요.

2. 소규모 프로젝트에서 PrimaryDataAsset 남용

아이템 5개짜리 프로토타입인데 Asset Manager까지 세팅하는 건 오버엔지니어링입니다. DataAsset으로 시작해서, 에셋이 늘어나면 그때 마이그레이션해도 늦지 않습니다.

3. PrimaryDataAsset에 하드 레퍼런스 사용

PrimaryDataAsset의 장점은 필요할 때만 로딩하는 건데, 내부에서 UTexture2D*처럼 하드 레퍼런스를 쓰면 결국 다 같이 로드됩니다. TSoftObjectPtr<>을 쓰세요.

// ❌ 하드 레퍼런스 - PrimaryDataAsset의 의미가 없어짐
UPROPERTY(EditAnywhere)
UTexture2D* Icon;

// ✅ 소프트 레퍼런스 - 필요할 때만 로드
UPROPERTY(EditAnywhere)
TSoftObjectPtr<UTexture2D> Icon;

마무리

정리하면:

  • UDataAsset = 단순 데이터 컨테이너. 설정값, 소규모 데이터에 적합
  • UPrimaryDataAsset = Asset Manager 연동. 게임 콘텐츠 대량 관리에 적합
  • 판단 기준은 "Asset Manager가 필요한가?" 한 가지입니다

더 공부하고 싶다면?

Reference