내일배움캠프 9일차 TIL : 개인 과제

김정환·2024년 9월 24일
0

키워드

  • 가변 매개변수 params
  • JSON 파싱

가변 매개변수 params

문제 상황

프로젝트를 진행하다가 개인적으로 사용하기 불편한 부분이 있어서 사용했다.

 StringBuilder sb = new StringBuilder();
 
 sb.Append($"Lv. {string.Format("{0:0#}", Player.Level)}\n");
 sb.Append($"{Player.Name} ( {className} )\n");
 sb.Append($"공격력 : {Player.Attack}\n");
 sb.Append($"방어력 : {Player.Defense}\n");
 sb.Append($"체력 : {Player.Health} / {Player.MaxHealth}\n");
 sb.Append($"Gold : {Player.Gold} G\n");
 
 Console.WriteLine(sb.ToString()); 

원래 위처럼 작성하다가 너무 반복된 부분이 많이 보여서 좀 정리할 겸, 편하게 작성할 겸 작성했다.

매개변수는 보통 하나씩 사용하거나 배열을 넘기는 경우로 많이 썼을 것이다.
하지만 매개변수의 갯수가 많아지면 그만큼의 매개변수를 계속 추가해주어야 한다.

해결법 : 가변 매개변수 params

다음 조건에 맞는 경우라면 가변 매개변수를 고려해볼 수 있다.

  • 매개변수의 자료형이 동일하다
  • 사용할 매개변수의 갯수를 다 알 수 없다.
static string GetString(params string[] args)
{
    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < args.Length; i++)
        sb.Append(args[i]);

    return sb.ToString();
}

public static void ShowScript(params string[] args)
{
    Console.WriteLine(GetString(args));
}

string[] 형태로 받는 매개변수 앞에 params를 붙이면
string 형태의 매개변수를 입력한 만큼 받을 수 있다.

이렇게 작성한 코드를 사용하면 다음처럼 쓸 수 있다.

Utility.ShowScript(
    $"Lv. {string.Format("{0:0#}", Player.Level)} ({Player.Exp / (float)Player.Level * 100f }%)\n",
    $"{Player.Name} ( {className} )\n",
    $"공격력 : {Player.BaseAttack} {(Player.EquipAttack > 0 ? $"(+{Player.EquipAttack})" : "")}\n",
    $"방어력 : {Player.BaseDefense} {(Player.EquipDefense > 0 ? $"(+{Player.EquipDefense})" : "")}\n",
    $"체력 : {Player.Health} / {Player.MaxHealth}\n",
    $"Gold : {Player.Gold} G\n"
);

성능면에서 개선되진 않겠지만 작성을 편하게 하는데 의의를 두었다.
괜히 했다. 그냥 cmd랑 같이 보이게하고 싶어서 했는데 안해도 됐다

JSON 파싱

도전 기능의 가장 마지막은 데이터를 저장하는 것이다.
이리 저리 생각해보다가 가장 심플하게 로컬에 저장하기로 했다.

그리고 데이터들을 특수 문자를 끼워서 문자열로 만들어 저장하기 보다는
DB 연동 등에도 쓸 수 있게 Json 형태로 만들어 쓰기로 했다.

JSON

JSON(= JavaScript Object Notation)은 보통 웹에 객체 데이터를 주고 받을 때 쓰는 표현법이다.
예전에 mysql DB를 쓸 때, 주로 받은 방식이 JSON 방식이라서 이번에도 적용해보았다.
(JSON 말고 XML을 써도 된다.)

// 저장할 자료
public class GameData
{
    // 플레이어 정보
    public Character Player;
	// 상점 판매 정보
    public Dictionary<int, bool> ItemSellingInfo;
}

public void Save()
{
    string data = JsonSerializer.Serialize(_gameData);
    File.WriteAllText(FILE_PATH, data);
}

C#에서도 JSON 라이브러리가 기본적으로 내장되어 있다.
라이브러리 위치는 System.Text.Json였다.

사용하기도 쉬웠다.
아래 함수를 저장할 객체를 넣고 호출하면 JSON 형태의 문자열을 반환해준다.(딸깍)

JsonSerializer.Serialize(저장할 자료);

이제 호출하면

? 왜 공백이야 이거
실수했나 싶어서 코드를 확인했지만 데이터에 이상 없었다.

문제 : JSON 파싱 문제

Unity 작업할 때는 좀 더 좋은 라이브러리가 있어서 그걸 써오다 보니
내장 라이브러리에서 이런 문제가 있는 줄은 몰랐다.

머리에 답이 없으면 검색을 해야한다.
구글링을 하면서 다음 두 글을 읽었다.
https://stackoverflow.com/questions/58784499/system-text-json-jsonserializer-serialize-returns-empty-json-object
https://stackoverflow.com/questions/58139759/how-to-use-class-fields-with-system-text-json-jsonserializer

원인 :

  1. .NET Core 3.x 버전에선 클래스 필드를 직렬화할 수 없었음.
  2. 근데 .NET 5 버전 이후부터 옵션을 추가해서 내부 필드를 직렬화할 수 있게 됨.

클래스 내부 필드(변수)를 직렬화(비휘발성 자료로 전환)하려면 옵션을 추가해야했다.
아 옵션을 또 추가 해야하는구나.
편하단말 취소

해결 : 옵션 추가

public void Save()
{
    JsonSerializerOptions opt = new JsonSerializerOptions();
	opt.IncludeFields = true; // 내부 필드 포함
	opt.WriteIndented = true; // 띄어쓰기

	string data = JsonSerializer.Serialize(_gameData, opt);
	File.WriteAllText(FILE_PATH, data);
}

위처럼 옵션을 매개변수로 넣어줘야한다.
여러 옵션이 있었는데
이 문제를 해결하려면 opt.IncludeFields 이 옵션을 true로 설정해야한다.
말 그대로 내부 필드를 포함하라는 설정이다.

추가로 사용한 opt.WriteIndented 이 옵션은 보기 편하게 줄바꿈 / 들여쓰기 해주는 설정이다.

다시 save 해보면

정상적으로 저장된다.

이제 읽어볼 차례다.

? 왜 생성 화면이야
생성 화면이 나오려면 Load한 결과물이 null일 때 예외처리 차원으로 설정한 것이다.

문제 2 : JSON Read 문제

혹시나 해서 콘솔로 로그를 찍어봤다.

중간에 True는 Player == null 의 결과물이다.
즉, Load 단계에서 데이터를 못 읽어왔다.

원인 2 : 프로퍼티, 접근성

이번에는 답을 빨리 찾았다.
구글링 중 클래스를 역직렬화할 땐 프로퍼티를 선언해서 받으라고 한다.
추가로 접근성의 문제도 있었다.

해결 2

public class GameData
{
    // 플레이어 정보
    public Character Player { get; set; }
    public Dictionary<int, bool> ItemSellingInfo { get; set; }
}

set 부분이 public으로 열려 있어야한다.
private나 protected로 접근성이 제한되어 있으면 Json 라이브러리가 접근하지 못해서 null을 띄운 것이다.

이렇게 코드를 좀 더 수정해준 뒤에야 정상적으로 load할 수 있었다.
그냥 쓰던거 쓸 걸;

#내일배움캠프 #스파르타내일배움캠프 #스파르타내일배움캠프TIL

profile
만성피로 개발자

0개의 댓글