데이터 테이블과 블루프린트 연동
구조체 기반 데이터 테이블을 만들고 행 조회·반복 노드로 아이템 같은 정형 데이터를 블루프린트에 연결합니다.
게임에는 플레이어의 능력치, 아이템 정보, 퀘스트 내용, 몬스터 스탯처럼 관리해야 할 데이터가 많습니다. 이 값을 여러 블루프린트 그래프에 상수로 흩어 두면, 검의 공격력 하나를 바꾸려 해도 그 숫자가 들어간 그래프를 모두 찾아야 합니다. 또 기획자가 수치를 조정하려면 그래프를 열고 노드를 건드려야 하므로, 로직을 실수로 바꿀 위험도 생깁니다.
언리얼 엔진은 이런 정형 데이터를 로직에서 떼어 한곳에서 관리하도록 데이터 테이블(Data Table)이라는 에셋을 제공합니다. 데이터 테이블은 스프레드시트처럼 행과 열로 값을 정리하고, 블루프린트는 필요한 행을 찾아 읽기만 합니다. 이 글에서는 앞 글의 구조체로 행의 모양을 정하고, 아이템 데이터 테이블을 만든 뒤, 블루프린트에서 행을 조회해 성공과 실패를 나누어 처리해 봅니다.
행과 열로 정리한 데이터 테이블
데이터 테이블은 게임 데이터를 행(row)과 열(column)로 정리해 저장하는 에셋입니다. 행 하나는 기본 검이나 회복 물약처럼 데이터 항목 하나를 나타내고, 열 하나는 이름, 공격력, 무게처럼 그 항목의 속성 하나를 나타냅니다. 각 행에는 고유한 행 이름(Row Name)이 붙으며, 블루프린트는 이 이름을 키로 삼아 행을 찾습니다.
데이터 테이블을 쓰면 관련 데이터가 한 에셋에 모이므로 어디를 고쳐야 하는지가 분명해집니다. 기획자나 디자이너는 블루프린트를 열지 않고 테이블 에디터에서 행을 추가하거나 값을 고칠 수 있고, 새 아이템을 넣을 때도 그래프는 그대로 둔 채 행만 늘리면 됩니다. 블루프린트 쪽에서는 행 이름으로 원하는 데이터를 찾아 쓰기만 하면 됩니다.
데이터 테이블의 열은 마음대로 만드는 것이 아니라 구조체가 정합니다. 앞 글 구조체와 열거형의 기본 이해에서 만든 것과 같은 구조체를 행 구조체(Row Structure)로 지정하면, 그 구조체의 멤버 하나하나가 테이블의 열이 됩니다. 그래서 데이터 테이블을 만들기 전에 먼저 구조체를 준비합니다.
구조체로 행의 모양 정하기
콘텐츠 브라우저의 빈 곳을 오른쪽 클릭하고 블루프린트 하위 메뉴에서 Structure를 선택해, 아이템 정보를 담을 구조체 F_ItemData를 만듭니다. 구조체 에디터를 열고 다음 다섯 멤버를 추가한 뒤 저장합니다.
| 멤버 | 타입 | 의미 |
|---|---|---|
ItemName | Text | 화면에 보여 줄 아이템 이름 |
Description | Text | 아이템 설명 |
AttackPower | Integer | 공격력 |
Weight | Float | 무게 |
ItemMesh | Static Mesh (Object Reference) | 아이템을 표시할 메시 |
ItemName과 Description을 Text로 둔 것은 플레이어에게 보여 줄 문구이기 때문입니다. Text 타입은 지역화(번역)를 지원하므로 화면 표시용 문구에 알맞습니다. Weight는 프로젝트가 직접 정한 필드라서, 그 숫자가 킬로그램인지 다른 단위인지는 팀이 정해야 하며 엔진이 의미를 붙여 주지 않습니다.
데이터 테이블 만들고 행 채우기
콘텐츠 브라우저의 빈 곳을 오른쪽 클릭하고 Miscellaneous 하위 메뉴의 Data Table을 선택합니다. 행 구조체를 고르는 창이 뜨면 방금 만든 F_ItemData를 선택하고, 에셋 이름을 DT_Items로 짓습니다. 데이터 테이블 에셋에는 흔히 DT_ 접두사를 붙입니다.
DT_Items를 더블클릭해 데이터 테이블 에디터를 열고, 툴바의 Add 버튼으로 행을 추가합니다. 새 행마다 고유한 행 이름을 정하는데, 이 이름이 나중에 블루프린트에서 행을 찾을 때 쓰는 키가 됩니다. Sword_Basic, Shield_Wooden, Potion_Health처럼 종류와 이름이 드러나게 짓습니다. 그다음 각 열에 구조체 멤버에 맞는 값을 입력합니다. 이 글의 예제에서는 다음 두 행을 채우고 저장합니다.
| 행 이름 | ItemName | Description | AttackPower | Weight | ItemMesh |
|---|---|---|---|---|---|
Sword_Basic | 기본 검 | 평범한 검입니다. | 10 | 2.5 | Shape_Cylinder(예시) |
Potion_Health | 체력 회복 물약 | 체력을 회복시켜 줍니다. | 0 | 0.1 | Shape_Sphere(예시) |
행이 많다면 에디터에서 하나씩 입력하는 대신 스프레드시트에서 작성한 CSV 파일을 가져올 수도 있습니다. 콘텐츠 브라우저에서 CSV 파일을 임포트할 때 DataTable로 가져오고 행 타입으로 F_ItemData를 고르면 되며, 이때 CSV의 첫 번째 열에는 행 이름이 들어가야 합니다.
Get Data Table Row로 행 읽기
데이터가 준비되었다면 블루프린트에서 읽어 봅니다. 데이터를 쓸 액터 블루프린트(예: BP_PlayerCharacter)를 열고, 이벤트 그래프의 빈 곳을 오른쪽 클릭해 Get Data Table Row 노드를 추가합니다. 이 노드는 테이블 하나와 행 이름 하나를 받아, 그 행을 찾았는지에 따라 실행 흐름을 둘로 나눕니다.
행 조회 결과에 따른 적용 경로
DT_Items/Sword_Basic 조회 요청→Found/NotFound 결과→성공 Out Row 적용 또는 실패 정책의 실행 경계이다.
LOOKUP · FOUND / NOT FOUND
행을 찾은 경로에서만 Out Row를 적용합니다
Get Data Table Row에 DT_Items와 조회 키 Sword_Basic(Name)을 연결합니다. 한 조회의 실행 결과는 Row Found 또는 Row Not Found로 나뉩니다.
Get Data Table Row의 실행 결과입니다. Out Row를 사용하는 처리는 Row Found 쪽에만 연결합니다.
조회 키와 표시 이름을 구분합니다
Row Name은 Name 타입의 키입니다. 지역화 Text와 구별하고, 대소문자만 다른 행 이름으로 구분하지 않습니다. 실패 경로에는 오류 안내·취소·기본값 중 정한 정책을 연결합니다.
이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| Row 요청 DT_Items에서 Sword_Basic을 조회한다. |
|
|
| 성공 경계 Found에서만 Out Row를 유효 결과로 취급한다. |
|
|
| 값 적용 Row Found에서 Sword_Basic의 표시명 기본 검과 공격력 10을 읽는다. |
|
|
행 조회 결과에 따른 적용 경로
DT_Items/Sword_Basic 조회 요청→Found/NotFound 결과→성공 Out Row 적용 또는 실패 정책의 실행 경계이다.
LOOKUP · FOUND / NOT FOUND
행을 찾은 경로에서만 Out Row를 적용합니다
Get Data Table Row에 DT_Items와 조회 키 Sword_Basic(Name)을 연결합니다. 한 조회의 실행 결과는 Row Found 또는 Row Not Found로 나뉩니다.
Get Data Table Row의 실행 결과입니다. Out Row를 사용하는 처리는 Row Found 쪽에만 연결합니다.
조회 키와 표시 이름을 구분합니다
Row Name은 Name 타입의 키입니다. 지역화 Text와 구별하고, 대소문자만 다른 행 이름으로 구분하지 않습니다. 실패 경로에는 오류 안내·취소·기본값 중 정한 정책을 연결합니다.
이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| Row 요청 DT_Items에서 Sword_Basic을 조회한다. |
|
|
| 성공 경계 Found에서만 Out Row를 유효 결과로 취급한다. |
|
|
| 값 적용 Row Found에서 Sword_Basic의 표시명 기본 검과 공격력 10을 읽는다. |
|
|
위 그림은 Get Data Table Row의 두 실행 결과를 보여 줍니다. 노드에 DT_Items와 Name 타입의 조회 키 Sword_Basic을 연결하면, 한 번의 조회는 Row Found와 Row Not Found 가운데 한쪽으로만 실행이 이어집니다. 행 데이터를 담은 Out Row를 적용하는 처리는 Row Found 쪽에만 연결하고, Row Not Found는 오류 안내나 기본값을 쓰는 경로로 보냅니다. 그림 아래의 설명처럼 Row Name은 Name 타입의 키이므로 화면에 보여 줄 지역화 Text와 구별해야 합니다.
단계 그림은 같은 조회를 세 단계로 따라갑니다. 먼저 DT_Items에서 Sword_Basic 행을 요청합니다. 다음으로 행을 찾았으므로 실행이 Row Found로 나가며, 이 경로에서만 Out Row를 유효한 결과로 취급합니다. 마지막으로 Row Found 뒤의 처리가 Sword_Basic의 표시 이름 "기본 검"과 공격력 10을 읽습니다.
노드를 설정하는 순서는 다음과 같습니다. Data Table 핀의 드롭다운에서 DT_Items를 고르고, Row Name 핀에는 행 이름 Sword_Basic을 고르거나 입력합니다. 행 이름을 다른 노드에서 받아야 한다면 Make Literal Name 노드를 연결하고, 문자열로 받은 이름이라면 String To Name으로 바꿔 연결합니다. 그다음 조회를 시작할 이벤트의 실행 선을 노드의 실행 입력에 잇습니다.
Row Name 핀이 Name 타입이라는 점을 기억해 둡니다. Name은 대소문자를 구별하지 않고 비교하므로, Sword_Basic과 sword_basic은 같은 행 이름으로 취급됩니다. 그래서 대소문자만 다른 두 행 이름으로 서로 다른 항목을 구분하려고 하면 안 됩니다. 또 화면에 보여 줄 ItemName(Text)과 조회 키(Name)는 역할이 다르므로, 표시 문구를 바꿨다고 조회 키가 바뀌지 않고 표시 문구를 키로 쓸 수도 없습니다.
찾은 행을 꺼내 쓰기
Get Data Table Row의 Out Row 출력 핀은 행 구조체로 지정한 F_ItemData 타입의 값을 내놓습니다. 이 핀에서 끌어 Break F_ItemData를 고르면 멤버별로 값을 꺼낼 수 있습니다. 예를 들어 Row Found 실행 핀 뒤에 Print String을 잇고, ItemName을 디버그 출력용 문자열로 바꿔 In String에 연결하면 찾은 아이템의 이름이 화면에 나옵니다. 같은 경로에서 AttackPower를 플레이어의 공격력 변수에 Set할 수도 있습니다.
Row Not Found 경로에는 프로젝트의 정책에 맞는 처리를 연결합니다. 오류를 알리고 요청을 취소할 수도 있고, 정해 둔 기본값을 쓸 수도 있습니다. 중요한 것은 행을 찾지 못했을 때의 Out Row를 찾은 행처럼 적용하지 않는 것입니다. 실패 경로에서 공격력을 바꾸려면 그 경로에 명시적인 Set을 두어야 하고, 아무것도 연결하지 않으면 기존 값이 그대로 남습니다.
모든 행을 차례로 읽기
행 이름 하나가 아니라 테이블의 모든 행을 다뤄야 할 때도 있습니다. 상점 화면에 모든 아이템을 나열하는 경우가 그렇습니다. 이때는 Get Data Table Row Names 노드로 DT_Items의 행 이름 배열을 얻고, 그 배열을 For Each Loop에 연결해 반복마다 나오는 행 이름으로 Get Data Table Row를 실행합니다. 반복 안에서도 Row Found 경로에서만 Out Row를 쓰는 규칙은 같습니다.
첫 조회를 확인하는 순서
첫 조회는 BeginPlay에서 Get Data Table Row를 실행하고, Row Found 뒤의 Print String으로 결과를 출력하는 것으로 시작합니다. Sword_Basic을 조회해 "기본 검"과 AttackPower 10을 읽는지 확인한 다음, 행 이름을 테이블에 없는 값으로 바꿔 Row Not Found 경로도 실행되는지 봅니다. 두 경로를 모두 실행해 봐야 실패했을 때 무엇이 일어나는지 알 수 있습니다.
이때 표시 이름과 조회 키를 섞지 않습니다. ItemName의 "기본 검"은 화면에 보여 줄 문구이고, 조회 키 Sword_Basic과 같은 값일 필요가 없습니다. 오히려 표시 문구는 번역이나 기획 변경으로 자주 바뀌므로, 조회 키는 잘 바뀌지 않는 이름으로 따로 두는 편이 안전합니다.
데이터 테이블을 쓰는 곳
데이터 테이블은 아이템 말고도 정형 데이터가 반복되는 곳에 두루 쓰입니다. 게임을 시작할 때 DT_Items에서 행 이름으로 아이템 데이터를 가져와 아이템 액터의 속성을 정할 수 있고, DT_Dialogue 같은 테이블에 NPC의 대화 내용을 저장해 두고 퀘스트 진행 상황에 맞는 행을 꺼내 출력할 수도 있습니다. 오브젝트의 배치나 속성을 테이블에 두면 디자이너가 블루프린트를 고치지 않고 레벨의 여러 요소를 조정할 수 있습니다.
이렇게 데이터와 로직을 나누면 기획자는 테이블을, 프로그래머는 그래프를 맡아 서로의 작업을 덜 건드리게 됩니다. 새 항목을 넣을 때 그래프를 고칠 필요가 없으므로 게임이 커져도 유지보수가 쉬워집니다.
정리
- 데이터 테이블은 정형 데이터를 행과 열로 정리하는 에셋이며, 열의 모양은 행 구조체로 지정한 구조체가 정합니다.
- 각 행에는 고유한 행 이름이 붙고, 블루프린트는 이 Name 타입의 키로 행을 찾습니다. Name은 대소문자를 구별하지 않고 비교합니다.
Get Data Table Row는 조회 결과에 따라Row Found와Row Not Found로 실행을 나누며,Out Row는Row Found경로에서만 적용합니다.- 실패 경로에 아무 처리도 연결하지 않으면 기존 상태가 그대로 남고, 기본값으로 바꾸려면 그 경로에 명시적인
Set이 필요합니다. - 화면에 보여 줄 Text와 조회 키 Name은 역할이 다르므로 섞지 않습니다.
연습 문제
1. 없는 행을 찾았을 때의 공격력
현재 플레이어의 AttackPower는 7입니다. 테이블에 없는 Sword_Missing을 조회했고, Row Not Found 경로는 오류 문구만 출력하며 공격력은 바꾸지 않도록 정했습니다. Out Row는 Row Found 경로에서만 적용합니다. 조회한 뒤 공격력은 0이 될까요, 7이 유지될까요?
해설 보기
7이 유지됩니다. Sword_Missing은 테이블에 없으므로 실행이 Row Not Found로 가고, 이 경로는 공격력을 바꾸지 않도록 정했기 때문입니다. Row Found 쪽의 Set은 실행되지 않습니다.
실패했을 때 공격력을 0 같은 기본값으로 바꾸는 정책을 골랐다면, 그 실패 경로에 명시적인 Set을 두어야 합니다. 조회에 실패한 Out Row를 찾은 행처럼 적용해서는 안 됩니다. 본문 예제의 Sword_Basic을 찾았을 때의 값 10과, 없는 행을 찾은 이번 조건도 구분합니다.
2. 소문자로 적은 행 이름
Row Name에 sword_basic을 입력해 조회했습니다. 테이블에는 Sword_Basic 행만 있습니다. 실행은 어느 핀으로 나갈까요? 기획자가 Potion_Health와 potion_health를 서로 다른 아이템으로 쓰려고 한다면 어떻게 조언해야 할까요?
해설 보기
Row Found로 나갑니다. 행 이름은 Name 타입이고, Name은 대소문자를 구별하지 않고 비교하므로 sword_basic은 Sword_Basic 행과 같은 이름으로 취급됩니다.
같은 이유로 대소문자만 다른 두 이름은 서로 다른 행 이름으로 구분할 수 없습니다. 두 아이템이 정말로 다르다면 Potion_Health_Small, Potion_Health_Large처럼 글자 자체가 다른 이름을 붙이도록 조언합니다.
3. 표시 이름으로 행을 찾기
UI에서 플레이어가 고른 아이템의 ItemName "기본 검"을 문자열로 바꾼 뒤 String To Name으로 변환해 Get Data Table Row에 넘겼습니다. 결과는 어떻게 될까요? 어떻게 고쳐야 할까요?
해설 보기
Row Not Found로 나갑니다. 테이블에는 행 이름이 Sword_Basic인 행만 있고, "기본 검"은 그 행의 ItemName 열에 들어 있는 표시 문구일 뿐 행 이름이 아니기 때문입니다.
UI가 아이템을 표시할 때 행 이름도 함께 들고 있다가, 선택이 일어나면 표시 문구가 아니라 그 행 이름을 조회 키로 넘기도록 고칩니다. 표시 문구는 번역에 따라 언어마다 달라지므로 키로 쓰기에 알맞지 않습니다.
CSV로 데이터 테이블을 가져오는 방법과 임포트 옵션은 언리얼 엔진 공식 문서의 데이터 기반 게임플레이 요소에서 확인할 수 있습니다.
다음 글 저장 및 로드 기본 구현에서는 게임을 껐다 켜도 플레이어의 진행 상황이 남도록 SaveGame 객체로 데이터를 저장하고 불러오는 방법을 다룹니다.