[뒤끝팁] 수많은 차트, 어떻게 관리하세요? 차트를 관리하는 차트 만들기

수많은 차트를 차트로 관리하는 콘텐츠 썸네일

안녕하세요! 개발이 필요 없는 게임 서버, 뒤끝입니다🙋‍♀️

오늘은 아마도 모두가 사용하고 계실, 차트 관리 기능의 또 다른 사용법에 대해 공유드립니다.
바로 ‘차트를 관리하는 차트 만들기‘입니다.

여러분은 차트를 몇 개나 관리하고 계시나요? 

게임을 운영하다 보면 차트는 점점 늘어나게 되고, 늘어난 차트의 수만큼 비용도 증가하게 됩니다. 
유저가 게임을 켤 때마다 모든 차트를 다운로드하기 때문에, 그만큼 호출 수도 증가하게 되죠.

지금부터 이 문제를 해결해 줄, ‘차트를 관리하는 차트’ 시작하겠습니다😎

목차 한눈에 보기👀

1. 차트를 관리하는 차트, 무엇일까?

차트를 관리하는 차트란, 게임에서 사용되는 모든 차트(몬스터 데이터, 상점 데이터 등)를 하나의 또 다른 차트에 정리해 관리하는 방법입니다.

이 기능의 핵심은, 차트 변경일을 비교해 유저들이 새로운 차트를 받게 만드는 것인데요! 이것을 활용하면, 수많은 차트들 중 업데이트된 차트만 기기에 덮어 씌우기가 가능해집니다. 

그렇기 때문에, 아래와 같은 장단점을 가집니다.

📌장점
(1) 로딩 시간 감소로, 유저 경험 개선 가능
(2) 차트 호출 수 감소로, 비용 절감 가능

📌단점
(1) 차트를 수정할 때마다, 차트 변경일 관리 필요  

2. 차트를 관리하는 차트 만드는 방법🙌

(1) '차트 변경일' 값이 포함된 차트 파일 만들기

차트 변경일은 차트의 업데이트 상태를 체크하는 기준값이 됩니다. 
그렇기 때문에, 차트를 관리하는 차트를 만들기 위해서는 아래 정보가 반드시 필요합니다.

▪️ 차트 이름: chartName
▪️ 차트 파일: chartFileId
▪️ 차트 변경일: updateDate

👉 왜 차트 리스트 불러오기 기능을 사용하지 않고, 새 차트를 만들어야 할까요? 

차트 리스트 불러오기로는, 이번 콘텐츠의 핵심인 '차트 변경일' 값을 가져올 수 없습니다.
이유는 뒤끝 기능에서 이 정보를 제공하지 않기 때문인데요. 때문에 직접 차트 변경일이 포함된 
차트를 만들어 수동으로 관리해야 합니다.

차트 예시)

아래와 같이 chartName, chartFileId, updateDate 세 가지 컬럼을 이용해 차트의 정보를 입력해 줍니다.
이때 주의해야 할 점은, 차트 변경일(updateDate)은 차트의 데이터를 변경할 때마다 수동으로 수정을 해야 한다는 것입니다.

차트 버전 관리를 위한, 차트 변경일이 포함된 차트
(2) 차트 파일을 뒤끝 콘솔에 업로드하기

만들어준 차트 파일을 뒤끝 콘솔에 업로드합니다. 업로드를 했다면 아래 사진과 같은 화면을 확인할 수 있습니다.

* 뒤끝 차트에 대한 기본 지식은 [뒤끝탐사대] 예시로 알아보는, 뒤끝 차트 A to Z 를 참고해 주세요😊

엑셀 형태의 차트 파일을 뒤끝 콘솔에 업로드한 모습
콘솔에 chartManager 차트 파일을 업로드한 모습
(3) 차트의 데이터를 변경하고, 실시간으로 반영하기

💡만약 몬스터 정보를 변경하고 싶다면? 

① 몬스터의 데이터가 있는 EnemyChart(enemyChart.csv) 차트 파일에서 내용을 변경해 줍니다.
② 그리고 방금 업로드해 준 chartManager에서 EnemyChart의 updateDate를 오늘 날짜로 수정합니다.

이렇게 두 가지만 수정해 주면, 유저들은 재접속 시 변경된 몬스터 정보를 바로 감지하게 됩니다💥

몬스터 데이터를 수정하고 있는 화면
① 몬스터 데이터를 수정하고 있는 화면
차트 변경일을 수정하는 화면
② 차트 변경일을 실제 변경 일시에 맞게 수정한 화면

3. 자세한 로직 안내🔎

다음 로직은 오늘 소개한 내용을 코드로 풀어낸 것으로, 로컬에 저장된 차트를 사용하는 게임을 가정했습니다.

🙋뒤끝 개발진: 이런 경우는 회원가입 시, 로컬에 저장된 차트가 존재하지 않을 수 있습니다. 그런 상황도 고려하여 준비하였으니, 걱정 마시고 읽어봐 주세요!

(1) 로컬에 저장되어 있는 ChartManager 불러오기

로그인을 한 이후, 로컬(기기)에 저장되어 있는 ChartManager를 불러옵니다.

* 로컬에 차트를 저장하는 방법은 GetOneChartAndSave 함수를 참고해주세요! (개발자 문서로 이동합니다)

로컬에서 차트를 불러올 때에는 string 형태로 리턴이 되기 때문에 만약 로컬에 차트가 존재하지 않는다면 string.Empty로 저장될 것입니다.

				
					string deviceChartManagerString = Backend.Chart.GetLocalChartData(chartManagerName);

// 기기에는 string 형태로 저장이 되며, 저장되어있지 않을 경우 string.Empty가 반환됩니다.
if (string.IsNullOrEmpty(deviceChartManagerString) == false) {
	// 기기에 저장된 chartManager 차트가 존재한다면
}
				
			
(2) 서버에 저장되어 있는 chartManager 불러오기

그리고 서버에서 ChartManager를 불러옵니다.

				
								// 특정 폴더에 있는 차트를 모두 불러옵니다.
			// 다만 해당 폴더에는 ChartManager 차트 하나만 존재할 예정이므로 무조건 1개만 리턴됩니다.
			var bro = Backend.Chart.GetChartListByFolder(1051);

			// 해당 폴더에는 chartManager 차트 하나만 존재할 것이므로 0으로 접근합니다.
			string chartManagerFileId = bro.FlattenRows()[0]["selectedChartFileId"].ToString();
			string chartManagerName = bro.FlattenRows()[0]["chartName"].ToString();

			// 서버에서 ChartManager 차트를 불러옵니다. 기기에 저장하지는 않습니다.
			var serverChartBro = Backend.Chart.GetChartContents(chartManagerFileId);

			// 서버에서 불러오지 못할 경우에는 데이터 꼬임 방지를 위해 진행을 중지합니다.
			if (serverChartBro.IsSuccess() == false) {
				return;
			}ㅊ
				
			
(3) 업데이트가 필요한 차트를 저장하는 Dictionary 만들기

먼저 차트 정보를 저장하는 Dictionary<string, ChartInfo> Dictionary를 생성합니다.(key는 차트 이름) 
그리고 해당 Dictionary에는 서버에서 불러온 ChartManager 속 차트들을 모두 파싱하여 삽입합니다.

				
					public class ChartInfo {
	public string chartName;
	public string chartFileId;
	public string updateDate;
	
	public ChartInfo(JsonData json) {
		chartName = json["chartName"].ToString();
		chartFileId = json["chartFileId"].ToString();
		updateDate = json["updateDate"].ToString();
	}
}
				
			
				
								// 서버에서 불러온 ChartManager을 언마샬하여 JsonData 형태로 캐싱합니다.
			JsonData newChartManagerJson = serverChartBro.FlattenRows();

			// 차트 이름으로 데이터를 검색할 것이기 때문에 Dictnary로 생성합니다.
			// 해당 Dictnary는 최신 버전으로 업데이트할 차트 리스트로 사용됩니다.(최신 버전이라면 해당 리스트에서 제외)
			Dictionary<string, ChartInfo> chartInfoDic = new Dictionary<string, ChartInfo>();

			// 반복문을 통해 모든 ChartManager 차트에 저장된 모든 차트들을 삽입합니다.
			foreach (JsonData chartInfoJson in newChartManagerJson) {
				ChartInfo chartInfo = new ChartInfo(chartInfoJson);
				chartInfoDic.Add(chartInfo.chartName, chartInfo);
			}
				
			
(4) 두 ChartManager 속 차트 버전들을 비교하기

로컬 기기 속 ChartManager를 Json으로 파싱하여 반복문을 돌리며 Dictionary로 저장된 차트 정보들과 버전을 비교합니다.

만약 버전이 같다면 버전이 같다면 업데이트할 필요가 없으므로 Dictionary에서 제외합니다. 그렇게 Dictionary에는 옛날 버전의 차트들, 서버에서 새로 생성된 차트들만 존재하게 됩니다.

				
								if (string.IsNullOrEmpty(deviceChartManagerString) == false) {
				// 기기에 저장된 chartManager 차트가 존재한다면
				
				// 기기에 저장된 string형태의 chartManager를 Json 형태로 변경
				JsonData deviceChartManagerJson = JsonMapper.ToObject(deviceChartManagerString);
				deviceChartManagerJson = BackendReturnObject.Flatten(deviceChartManagerJson);

				// 기기에 저장된 chartManager 차트 속 차트들을 서버에서 불러온 데이터와 대조합니다.
				foreach (JsonData deviceChartJson in deviceChartManagerJson["rows"]) {
					ChartInfo deviceChartInfo = new ChartInfo(deviceChartJson);

					// 이미 기기에 저장되어 있는 차트가 있는지 확인합니다.
					if (chartInfoDic.ContainsKey(deviceChartInfo.chartName)) {

						// 기기에 저장되어 있는 차트의 수정 날짜(updateDate)가 일치하는지 확인합니다.
						if (chartInfoDic[deviceChartInfo.chartName].updateDate == deviceChartInfo.updateDate) {
							// 수정날짜까지 일치할 경우, 재다운로드 리스트(chartInfoDic)에서 제외합니다.
							chartInfoDic.Remove(deviceChartInfo.chartName);
						}
					}
				}
				
			
(5) Dictionary 속 차트들 서버에서 불러와 로컬(기기)에 저장하기

Dictionary에 남아있는 차트들을 반복문을 통해 모두 로컬(기기)에 저장합니다.(GetOneChartAndSave)

이후 로컬 차트들을 불러와 Json으로 변경 후, Dictionary 혹은 List 형태로 변경하면 사용할 수 있게 됩니다.

				
								// 재다운로드할 차트 리스트에서 차트가 하나라도 존재하는지 확인합니다.
			if (chartInfoDic.Count > 0) {

				// 차트를 재다운로드하여 기기에 덮어씌웁니다.
				foreach(var downloadChartInfo in chartInfoDic) {
					Debug.Log(downloadChartInfo.Value.chartName + "을 새로운 버전으로 다운받습니다.");
					Backend.Chart.GetOneChartAndSave(downloadChartInfo.Value.chartFileId, downloadChartInfo.Value.chartName);
				}

				// chartManager 차트를 최신화합니다.
				Backend.Chart.GetOneChartAndSave(chartManagerFileId, chartManagerName);
			}
			else {
				Debug.Log("업데이트할 내역이 존재하지 않습니다.");
			}
				
			
(6) 서버에서 불러온 ChartManager 로컬에 저장

모든 차트의 업데이트가 성공적으로 끝났다면 서버에서 불러온 ChartManager를 로컬 기기에 저장하여, 로컬에 저장된 ChartManager를 최신화합니다. (5번 코드에 삽입되어 있음)

전체 로직 구현
				
					public class ChartInfo {
	public string chartName;
	public string chartFileId;
	public string updateDate;
	
	public ChartInfo(JsonData json) {
		chartName = json["chartName"].ToString();
		chartFileId = json["chartFileId"].ToString();
		updateDate = json["updateDate"].ToString();
	}
}

public void ChartDownload() {
			Backend.BMember.CustomLogin("user1", "user1");

			// 특정 폴더에 있는 차트를 모두 불러옵니다.
			// 다만 해당 폴더에는 ChartManager 차트 하나만 존재할 예정이므로 무조건 1개만 리턴됩니다.
			var bro = Backend.Chart.GetChartListByFolder(1051);

			// 해당 폴더에는 chartManager 차트 하나만 존재할 것이므로 0으로 접근합니다.
			string chartManagerFileId = bro.FlattenRows()[0]["selectedChartFileId"].ToString();
			string chartManagerName = bro.FlattenRows()[0]["chartName"].ToString();

			// 서버에서 ChartManager 차트를 불러옵니다. 기기에 저장하지는 않습니다.
			var serverChartBro = Backend.Chart.GetChartContents(chartManagerFileId);

			// 서버에서 불러오지 못할 경우에는 데이터 꼬임 방지를 위해 진행을 중지합니다.
			if (serverChartBro.IsSuccess() == false) {
				return;
			}

			// 서버에서 불러온 ChartManager을 언마샬하여 JsonData 형태로 캐싱합니다.
			JsonData newChartManagerJson = serverChartBro.FlattenRows();

			// 차트 이름으로 데이터를 검색할 것이기 때문에 Dictnary로 생성합니다.
			// 해당 Dictnary는 최신 버전으로 업데이트할 차트 리스트로 사용됩니다.(최신 버전이라면 해당 리스트에서 제외)
			Dictionary<string, ChartInfo> chartInfoDic = new Dictionary<string, ChartInfo>();

			// 반복문을 통해 모든 ChartManager 차트에 저장된 모든 차트들을 삽입합니다.
			foreach (JsonData chartInfoJson in newChartManagerJson) {
				ChartInfo chartInfo = new ChartInfo(chartInfoJson);
				chartInfoDic.Add(chartInfo.chartName, chartInfo);
			}

			// 기기에 저장된 chartManager 차트를 불러옵니다.
			string deviceChartManagerString = Backend.Chart.GetLocalChartData(chartManagerName);

			// 기기에는 string 형태로 저장이 되며, 저장되어있지 않을 경우 string.Empty가 반환됩니다.
			if (string.IsNullOrEmpty(deviceChartManagerString) == false) {
				// 기기에 저장된 chartManager 차트가 존재한다면
				
				// 기기에 저장된 string형태의 chartManager를 Json 형태로 변경
				JsonData deviceChartManagerJson = JsonMapper.ToObject(deviceChartManagerString);
				deviceChartManagerJson = BackendReturnObject.Flatten(deviceChartManagerJson);

				// 기기에 저장된 chartManager 차트 속 차트들을 서버에서 불러온 데이터와 대조합니다.
				foreach (JsonData deviceChartJson in deviceChartManagerJson["rows"]) {
					ChartInfo deviceChartInfo = new ChartInfo(deviceChartJson);

					// 이미 기기에 저장되어 있는 차트가 있는지 확인합니다.
					if (chartInfoDic.ContainsKey(deviceChartInfo.chartName)) {

						// 기기에 저장되어 있는 차트의 수정 날짜(updateDate)가 일치하는지 확인합니다.
						if (chartInfoDic[deviceChartInfo.chartName].updateDate == deviceChartInfo.updateDate) {
							// 수정날짜까지 일치할 경우, 재다운로드 리스트(chartInfoDic)에서 제외합니다.
							chartInfoDic.Remove(deviceChartInfo.chartName);
						}
					}
				}
			}

			// 재다운로드할 차트 리스트에서 차트가 하나라도 존재하는지 확인합니다.
			if (chartInfoDic.Count > 0) {

				// 차트를 재다운로드하여 기기에 덮어씌웁니다.
				foreach(var downloadChartInfo in chartInfoDic) {
					Debug.Log(downloadChartInfo.Value.chartName + "을 새로운 버전으로 다운받습니다.");
					Backend.Chart.GetOneChartAndSave(downloadChartInfo.Value.chartFileId, downloadChartInfo.Value.chartName);
				}

				// chartManager 차트를 최신화합니다.
				Backend.Chart.GetOneChartAndSave(chartManagerFileId, chartManagerName);
			}
			else {
				Debug.Log("업데이트할 내역이 존재하지 않습니다.");
			}

			// 차트 갱신 끝
			// 이후로는 기기에 저장된 차트 리스트들을 이용하여 List 혹은 Dictionary 형태로 변경하여 게임에 맞게 사용하시면 됩니다.

			// 예시
			Backend.Chart.GetLocalChartData("WeaponChart");
			Backend.Chart.GetLocalChartData("ShopChart");

			// 상세 예시
			JsonData enemyChartJson = JsonMapper.ToObject(Backend.Chart.GetLocalChartData("EnemyChart"));
			enemyChartJson = BackendReturnObject.Flatten(enemyChartJson);

			List<EnemyInfo> enemyInfoList = new List<EnemyInfo>();
			foreach(JsonData enemyChart in enemyChartJson) {
				enemyInfoList.Add(new EnemyInfo(enemyChart));
			}
		}
				
			

오늘은 ‘차트를 관리하는 차트’를 만들어, 로컬 차트와 서버 차트를 비교하며 로컬 차트들을 최신화하는 로직을 만들어 보았습니다.

변경된 차트만 불러와, 요금과 로딩 시간이 모두 절약되는 ‘차트 관리 차트’, 
안내드린 방법을 따라서 한 번 구성해 보세요!

🚨 이때, 한 가지 주의할 점이 있는데요!  

기기에 저장되어 있는 로컬 차트를 이용하기 때문에, 크래킹 보안이 잘 되어있지 않을 경우
서버에서 불러오는 로직보다는 조작의 위험성이 있을 수 있으니 반드시 보안에 신경 써 주세요.

관련하여 궁금한 부분이 있으시다면, 언제든 댓글 남겨 주시기 바라며
주의해야 할 부분 하나 공유드리면서 이만 마치겠습니다.😊

감사합니다.

0

2 Comments

  1. 줄이면 차관차인가요 차차인가요??

댓글