이전에 구현한 데이터 드리븐 은 개발자가 직접 엑셀 파일로 들어가서, CSV 파일로 저장한 다음 유니티 에디터에 직접 넣었어야 했다.
그러나 매번 시트에서 Unity로 가져오는 과정은 번거롭다.
이러한 과정을 버튼 한 번으로 자동으로 CSV 파일을 내려받아 에디터에 저장해주는 시스템을 구축할 것이다.
먼저 전체 코드를 살펴보자.
// Assets/Editor/GoogleSheetsSync.cs
#if UNITY_EDITOR
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
public static class GoogleSheetsSync
{
// 카드·몬스터 두 문서를 하나의 배열로 관리
private static readonly SheetDoc[] Docs =
{
// 데이터 테이블1
new(
"비공개ID", // 문서 ID
new (string Sheet, string File)[]
{
("시트1", "00.데이터1"),
("시트2", "01.데이터2")
}
),
// 데이터 테이블2
new(
"비공개",
new (string Sheet, string File)[]
{
("유닛", "10.유닛 테이블"),
("탱커", "11.몬스터 - 탱커"),
("러셔", "12.몬스터 - 러셔"),
("전사", "13.몬스터 - 전사"),
}
)
};
// 메뉴 클릭으로 동기화
[MenuItem("Tools/Sync Sheets")]
private static void SyncSheets()
{
foreach (var doc in Docs)
foreach (var pair in doc.Pairs)
DownloadCsv(doc.DocId, pair.Sheet, pair.File);
AssetDatabase.Refresh();
Debug.Log("<color=lime>[SheetSync] 모든 시트 동기화 완료</color>");
}
#region 내부 구현
private static void DownloadCsv(string docId, string sheetName, string fileName)
{
var url = $"<https://docs.google.com/spreadsheets/d/{docId}/gviz/tq?tqx=out:csv>" +
$"&sheet={UnityWebRequest.EscapeURL(sheetName)}";
var req = UnityWebRequest.Get(url);
req.SendWebRequest();
while (!req.isDone) { }
if (req.result != UnityWebRequest.Result.Success)
{
Debug.LogError($"[SheetSync] <b>{sheetName}</b> 실패 ▶ {req.error}");
return;
}
const string folder = "Assets/Resources/CSV";
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
string clean = CleanCsv(req.downloadHandler.text);
File.WriteAllText(Path.Combine(folder, $"{fileName}.csv"), clean, Encoding.UTF8);
Debug.Log($"[SheetSync] {sheetName} → {fileName}.csv 저장");
}
private readonly struct SheetDoc
{
public readonly string DocId;
public readonly (string Sheet, string File)[] Pairs;
public SheetDoc(string docId, (string Sheet, string File)[] pairs)
{ DocId = docId; Pairs = pairs; }
}
private static string CleanCsv(string raw)
{
var sb = new StringBuilder();
bool firstLine = true;
StringBuilder names = null, types = null;
foreach (var line0 in raw.Split('\\n'))
{
var line = line0.TrimEnd('\\r', '\\n');
if (string.IsNullOrEmpty(line)) continue;
// 1) 따옴표·끝 콤마 정리
if (line.StartsWith("\\"") && line.EndsWith("\\""))
line = line[1..^1];
line = line.Replace("\\",\\"", ",");
line = Regex.Replace(line, ",+$", "");
// 2) 첫 줄이면 ‘이름 타입’ 분리 작업
if (firstLine)
{
names = new StringBuilder();
types = new StringBuilder();
foreach (var cell in line.Split(','))
{
var parts = cell.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries);
names.Append(parts[0]).Append(',');
types.Append((parts.Length > 1 ? parts[1] : "")).Append(',');
}
sb.AppendLine(Regex.Replace(names.ToString(), ",+$", ""));
sb.AppendLine(Regex.Replace(types.ToString(), ",+$", ""));
firstLine = false;
}
else // 데이터 행은 그대로
sb.AppendLine(line);
}
return sb.ToString();
}
#endregion
}
#endif
이제 코드에 대해서 자세하게 설명한다.
Docs
배열 구조와 Google Sheets 문서 ID 및 시트-파일 매핑의 의미// 스프레드시트 한 개 문서에 대한 정보 구조체
public struct SheetDoc {
public string docId; // 구글 문서 ID
public string[] sheetNames; // 가져올 시트 이름들
public string[] fileNames; // 각 시트를 저장할 CSV 파일명들
}
// 동기화 대상 Google Sheets 문서 및 시트 설정
static SheetDoc[] Docs = new SheetDoc[] {
new SheetDoc {
docId = "1AbCdEFg...XYZ", // 예시: Google 스프레드시트 문서 ID
sheetNames = new string[] { "Enemies", "Items" },
fileNames = new string[] { "Enemies.csv", "Items.csv" }
}
// 필요한 만큼 SheetDoc 추가 가능
};
먼저, 동기화할 Google 스프레드시트 정보는 코드 내의 Docs
배열에 정의되어 있다.
이 배열은 동기화 대상이 되는 여러 개의 Google 스프레드시트 문서와 각 문서에 속한 시트들을 나타낸다.
각 항목은 커스텀 구조체 SheetDoc
타입으로 구성되어 있으며, 문서 ID, 시트 이름 목록, 그리고 각 시트를 저장할 CSV 파일 이름 목록을 포함한다.
위 코드를 보면, SheetsDoc 구조체는 하나의 스프레드시트 문서를 나타낸다.
docId 필드는 해당 문서의 고유 ID로
문서 ID 는 스프레드시트 URL에서 확인할 수 있는 문자열이다.
예를들어,