Unity Google Sheet 연동

2022년 06월 28일
제작기간 2022년 06월 28일
태그 Unity

JSON과 다르게 CSV는 UI가 달려있는 툴을 통해 편집하는 것이 편하다고 생각한다. 그 중 가장 보편화된 툴이 Google Sheets인데 매번 export 후 import하는 만큼 번거로운 일도 없다. 편하게 편집하고 불편하게 import하는 것은 굉장히 불필요한 일이니, 이 과정을 간편화하고자 한다.

준비

우선, 편집 중인 시트가 아무때나 클라이언트에 적용되면 곤란하니, 버튼을 통해 다운로드를 하도록 했다.

using UnityEditor;

[CustomEditor(typeof(Utility.SheetDownloader))]
public class DownloaderEditor : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        if (GUILayout.Button("Download Scenario"))
        {
            (target as Utility.SheetDownloader).DownloadScenario();
        }
    }
}

Unity의 에디터화면에서 SheetDownloader가 있는 오브젝트의 Inspector창 GUI에 버튼을 추가하는 스크립트다.

‘Download Scenario’ 버튼을 클릭하면, SheetDownloaderDownloadScenario()가 호출된다.이제 프로그래머가 버튼을 클릭함으로 최신 시트를 다운로드 받을 수 있다.

시트 다운로드

공개 링크를 통한 다운로드

보안을 신경쓰지 않는 경우는 당연히 없겠지만, 급하거나 매우 소규모인 경우에는 이 방식을 사용할 수 있다.

public void DownloadScenario()
{
    StartCoroutine(Download(_SHEET_ID, "csv"));
}

private IEnumerator Download(string sheetID, string format)
{
    var url = $"https://docs.google.com/spreadsheets/d/{sheetID}/export?format={format}&sheet={_SHEET_NAME}";
    using (var www = UnityWebRequest.Get(url))
    {
        Debug.Log("Start Downloading...");

        yield return www.SendWebRequest();
        if (www.result != UnityWebRequest.Result.Success) yield break;

        var fileUrl = $"{_SHEET_PATH}/{_SHEET_NAME}.{format}";
        File.WriteAllText(fileUrl, www.downloadHandler.text + "\n");

        Debug.Log("Download Complete.");
    }
}

링크가 있는 모든 사용자에게 공개된 스프레드 시트를 대상으로, 한다. sheetID, format, sheetName을 이용하여 생성한 url로 HTTP 요청을 보내는 방식이다. 코드가 매우 짧아 읽기도 관리하기도 편하다. 별도로 클라우드에서의 세팅이 필요하지 않다.

구글 Cloud API를 이용

Google Cloud API 튜토리얼에 자세한 방법이 적혀있다.

우선, 구글 프로젝트를 생성한다. 프로젝트는 리소스에 대한 컨테이너 역할을 해준다.

프로젝트에 Google Sheets API를 추가해주고, 사용자 인증 정보를 만든다. 에디터에서 사용할 예정임으로 기본 사용자 인증 정보가 필요하다. 여기서는 서비스 계정을 추가하여 이 계정을 sheet의 뷰어로 추가해주었다.

nuget install Google.Apis.Sheets.v4

클라이언트에서 Api를 사용하기 위해서 NuGet을 통해 패키지를 인스톨할 수 있다. NuGet은 .Net환경에서 코드를 공유하는 일종의 메커니즘이다. 패키지를 중앙 레포지토리에서 관리하며 호스팅하는 역할을 한다.

위의 커맨드로 Google Sheets api를 인스톨하면 필요한 다른 패키지도 함께 인스톨한다. 그러나, .net 버전별로 라이브러리를 모두 다운로드하기 때문에 에러가 발생할 수 있다.

Multiple precompiled assemblies with the same name Google.Apis.Sheets.v4.dll included or the current platform. Only one assembly with the same name is allowed per platform.

위 에러는 같은 이름의 dll파일이 복수개 존재하기 때문에 생기기 때문에 사용할 버전만 남겨줄 필요가 있다.

Project Settings/Player/Other Settings/ConfigurationApi Compatibility Level이 있다. .NET Standard 2.0을 권장하지만(더 가볍다) 사용하는 다른 Api가 4.x를 지원할 수도 있다. 자신의 프로젝트에서 사용하는 버전의 라이브러리만 남기면 에러를 해결할 수 있다.

아래는 위에서 준비한 데이터와 클라우드 인증키를 통해 만든 다운로더다. 모든 시트 정보를 로드한 뒤, 각 시트에 대한 요청을 통해 데이터를 받아오고 csv로 가공하여 저장하는 방식이다.

private static string[] Scopes = { SheetsService.Scope.SpreadsheetsReadonly };

private void Download()
{
    Debug.Log("Start Downloading...");

    GoogleCredential credential;

    // '구글 클라우드/API/사용자 인증 정보/서비스 계정/키/생성'을 통해 다운받은 credetial.json을 로드함
    using (var stream = new FileStream(_CREDENTAIL_URL, FileMode.Open, FileAccess.Read))
    {
        // 인증 생성
        credential = GoogleCredential.FromStream(stream).CreateScoped(Scopes);
    }

    // 인증 정보와 시트 이름을 통해 서비스 생성
    var service = new SheetsService(new BaseClientService.Initializer
    {
        HttpClientInitializer = credential,
        ApplicationName = _APPLICATION_NAME
    });

    var sheetsRequest = service.Spreadsheets.Get(_SCENARIO_SHEET_ID);
    var sheetsResponse = sheetsRequest.Execute();
    var sheets = sheetsResponse.Sheets;
    if (sheets == null || sheets.Count == 0)
    {
        Debug.Log("No data found.");
        return;
    }
    Debug.Log("Request Complete.");

    // 모든 시트에 대해 각각 값을 요청함
    foreach (var sheet in sheets)
    {
        var sheetName = sheet.Properties.Title;
        var request = service.Spreadsheets.Values.Get(_SCENARIO_SHEET_ID, $"{sheetName}!A:H");
        var response = request.Execute();
        var rows = response.Values;

        var fileUrl = $"{_SCENARIO_PATH}/{languageCode}/{sheetName}.csv";
        File.WriteAllText(fileUrl, ToCSV(rows));
        Debug.Log($"File saved : {sheetName}");
    }
}

참고

아래는 값 리스트를 CSV 파일로 저장하기 위해 작성한 코드다. 참고용으로 첨부한다.

private string ToCSV(IList<IList<object>> rows)
{
    var mainSb = new StringBuilder();
    foreach (var row in rows)
    {
        var sb = new StringBuilder();
        for (int i = 0; i < row.Count; i++)
        {
            if (row[i].ToString().Contains(","))
            {
                sb.Append($"\"{row[i]}\"");
            }
            else
            {
                sb.Append(row[i]);
            }
            if (i < row.Count - 1)
            {
                sb.Append(",");
            }
        }
        mainSb.Append(sb.ToString());
        mainSb.Append('\n');
    }
    return mainSb.ToString();
}

후기

CSV에 값을 하나하나 넣는 것이 불편하여 만든 다운로더다. 시트로 확인하니 확실히 수정이 쉽다. 원래는 가장 위에 적은 공개 링크로 하려고 했으나, 영 찝찝하여 방법을 알아보던 중에 글까지 남기게 되었다.👍