서비스
교육 철학

데이터 테이블(Data Tables)

Abstract

📌
이 페이지는 데이터 테이블의 코어 쪽 구조 — 행이 어떻게 저장되고 조회 API 가 무엇을 제공하는지 — 를 다룹니다. 텍스트 파일을 만들어 실제로 게임에 붙이는 전 과정은 Unity 쪽 데이터 테이블 (Data Tables) 페이지에 ToyboxNightmare 의 적 스탯을 예로 단계별로 적어 두었습니다. 여기서는 "로드한 뒤 어떻게 꺼내 쓰는가"에 집중합니다.
테이블은 Dictionary<int, 행> 이다
내부는 단순합니다. Id 를 키로 하는 딕셔너리 하나, 그리고 가장 작은 Id 와 가장 큰 Id 의 행을 따로 기억해 둡니다. 여기서 조회 방법별 비용이 갈립니다.
조회
비용
쓰는 때
GetDataRow(id)
즉시 (딕셔너리 조회)
기본. 가능하면 항상 이것
MinIdDataRow / MaxIdDataRow
즉시
"1레벨 데이터", "최고 레벨 데이터"
GetDataRow(조건) / GetDataRows(조건)
전체 순회
가끔. 매 프레임 부르면 안 된다
GetDataRows(정렬)
전체 복사 후 정렬
목록 화면을 열 때 한 번
Id 설계가 곧 성능이다
조건 검색이 느리다는 것은, 자주 찾는 기준을 Id 에 담으라는 뜻입니다. ToyboxNightmare 의 적을 테이블로 옮긴다고 하면, 지금은 주소 문자열("Zombunny")로 스탯을 찾고 있으므로 매번 조건 검색이 됩니다. 두 가지 해법이 있습니다.
// 방법 1: 로드 직후 한 번만 순회해서 별도 색인을 만든다 private readonly Dictionary<string, EnemyRow> mByAssetName = new Dictionary<string, EnemyRow>(); private void BuildIndex(IDataTable<EnemyRow> table) { EnemyRow[] rows = table.GetAllDataRows(); for (int i = 0; i < rows.Length; i++) { mByAssetName.Add(rows[i].AssetName, rows[i]); } } // 방법 2: 게임 코드가 처음부터 Id 로 적을 가리킨다 (스포너 테이블에 주소 대신 적 Id 를 적는다) EnemyRow row = table.GetDataRow(enemyTypeId);
C#
복사
RPG 처럼 테이블이 많은 게임은 Id 에 규칙을 넣어 묶기도 합니다 — 10000번대는 무기, 20000번대는 방어구, 10203 은 "무기 / 2등급 / 3번" 같은 식입니다.
가비지를 만들지 않는 조회
배열을 돌려주는 메서드는 부를 때마다 새 배열을 만듭니다. 자주 부르는 곳에서는 결과를 받을 리스트를 넘기는 쪽을 씁니다.
private readonly List<EnemyRow> mRowBuffer = new List<EnemyRow>(); table.GetAllDataRows(mRowBuffer); // mRowBuffer 를 비우고 채워 준다. 새 할당 없음
C#
복사
런타임 수정 — 어디에 쓰나
AddDataRow(문자열) / RemoveDataRow(id) 로 로드된 테이블을 고칠 수 있습니다. 쓰임새는 두 가지입니다: 서버가 내려준 이벤트 데이터를 기존 테이블에 덧붙이기, 그리고 테스트 코드에서 파일 없이 테이블 채우기.
⚠️
이 에디션에서 다른 점 · 본문을 읽을 때 주의할 점
•
본문 예제는 행 클래스가 IDataRow 를 직접 구현합니다. 그대로도 동작하지만, 게임 코드에서는 Unity 쪽이 제공하는 DataRowBase 를 상속하는 것이 관례입니다(Id 를 override, 필요한 ParseDataRow 만 override).
•
LoadDataTable<T>(...) 라는 메서드는 없습니다. CreateDataTable<T>() 로 만든 뒤 DataTableBase 로 캐스팅해 ReadData(주소) 를 부릅니다. 본문 예제도 고쳐 두었습니다.
•
같은 행 타입으로 테이블을 두 번 만들면 Already exist data table 예외입니다. 한 타입으로 여러 테이블이 필요하면(예: 스테이지별 스폰 표) CreateDataTable<T>("Stage01") 처럼 이름을 붙입니다.
•
본문 "리소스 시스템 연동"의 세 가지 소스(에셋 / 디스크 바이너리 / 가상 파일 시스템) 중 이 에디션에서 동작하는 것은 에셋(Addressables 의 TextAsset) 하나입니다.
•
float.Parse 는 기기 언어에 따라 소수점 해석이 달라집니다. 행을 파싱할 때 CultureInfo.InvariantCulture 를 넘기세요. 본문 예제의 float.Parse(columnTexts[index++]) 는 독일어·프랑스어 등으로 설정된 기기에서 0.5 를 잘못 읽습니다.

데이터 테이블(Data Tables)

Game Framework의 Data Table 시스템은 게임 내의 표 형태 데이터를 로드, 파싱, 접근할 수 있도록 구조화된 방식을 제공합니다.
이 시스템은 아이템 속성, 캐릭터 능력치, 레벨 설정 등과 같이 테이블 형태로 관리하면 효율적인 게임 데이터를 다루는 데 적합합니다.
설정 데이터(Configuration)나 다국어(Localization) 시스템 등 다른 데이터 관리 기능은 별도의 문서를 참고하세요.

개요

Data Table 시스템을 사용하면 다음과 같은 작업이 가능합니다.
•
텍스트 파일, 바이너리 파일 등 다양한 소스에서 표 형태 데이터 로드 및 파싱
•
ID 또는 사용자 정의 조건으로 데이터 행(Row) 접근
•
데이터 행을 열거, 필터링, 정렬
•
런타임에 데이터 행 추가/제거
•
테이블 내 최소/최대 ID 값 빠르게 조회

핵심 구성 요소

1. Data Table Manager

•
모든 데이터 테이블을 관리하는 핵심 컴포넌트
•
GameFrameworkModule을 상속
•
데이터 테이블 생성, 가져오기, 파괴 담당

2. Data Table

•
DataTable<T> 인스턴스로 표현되며, T는 IDataRow 인터페이스 구현 타입
•
각 테이블은 고유 ID로 인덱싱된 데이터 행 집합을 저장

3. Data Row

•
IDataRow를 구현한 커스텀 클래스
•
각 행은 고유 ID를 가진 단일 레코드
•
문자열 또는 바이너리 데이터를 구조화된 형태로 변환하는 ParseDataRow 메서드 구현

구현 세부사항

데이터 저장 방식

•
내부적으로 Dictionary<int, T> 사용
→ O(1) 속도로 ID 기반 조회 가능
•
최소 ID 행과 최대 ID 행을 캐싱하여 빠른 접근 지원
// 이 에디션의 소스에서는 mDataSet / mMinIdDataRow / mMaxIdDataRow private readonly Dictionary<int, T> m_DataSet; private T m_MinIdDataRow; private T m_MaxIdDataRow;
C#
복사

데이터 로딩

•
DataProvider 시스템을 사용해 리소스 로드 및 파싱
•
에셋에서 읽는 ReadData 는 비동기, 이미 메모리에 있는 문자열·바이트를 넣는 ParseData 는 동기
•
텍스트, 바이너리, 커스텀 포맷 가능

데이터 파싱

•
각 IDataRow 클래스는 다음 메서드 중 필요한 것을 구현
bool ParseDataRow(string dataRowString, object userData); bool ParseDataRow(byte[] dataRowBytes, int startIndex, int length, object userData);
C#
복사

사용 예시

데이터 행 클래스 정의

public class PlayerLevelData : IDataRow { public int Id { get; private set; } public string Name { get; private set; } public int ExpRequired { get; private set; } public float AttackBonus { get; private set; } public bool ParseDataRow(string dataRowString, object userData) { string[] columnTexts = dataRowString.Split('\t'); int index = 0; Id = int.Parse(columnTexts[index++]); Name = columnTexts[index++]; ExpRequired = int.Parse(columnTexts[index++]); AttackBonus = float.Parse(columnTexts[index++]); return true; } public bool ParseDataRow(byte[] dataRowBytes, int startIndex, int length, object userData) { // 바이너리 파싱 구현 return true; } }
C#
복사

데이터 테이블 로드 및 접근

// 매니저 가져오기 IDataTableManager dataTableManager = GameFrameworkEntry.GetModule<IDataTableManager>(); // 생성 + 로드 (LoadDataTable 이라는 메서드는 없다. 만든 뒤 DataTableBase.ReadData 를 부른다. 비동기) IDataTable<PlayerLevelData> created = dataTableManager.CreateDataTable<PlayerLevelData>(); ((DataTableBase)created).ReadData("PlayerLevel"); // ↓ ReadDataSuccess 이후 // 가져오기 IDataTable<PlayerLevelData> playerLevelTable = dataTableManager.GetDataTable<PlayerLevelData>(); // ID로 행 조회 PlayerLevelData level5 = playerLevelTable.GetDataRow(5); // 조건으로 조회 PlayerLevelData[] highLevels = playerLevelTable.GetDataRows(row => row.ExpRequired > 1000); // 정렬 PlayerLevelData[] sortedLevels = playerLevelTable.GetDataRows((a, b) => a.ExpRequired - b.ExpRequired);
C#
복사

주요 기능

•
ID 조회
T GetDataRow(int id)
C#
복사
•
조건 검색
T[] GetDataRows(Predicate<T> condition)
C#
복사
•
정렬
T[] GetDataRows(Comparison<T> comparison)
C#
복사
•
조건 + 정렬
T[] GetDataRows(Predicate<T> condition, Comparison<T> comparison)
C#
복사

런타임 수정 기능

•
행 추가
bool AddDataRow(string dataRowString, object userData); bool AddDataRow(byte[] dataRowBytes, int startIndex, int length, object userData);
C#
복사
•
행 삭제
bool RemoveDataRow(int id);
C#
복사
•
전체 삭제
void RemoveAllDataRows();
C#
복사

리소스 시스템 연동

•
ResourceManager와 통합하여 다양한 소스에서 로드 가능
◦
애셋 파일(텍스트, 바이너리, 커스텀)
◦
디스크 바이너리 파일
◦
가상 파일 시스템(VFS)
•
DataProvider가 실제 리소스 로드와 파싱 담당

메모리 효율성

•
Dictionary 기반 O(1) 조회
•
최소/최대 ID 캐싱
•
행 객체는 로드 시 new T() 로 한 번 만들어져 테이블이 파괴될 때까지 유지 (풀링은 하지 않는다. 조회 중 할당이 없다는 뜻)
•
결과 리스트 재사용 메서드 제공
void GetDataRows(Predicate<T> condition, List<T> results) void GetDataRows(Comparison<T> comparison, List<T> results) void GetAllDataRows(List<T> results)
C#
복사

결론

Game Framework의 Data Table 시스템은 구조화된 데이터를 효율적으로 관리하고, 다양한 소스에서 유연하게 로드할 수 있도록 설계되었습니다.
IDataRow 인터페이스를 구현하면, ID 기반 빠른 조회, 조건 검색, 정렬, 런타임 수정까지 가능하며, ResourceManager와의 연동으로 프로젝트 전반에서 활용성이 높습니다.