[Unreal Engine] Data Table

이매·2026년 3월 7일

Unreal Data Driven Design

목록 보기
2/12
post-thumbnail

1. Data Table

우리가 일반적으로 데이터를 관리할 때는 정형적인 구조를 가진 데이터 형식을 사용한다.
예를 들어 엑셀이나 데이터베이스에서 흔히 볼 수 있는 다음과 같은 형태이다.

이처럼 행(Row)과 열(Column)로 구성된 구조화된 데이터 형태를 데이터 테이블(Data Table)이라고 한다.

Unreal Engine에서도 이러한 개념을 기반으로 Data Table이라는 데이터 관리 시스템을 제공한다.
Data Table은 구조화된 데이터를 테이블 형태로 저장하고 관리할 수 있는 Asset이다.

여기서 각 행(Row)은 하나의 데이터 항목을 의미하고,
각 열(Column)은 해당 데이터가 가지는 속성값을 의미한다.

이처럼 Data Table은 여러 개의 데이터를 동일한 구조로 정리하여 관리할 수 있는 데이터 컨테이너라고 볼 수 있다.

1-1. Data Table이 필요한 이유

게임 개발에서는 캐릭터 능력치나 아이템 스탯과 같은 데이터가 매우 많이 등장한다.
일반적인 방식에서는 이러한 값들이 코드 내부에 직접 작성되는 경우가 많다.

float AttackDamage = 10.0f;
float Speed = 100.0f;

하지만 이런 방식은 데이터가 코드에 강하게 결합되는 문제를 만든다.

예를 들어 다음과 같은 상황이 발생할 수 있다.

  • 데이터를 수정하기 위해 코드를 수정해야 한다.
  • 값을 변경할 때마다 프로젝트를 다시 빌드해야 한다.
  • 기획자가 직접 데이터를 수정하기 어렵다.

Data Table을 사용하면 이러한 데이터를 코드에서 분리하여 별도의 데이터 Asset으로 관리할 수 있다.

Game Logic (C++)
    │
    │ 데이터 조회
    ▼
Data Table

즉 코드에서는 데이터를 직접 보관하지 않고, 필요할 때 Data Table에서 데이터를 읽어와 사용하는 방식으로 동작하게 된다.

이러한 구조는 다음과 같은 장점을 만든다.

  • 코드 수정 없이 데이터만 변경 가능
  • 기획자가 에디터에서 직접 밸런싱 가능
  • 여러 데이터를 한 곳에서 체계적으로 관리 가능

따라서 Data Table은 Data-Driven Design을 적용할 때 가장 먼저 쉽게 사용되는 데이터 관리 방식이라고 볼 수 있다.



2. Data Table 사용 예시

Unreal Engine의 Data Table은 UStruct를 기반으로 데이터를 저장하는 구조를 가진다.
즉, Data Table에 저장되는 모든 데이터는 동일한 구조를 가진 Struct 형태로 정의되어야 한다.

일반적인 테이블에서 Column이 데이터의 속성을 의미한다면, Unreal Engine에서는 이 Column 구조를 Struct로 정의한다.

예를 들어 캐릭터의 기본 능력치를 관리한다고 가정해 보자.
캐릭터는 다음과 같은 스탯을 가진다,

  • HP
  • MP
  • 공격력
  • 방어력

이러한 데이터를 Data Table로 관리하기 위해서는 일반적으로 다음과 같은 과정을 거친다.

  1. Struct 생성
    캐릭터의 능력치 구조를 정의하는 Struct를 만든다.

  2. Data Table 생성
    위에서 만든 Struct를 기반으로 Data Table을 생성한다.

  3. Data Load
    Blueprint 또는 C++에서 Data Table의 Row 데이터를 읽어와 캐릭터의 스탯으로 사용한다.

이제 Blueprint/C++에서 각각 Struct를 생성하고 Data Table을 만들어 실제로 사용하는 방법을 살펴보자.

2-1. Blueprint

2-1-1. Struct 생성

먼저 콘텐츠 브라우저에서 캐릭터 스탯을 저장하기 위한 Struct를 생성한다.

이후 Struct 내부에 다음과 같은 변수를 추가한다.

이 Struct는 Data Table의 Column 구조가 된다.
즉 Data Table에 들어가는 모든 데이터는 이 Struct와 동일한 형태를 가지게 된다.

2-1-2. Data Table 생성

Struct를 만들었다면 이제 이를 기반으로 Data Table Asset을 생성할 수 있다.

Data Table 생성 창이 나타나면 Row Struct를 선택해야 한다.
여기에 만들어뒀던 Character Stat Struct를 선택한다.

이렇게 하면 CharacterStat Struct를 기반으로 하는 Data Table이 생성된다.

2-1-3. Data Table 데이터 작성

Data Table을 열면 다음과 같은 형태의 테이블이 나타난다.

여기서 Add 버튼을 눌러 Data Table에 새로운 데이터를 추가할 수 있다.

Data Table에서

  • Row → 하나의 데이터 항목
  • Column → Struct에서 정의된 변수

를 의미한다.
즉 각 Row는 하나의 캐릭터 데이터를 나타내며, Column은 해당 캐릭터의 능력치 정보를 담는다.

이렇게 Data Table에 데이터를 작성하면 여러 캐릭터의 스탯을 하나의 테이블에서 관리할 수 있다.

또한 데이터를 직접 입력하는 것 외에도, CSV 파일을 가져와 Data Table로 Import하는 방식을 사용할 수도 있다.
이 방법을 사용하면 엑셀과 같은 외부 툴에서 데이터를 작성한 뒤 Unreal Engine으로 쉽게 가져올 수 있다.

2-1-4. Blueprint에서 Data Table 사용

  • StatComponent 생성

먼저 캐릭터의 스탯을 관리하기 위한 Actor Component를 생성한다.

  • StatComponent 구성

그리고 StatComponent의 BeginPlay에서 Data Table을 읽어 캐릭터의 초기 스탯을 설정할 수 있다.

  • Character에 StatComponent 추가

이제 캐릭터 Blueprint에 StatComponent를 추가한다.

  • Stat Component의 Data Table과 Character Type 설정

그리고 Character Blueprint의 Details 패널에서

Data Table과 원하는 CharacterType을 설정해주자.

이렇게 하면 게임 시작 시 StatComponent가 Data Table에서 Warrior Row 데이터를 읽어와 캐릭터의 스탯을 초기화한다.

2-2. C++

Blueprint와 동일한 구조를 C++에서도 구성할 수 있다.
C++에서는 Struct를 정의하고 Data Table에서 Row 데이터를 읽어 캐릭터의 스탯을 초기화하는 방식으로 구현한다.

전체 흐름은 Blueprint와 동일하다.

2-2-1. Struct 생성

먼저 Data Table에서 사용할 Struct를 정의한다.
Data Table에서 사용되는 Struct는 반드시 FTableRowBase를 상속해야 한다.

FCharacterStat.h

#pragma once

#include "Engine/DataTable.h"
#include "CoreMinimal.h"
#include "FCharacterStat.generated.h"

USTRUCT(BlueprintType)
struct FCharacterStat : public FTableRowBase
{
    GENERATED_BODY()

public:

    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    int32 HP;

    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    int32 MP;

    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    int32 Attack;

    UPROPERTY(EditAnywhere, BlueprintReadOnly)
    int32 Defense;
};

2-2-2. Data Table 생성

Struct를 만들었다면 Blueprint와 동일하게 Data Table Asset을 생성할 수 있다.

데이터는 블루프린트에서와 동일한 방식으로 추가해주면 된다.

2-2-3. C++에서 Data Table 사용

  • StatComponent 생성

이제 캐릭터 스탯을 관리하기 위한 StatComponent를 C++로 만든다.

StatComponent.h

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "StatComponent.generated.h"

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class DATADRIVENDESIGN_API UStatComponent : public UActorComponent
{
	GENERATED_BODY()

public:	
	UStatComponent();

protected:
	virtual void BeginPlay() override;

public:	
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UDataTable* CharacterStatTable;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	FName CharacterType;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	int32 HP;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	int32 MP;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	int32 Attack;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	int32 Defense;
};

StatComponent.cpp

#include "FCharacterStat.h"

void UStatComponent::BeginPlay()
{
	Super::BeginPlay();
	
	if (CharacterStatTable == nullptr)
	{
		return;
	}

	const FCharacterStat* Stat = CharacterStatTable->FindRow<FCharacterStat>(CharacterType, TEXT(""));

	if (Stat)
	{
		HP = Stat->HP;
		MP = Stat->MP;
		Attack = Stat->Attack;
		Defense = Stat->Defense;
	}
}

FindRow 함수를 사용하면 Row Name을 기준으로 Data Table의 데이터를 가져올 수 있다.
즉, CharacterType이 Warrior라면 Warrior Row 데이터를 가져오게 된다.

  • Character에 StatComponent 추가

이제 캐릭터 클래스에 StatComponent를 추가한다.

Character.h

public:

	...

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	class UStatComponent* StatComponent;

Character.cpp

#include "StatComponent.h"

ADataDrivenDesignCharacter::ADataDrivenDesignCharacter()
{
	...

	StatComponent = CreateDefaultSubobject<UStatComponent>(TEXT("StatComponent"));
}

이제 에디터에서 마찬가지로 Data Table과 Character Type을 설정해주면 블루프린트와 동일하게 동작한다.

여기서 Character Type을 Enum형태로 만들어서 관리하면 문자열 오타 문제 등을 줄이고 더 쉽게 설정이 가능하다.



3. Data Table 사용 시 고려 사항

3-1. 주의점

Data Table은 여러 데이터를 한 곳에서 관리할 수 있는 매우 편리한 시스템이지만, 사용할 때 몇 가지 주의할 점이 있다.

3-1-1. Row Name 관리

Data Table의 각 데이터는 Row Name을 기준으로 구분된다.

따라서 Blueprint나 C++ 코드에서 Row Name을 직접 참조하고 있는 경우, Row Name을 변경하면 해당 참조가 정상적으로 동작하지 않을 수 있다.

3-1-2. Struct 변경 시 데이터 초기화 가능성

Data Table은 Struct를 기반으로 생성되기 때문에 Struct 구조가 변경되면 기존 Data Table 데이터에 영향을 줄 수 있다.

예를 들어 Struct에 새로운 변수를 추가하면 기존 Row에는 해당 변수 값이 기본값으로 설정될 수 있으며, 변수 타입이나 이름을 변경하면 기존 데이터가 정상적으로 유지되지 않을 수 있다.

따라서 Struct 설계는 가능한 한 초기에 안정적으로 구성하는 것이 중요하다.

3-1-3. Asset 참조 시 로딩 문제

Data Table에서 Asset을 직접 참조할 때는 로딩 방식에도 주의해야 한다.

예를 들어 Data Table의 Struct 안에 다음과 같은 Asset을 직접 참조하도록 구성할 수 있다.

  • SkeletalMesh
  • Animation
  • Material
  • Sound

이처럼 Asset을 Data Table row가 Hard Reference를 포함하면, 해당 Asset들이 로딩 의존성에 포함될 수 있다.

즉 다음과 같은 상황이 발생할 수 있다.

Data Table 로드
→ Data Table이 참조하는 Asset 로드
→ 여러 Asset이 동시에 메모리에 로딩

이 경우 프로젝트 규모가 커질수록 불필요한 Asset이 한 번에 로드되어 메모리 사용량이 증가하거나 로딩 시간이 길어질 수 있다.

그래서 Asset을 직접 참조하기보다는 Soft Reference 방식을 사용하는 경우가 많다.
Soft Reference는 Asset의 경로만 저장하고 실제 Asset은 필요할 때 로드하기 때문에, 대규모 프로젝트에서 메모리 관리와 로딩 최적화에 도움이 된다.

3-2. 적합한 데이터

Data Table은 정적인 데이터를 관리하는 데 매우 적합한 시스템이다.
예를 들어 다음과 같은 데이터들이 대표적인 사용 사례이다.

  • 캐릭터 기본 스탯
  • 몬스터 능력치
  • 아이템 정보
  • 스킬 기본 수치
  • 레벨별 경험치 테이블

이러한 데이터들은 게임 실행 중에 크게 변경되지 않고, 여러 시스템에서 공통으로 참조하기 때문에 Data Table로 관리하면 매우 편리하다.

또한 CSV 파일을 사용하면 엑셀에서 데이터를 관리한 뒤 Unreal Engine으로 쉽게 임포트할 수 있는 장점도 있다.

3-3. 한계점

Data Table은 편리하지만 몇 가지 한계도 존재한다.

3-3-1. 복잡한 구조의 표현이 어려움.

예를 들어 다음과 같은 데이터는 관리와 표현이 어렵다.

  • 여러 단계의 중첩 데이터
  • 복잡한 객체 참조
  • 다양한 Asset 참조

3-3-2. 데이터 확장성이 제한적

Data Table은 하나의 Struct 기반으로 구성되기 때문에 서로 다른 형태의 데이터를 유연하게 관리하기 어렵다.
예를 들어

  • 무기
  • 소비 아이템
  • 장비

처럼 서로 다른 속성을 가진 데이터를 하나의 Data Table에서 관리하려고 하면 Struct가 지나치게 커질 수 있다.

3-3-3. 런타임에서 수정이 어려움

Data Table은 기본적으로 정적 데이터를 관리하기 위한 시스템이기 때문에 게임 실행 중에 데이터를 수정하거나 저장하는 용도로는 적합하지 않다.

따라서 런타임 중에 데이터의 변경이나 추가가 필요한 경우 Data Table이 아닌 다른 방법을 이용하는 것이 좋다.

3-3-4. 협업 시 충돌 문제

Data Table은 협업 환경에서 동시에 수정하기 어렵다는 한계가 있다.

Unreal Engine 프로젝트에서 대부분의 에셋은 Binary Asset 형태로 저장된다.
이러한 파일들은 일반적인 텍스트 파일처럼 Git에서 자동으로 병합(merge)하기 어렵기 때문에, 실제 프로젝트에서는 작업 시에 Checkout를 걸어두는 경우가 많다.

그래서 한 사람이 Data Table을 수정하고 있는 동안 다른 사람은 같은 파일을 수정할 수 없다.
따라서 팀 규모가 커질수록 콘텐츠 작업의 병목(Bottleneck)을 만들 수 있다.

profile
언리얼 엔진 주니어(신입) 개발자 | 소설 쓰는 취준 개발자

1개의 댓글

comment-user-thumbnail
2026년 3월 8일

설명 GOAT..

답글 달기