서비스
교육 철학

데이터 테이블 (Data Tables)

Abstract

📌
데이터 테이블은 엑셀 표를 게임 안으로 가져오는 통로입니다. 한 줄이 한 행, 한 행이 C# 객체 하나가 됩니다. 수치를 코드에서 빼내 텍스트 파일로 옮기면, 밸런스를 고칠 때 재컴파일이 필요 없고 기획자가 직접 손댈 수 있습니다.
실무에서 필요한 순간
•
적·아이템·스킬이 수십 종이 되어 코드 상수로는 한눈에 안 들어올 때
•
밸런스 수치를 프로그래머가 아닌 사람이 고쳐야 할 때
•
빌드 후에도 수치를 패치하고 싶을 때 (Addressables 원격 그룹과 조합)
직접 해 보기 — ToyboxNightmare 의 적 스탯을 테이블로 옮기기
예제는 적 스탯을 EnemyTable.cs 의 C# 배열로 들고 있습니다. 이것을 데이터 테이블로 바꾸는 전 과정입니다.
① 텍스트 파일을 만든다 — Enemy.txt, 탭 구분. # 으로 시작하는 줄은 주석입니다.
#Id AssetName MaxHP Attack Interval Score 1 Zombunny 100 10 0.5 10 2 ZomBear 100 10 0.5 10 3 ZombieDuck 120 20 1.0 20 4 Clown 150 30 1.5 25 5 Hellephant 200 35 2.0 50
Plain Text
복사
② 행 클래스를 만든다 — DataRowBase 를 상속하고 Id 와 ParseDataRow 를 구현합니다.
using UnityGameFramework.Runtime; namespace ToyBoxNightmare { public class EnemyRow : DataRowBase { private int mId; public override int Id { get { return mId; } } public string AssetName { get; private set; } public int MaxHitPoints { get; private set; } public int AttackDamage { get; private set; } public float TimeBetweenAttacks { get; private set; } public int ScoreValue { get; private set; } public override bool ParseDataRow(string dataRowString, object userData) { const int ColumnCount = 6; string[] columns = dataRowString.Split('\t'); if (columns.Length != ColumnCount) return false; mId = int.Parse(columns[0]); AssetName = columns[1]; MaxHitPoints = int.Parse(columns[2]); AttackDamage = int.Parse(columns[3]); TimeBetweenAttacks = float.Parse(columns[4], System.Globalization.CultureInfo.InvariantCulture); ScoreValue = int.Parse(columns[5]); return true; } } }
C#
복사
③ Addressables 에 등록한다 — Enemy.txt 를 Groups 창에 끌어다 놓고 주소를 EnemyTable 로 줍니다.
④ 로드한다 — 비동기이므로 성공 이벤트를 기다립니다. 게임 시작 전 로딩용 Procedure 에 두는 것이 정석입니다.
DataTableComponent dataTable = GameEntry.GetComponent<DataTableComponent>(); EventComponent events = GameEntry.GetComponent<EventComponent>(); events.Subscribe(LoadDataTableSuccessEventArgs.EventId, OnLoadDataTableSuccess); events.Subscribe(LoadDataTableFailureEventArgs.EventId, OnLoadDataTableFailure); IDataTable<EnemyRow> table = dataTable.CreateDataTable<EnemyRow>(); ((DataTableBase)table).ReadData("EnemyTable");
C#
복사
⑤ 꺼내 쓴다 — 성공 이벤트 이후 어디서든.
IDataTable<EnemyRow> table = GameEntry.GetComponent<DataTableComponent>().GetDataTable<EnemyRow>(); EnemyRow row = table.GetDataRow(5); // Id 로 EnemyRow duck = table.GetDataRow(r => r.AssetName == "ZombieDuck"); // 조건으로
C#
복사
⚠️
자주 걸리는 곳
•
ReadData 는 IDataTable<T> 에 없습니다. CreateDataTable<T>() 가 돌려준 객체를 DataTableBase 로 캐스팅해서 부릅니다.
•
ReadData 직후에는 테이블이 비어 있습니다. 성공 이벤트 전에 GetDataRow 를 하면 null 입니다.
•
같은 행 타입으로 테이블을 두 번 만들면 예외입니다. 재시작 흐름이 있다면 HasDataTable<T>() 로 먼저 확인합니다.
•
float.Parse 는 기기 언어 설정에 따라 소수점 기호가 달라집니다. CultureInfo.InvariantCulture 를 넘기세요.
•
이 에디션은 에디터 툴이 없어 엑셀 → 텍스트 변환을 제공하지 않습니다. 엑셀에서 "탭으로 분리된 텍스트"로 저장하면 그대로 쓸 수 있습니다.

데이터 테이블 (Data Tables)

관련 소스 파일

•
Scripts/Editor/ResourceAnalyzer/ResourceAnalyzerController.cs
•
Scripts/Editor/ResourceCollection/Asset.cs
•
Scripts/Editor/ResourceCollection/ResourceCollection.cs
•
Scripts/Editor/ResourceEditor/ResourceEditorController.cs
•
Scripts/Runtime/Config/ConfigComponent.cs
•
Scripts/Runtime/Config/ConfigHelperBase.cs
•
Scripts/Runtime/Config/DefaultConfigHelper.cs
•
Scripts/Runtime/DataTable/DataRowBase.cs
•
Scripts/Runtime/DataTable/DataTableComponent.cs
•
Scripts/Runtime/DataTable/DataTableHelperBase.cs
•
Scripts/Runtime/DataTable/DefaultDataTableHelper.cs
•
Scripts/Runtime/Localization/DefaultLocalizationHelper.cs
•
Scripts/Runtime/Localization/LocalizationComponent.cs
•
Scripts/Runtime/Localization/LocalizationHelperBase.cs

개요

데이터 테이블 시스템은 Unity Game Framework의 특화된 구성 요소로, 구조화된 게임 데이터를 효율적으로 관리하기 위해 설계되었습니다.
이 시스템은 다양한 소스로부터 테이블 형태의 데이터(행과 열) 를 로드, 파싱, 접근할 수 있는 강력한 메커니즘을 제공합니다.
게임 설정, 적(Enemy) 능력치, 아이템 데이터베이스 등 게임 고유의 구조적 정보의 기반 역할을 합니다.
•
비테이블 형식 설정 값: 👉 Configuration System 참고
•
다국어 및 지역화 데이터: 👉 Localization System 참고

아키텍처

데이터 테이블 시스템은 컴포넌트 - 매니저 - 헬퍼 패턴을 따릅니다.
이는 관심사의 분리를 명확히 하고, 헬퍼 클래스를 통한 유연한 커스터마이징을 가능하게 합니다.

핵심 구성 요소

1. DataTableComponent

•
데이터 테이블 시스템의 주요 인터페이스
•
역할:
◦
데이터 테이블 매니저 초기화
◦
데이터 테이블 생성, 접근, 파괴
◦
데이터 로딩 및 이벤트 디스패치
◦
데이터 테이블 존재 여부 및 내용 쿼리 제공
DataTableComponent dataTableComponent = GameEntry.GetComponent<DataTableComponent>();
C#
복사

2. DataTableHelperBase

•
추상 클래스 (abstract base class)
•
인터페이스:
◦
IDataProviderHelper<DataTableBase>: 데이터 읽기 및 로딩
◦
IDataTableHelper: 데이터 테이블 관련 작업
•
확장하여 커스텀 헬퍼 구현 가능

3. DefaultDataTableHelper

•
DataTableHelperBase의 기본 구현체
•
기능:
◦
텍스트 또는 바이너리 에셋에서 데이터 테이블 읽기
◦
문자열/바이트 배열 파싱
◦
사용 종료 시 데이터 에셋 해제
•
특징:
◦
텍스트 기반/바이너리 데이터 테이블 모두 지원
◦
.bytes 파일 특별 처리
◦
리소스 시스템과 통합 관리

4. DataRowBase

•
사용자 정의 데이터 행(Row)의 기반 클래스
•
특징:
◦
IDataRow 인터페이스 구현
◦
필수: Id 속성 구현
◦
가상 메서드 제공: 문자열/바이트 기반 데이터 파싱
•
모든 게임 전용 데이터 행 클래스는 반드시 DataRowBase 상속 필요

데이터 흐름

데이터 테이블 로드 및 파싱 시 데이터 흐름은 다음과 같습니다:
1.
DataTableComponent → 로드 요청
2.
DefaultDataTableHelper → 데이터 읽기 및 파싱
3.
DataRowBase 상속 클래스 → 실제 데이터 구조화
4.
DataTableComponent → 접근/조회 가능 상태로 관리

지원 데이터 포맷

1. 텍스트 형식 (Text Format)

•
각 줄이 데이터 행(Row)
•
#으로 시작하는 줄은 주석 처리
•
AddDataRow를 통해 테이블에 추가
•
예시:
10001 Sword 500 10002 Shield 800
Plain Text
복사

2. 바이너리 형식 (Binary Format)

•
텍스트보다 로드 속도 빠름 (하지만 사람이 읽기 힘듦)
•
특징:
◦
BinaryReader로 데이터 탐색
◦
7비트 인코딩 정수로 행 길이 지정
◦
각 행을 바이트 배열로 처리
•
대규모 데이터셋 및 프로덕션 빌드 최적화에 유용

사용 예시

1. 데이터 행 정의

public class ItemData : DataRowBase { private int mId; // Id 는 DataRowBase 의 abstract 프로퍼티다. 반드시 override 한다 public override int Id { get { return mId; } } public string Name { get; private set; } public int Price { get; private set; } public override bool ParseDataRow(string dataRowString, object userData) { string[] columns = dataRowString.Split('\t'); if (columns.Length < 3) return false; mId = int.Parse(columns[0]); Name = columns[1]; Price = int.Parse(columns[2]); return true; } }
C#
복사

2. 데이터 테이블 생성 및 로드

DataTableComponent dataTableComponent = GameEntry.GetComponent<DataTableComponent>(); IDataTable<ItemData> itemDataTable = dataTableComponent.CreateDataTable<ItemData>(); // ReadData 는 IDataTable<T> 가 아니라 DataTableBase 에 있다. 인자는 Addressables 주소, 로드는 비동기 ((DataTableBase)itemDataTable).ReadData("Items");
C#
복사

3. 데이터 접근

ItemData item = itemDataTable.GetDataRow(10001); Debug.Log($"Item: {item.Name}, Price: {item.Price}"); ItemData[] items = itemDataTable.GetAllDataRows(); for (int i = 0; i < items.Length; i++) { // 아이템 데이터 처리 } // 조건으로 고르기 ItemData[] expensive = itemDataTable.GetDataRows(row => row.Price >= 500);
C#
복사

리소스 시스템과의 통합

•
DefaultDataTableHelper → ResourceComponent를 통해 에셋 로딩
•
데이터 테이블이 필요 없어지면 자동 언로드

관리 기능

테이블 관리

작업
설명
메서드
존재 확인
테이블 존재 여부 확인
HasDataTable<T>()
생성
새 데이터 테이블 생성
CreateDataTable<T>()
가져오기
기존 데이터 테이블 반환
GetDataTable<T>()
전체 조회
모든 테이블 반환
GetAllDataTables()
파괴
데이터 테이블 제거
DestroyDataTable<T>()

행 관리

작업
설명
메서드
존재 확인
특정 데이터 행 존재 여부 확인
HasDataRow(id)
단일 조회
ID로 데이터 행 조회
GetDataRow(id)
복수 조회
조건(Predicate)에 맞는 데이터 행 조회
GetDataRows(condition)
전체 조회
모든 데이터 행 가져오기
GetAllDataRows()

성능 최적화

•
바이너리 포맷 지원 → 로드 속도 향상
•
Cached Bytes → 바이너리 작업 시 메모리 사전 할당
•
행은 로드 시 한 번만 생성 → 테이블이 파괴될 때까지 유지되므로 조회 중 할당이 없음
메모리 관리 메서드:
•
EnsureCachedBytesSize(size) : 캐시 메모리 확보
•
FreeCachedBytes() : 메모리 해제

이벤트 지원

로드 결과는 EventComponent 로 전달됩니다. Subscribe(XxxEventArgs.EventId, handler) 로 받습니다.
•
LoadDataTableSuccessEventArgs: 데이터 성공적으로 로드됨
•
LoadDataTableFailureEventArgs: 데이터 로드 실패
•
LoadDataTableUpdateEventArgs: 로딩 진행 상황 업데이트
•
LoadDataTableDependencyAssetEventArgs: 종속 에셋 로드 시 호출

요약

데이터 테이블 시스템은 Unity Game Framework에서 구조화된 게임 데이터 관리를 위한 핵심 도구입니다.
•
데이터 로딩, 파싱, 접근을 분리해 클린 아키텍처 구현
•
텍스트/바이너리 지원 및 성능 최적화 기능 내장
•
리소스 시스템과 통합되어 효율적 관리 가능
➡️ 간단한 아이템 목록부터 RPG의 대규모 데이터베이스까지 확장 가능한 유연한 구조를 제공합니다.