본문 바로가기
게임개발/개발일지

[개발일지 Project-G] #7. 뒤끝 차트 읽기 구현

by youcheachae 2025. 8. 26.

Project-G는 p2p 기반 2D 탄막게임입니다. 플레이어 간에 탄막이 공유됩니다. 

해당일지는 Project-G를 개발하면서 생긴 내용을 정리하는 글입니다. 

*이전 글 보러 가기*

2025.08.08 - [개발] - [개발일지 Project-G] #6. 뒤끝 - 테이블에 유저 정보 저장하기 / 불러오기


이때까지 구글 수프레트 시트를 작성 후, csv 파일을 Resource 폴더 하위에 넣은 후 데이터 파싱을 진행하는 과정을 거쳤다.

이렇게 사용할 때 불편함과 문제점이 몇 가지 있다.

1. 시트 데이터를 수정한 후 csv파일로 저장하고, 유니티에 추가해야 하는 번거로움이 있다.

데이터가 자주 바뀌는 밸런싱을 진행할 때, 해당 과정이 생각보다 많은 시간을 차지하는 경우가 있었다.

2. 보안에 문제가 생길수도 있다.

클라이언트 안에 민감한 데이터가 있으면 빌드 시 해당 데이터를 수정하는 등 악용할 수 있다.

암호화로 숨겨두는것은 가능하겠지만 클라이언트의 데이터는 신뢰할 수 없다고 한다.

 

이로 인해서 서버에서 데이터를 받아오는 기능이 있으면 좋을 것 같아 뒤끝의 차트 기능을 사용하게 되었다.


 

뒤끝 콘솔 세팅은 뒤끝 sdk 참고

https://docs.backnd.com/sdk-docs/backend/base/chart/sample/

 

NPC 대사를 서버에서 불러오기 | 뒤끝 개발자

수정이 잦은 NPC의 대사를 서버에서 동적으로 불러와 효과적으로 게임을 관리하세요.

docs.backnd.com

 

뒤끝 차트에 차트 업로드

 


🧼 차트 테이블 불러오기 (BackendChartManager.cs)

// 차트 목록 담겨져 있는 bro
private BackEnd.Content.BackendContentTableReturnObject chartListBro;

private void ChartTableCheck()
{
    // 로그인 후 차트 불러와야함 ! 
    chartListBro = Backend.CDN.Content.Table.Get();

    if (chartListBro.IsSuccess() == false)
    {
        Debug.LogError(chartListBro);
        return;
    }

    string str = "";
    foreach (BackEnd.Content.ContentTableItem item in chartListBro.GetContentTableItemList())
    {
        str += item.chartName + '\n';
        str += item;

        Debug.Log(str);
        str = "";
    }
}

Backend.CDN.Content.Table.Get()

    ○ 뒤끝 콘솔의 차트 목록을 가져온다

    ○ 차트 이름, 차트 ID, 차트 설명등의 정보를 가져올 수 있다.

● 주의 ! 로그인 후 차트 불러오기가 가능하다

 


가져온 데이터를 사용하기 위해서는, 차트 데이터에 해당하는 클래스를 생성해야 한다

예를 들어 Map 데이터는 MapData를 생성해야 하고, Spawner데이터는 SpawnerData클래스를 생성하여 초기화해야 한다.

이때 단순히 if문이나 swtich문으로 분기를 처리한다면 차트 종류가 늘어날 때마다 BackendChartManager의 코드를 계속 수정해야 하고, 확장성이 떨어진다.

 

즉 차트별로 데이터를 관리하는 클래스를 생성하고, 이를 BackendChartManager의  Dictionary <string, ICharHandler> 형태로 저장하는 방식을 사용했다.

이를 실행하기 위해서 ICharHandler 인터페이스를 생성하였다.

public interface ICharHandler
{
    public void ParseAndStore(LitJson.JsonData data);
}

 

해당 인터페이스를 구현하는 클래스를 작성했다

public class MapChart : ICharHandler
{
    public void ParseAndStore(LitJson.JsonData jsonData)
    {
        foreach (LitJson.JsonData row in jsonData)
        {
            // row는 각 원소(오브젝트)
            MapType mapType = Extension.StringToEnum<MapType>(row["MapType"].ToString());
            Difficulty diffi = Extension.StringToEnum<Difficulty>(row["Difficulty"].ToString());
            string contents = row["MapContents"].ToString();
            int rate = int.Parse(row["Rate"].ToString());

            // Debug.Log($"MapType={mapType}, Difficulty={difficulty}, Rate={rate}, Contents={contents}");

            MapData data = new MapData(mapType, diffi, contents, rate);
            MapDataManager.Instance.AddtoMapDictionary(mapType, data);
        }
    }
}

● 매개변수로 들어온 LitJson.JsonData를 파싱 후 MapData를 생성한 후 MapDataManager에 저장한다.


 

그럼 차트 이름에 대한 ICharHandler를 구현한 클래스를 리턴해주는 함수가 필요하다.

해당 부분은 ChartHandlerFactory로 분리해서 차트이름 -> 클래스 매핑을 관리해 주는 factory 클래스를 생성한다.

 

public class ChartHandlerFactory
{
    Dictionary<string, ICharHandler> keyValuePairs;

    public ChartHandlerFactory() 
    {
        // 차트가 추가되면 이 부분에 차트 이름 - 해당 클래스 생성 필요! 
        keyValuePairs = new Dictionary<string, ICharHandler>() 
        {
            { "Map" , new MapChart()},
            { "SpanwerData" , new SpawnerChart()},
            { "Stage_Forest" , new StageChart()}
        };
    }

    public ICharHandler ChartNameByChart(string name)
    {
        if(keyValuePairs.ContainsKey(name))
            return keyValuePairs[name];

        return null;
    }
}

 

→ BackendChartManager에서는 ICharHandler 구현체 생성 로직을 알 필요 없음

새로운 차트 추가 시 ICharHandler 구현체를 작성하고 ChartHandlerFactory에 등록만 해주면 된다.

 


🧼 차트 테이블 불러오기 (BackendChartManager.cs)

    // key : 차트 ID - value : 차트에 해당하는 클래스 
    private Dictionary<string, ICharHandler> keyValuePairs;
    
    private void ChartTableCheck()
    {
        // 로그인 후 차트 불러와야함 ! 
        chartListBro = Backend.CDN.Content.Table.Get();

        if (chartListBro.IsSuccess() == false)
        {
            Debug.LogError(chartListBro);
            return;
        }

        string str = "";
        foreach (BackEnd.Content.ContentTableItem item in chartListBro.GetContentTableItemList())
        {
            // 차트 이름에 해당하는 클래스 생성후 return
            ICharHandler handler = chartHandlerFactory.ChartNameByChart(item.chartName);
            // register
            RegisterChartHanlder(item.chartId, handler);
        }
        
        public void RegisterChartHanlder(string key, ICharHandler value)
        {
            if (!keyValuePairs.ContainsKey(key))
            {
                keyValuePairs.Add(key, value);
            }
        }
    }

 Backend.CDN.Content.Table.Get() 

으로 차트목록을 가져온 뒤, 차트 이름에 대한 IChartHandler를 생성 후 Dictionary에 저장한다.

 


🧼 차트 내용 조회 (BackendChartManager.cs)

// 차트 ID 별 Contenct 
// *주의 :차트 파일 ID 아님*
private Dictionary<string, BackEnd.Content.ContentItem> chartIdByContenct;
    
// 차트 내용 조회
private void ChartContentCheck() 
{
    BackEnd.Content.BackendContentReturnObject bro2;

    bro2 = Backend.CDN.Content.Get(chartListBro.GetContentTableItemList());

    if (!bro2.IsSuccess())
    {
        Debug.LogError("GetContent Fail : " + bro2);
        return;
    }

    // 성공 시 딕셔너리 형태로 변환
    // key : 차트 Id - value : content
    chartIdByContenct = bro2.GetContentDictionarySortByChartId();
}

BackendContentTableReturnObject.GetContentTableItemList()

    ○ 불러온 차트 내용을 조회한다.

● BackendContentReturnObject.GetContentDictionarySortByChartId()

    ○ 차트이름에 해당하는 콘텐츠(내용)를 딕셔너리 형태로 변환

 

        // 차트 내용 조회
private void ChartContentCheck() 
{
	// ,,,,
    // 내용 확인
    foreach (string keyName in chartIdByContenct.Keys) 
    {
        Debug.Log( "키 이름 : " + keyName + " \n "+ chartIdByContenct[keyName].ToString());

        // ChartHandle실행 
        LitJson.JsonData jsonData = chartIdByContenct[keyName].contentJson;
        ChartHandle(keyName, jsonData);
    }
}
        
public void ChartHandle(string key, LitJson.JsonData data)
{
    if (keyValuePairs.TryGetValue(key, out ICharHandler value))
    {
        value.ParseAndStore(data);
    }
}

●  차트 이름에 해당하는 ContentItem을 조회하고, LitJson.JsonData를 return 받는다.

●  차트 이름에 매핑된 ICharHandler 구현체의 ParseAndStore() 메서드를 실행한다.

●  해당 구현체에서 데이터 파싱과 데이터 저장이 이루어진다.

 


결과

유니티에서 데이터를 가지고 있을 수 있게 되었다.

 


SDK문서와 달랐던 부분

sdk에서는 contentJson 값을 가지고 LitJson.JsonMapper.ToObject로 변환시켜 준다.

이 부분에서 잘못된 타입이라는 오류가 계속 떠 찾아보게 되었다.

 

여기서. BackEnd.Content.ContentItem의 contentJson이 어떤 타입인지 보자

 

contentString값을 LitJson형태로 변환시킨 후 리턴한다

(+추가) constentString값은 json형태의 문자열이다

 

즉 contentstring을 LitJson.JsonData 타입으로 바꾼 값(contentJson)을 또다시 LitJson.JsonMapper을 통해 Json으로 바꾸려고 하니 잘못된 타입이라는 오류가 계속 뜬 것이었다.

 

사용할 때는 contentstring을 가져와 LitJson.JsonMapper을 해주거나,

내가 사용했던 것 것처럼 contentJson을 가져와 사용해 주면 된다.!

해결 완!