내일배움캠프/TIL

특강 정리 - 직렬화 2차특강

서보훈 2024. 11. 11. 20:05

플레이어 객체 → 여러 정보가 필요, 저장/불러어기가 가능해야함

  • 레벨
  • 경험치
  • 닉네임

※ 묶어서 관리하기위해 직렬화 클래스로 관리

public class Character : MonoBehaviour
{
    public UserData userData;
}


[System.Serializable]
public class UserData
{
    public string nickname;
    public int lv;
    public float hp;

    public List<string> friends = new List<string>();

    public Inventory inven;
}

이때 데이터 사용시에는 데이터를 저장한 클래스를 통해서 사용

public Character character;

public void CheckUserName()
{
    Debug.Log(character.userData.nickName);
}

 

저장시, 직렬화된 클래스 전체를 json화 하여 저장

public void Save()
{
    var saveData = JsonUtility.ToJson(userData);

    File.WriteAllText(Application.persistentDataPath + "/UserData.txt", saveData);
}

불러오기시, JsonUtility를 사용하여 json을 클래스 내용에 재입력 할 수 있음

public void Load()
{
    string loadData = File.ReadAllText(Application.persistentDataPath + "/UserData.txt");

    userData = JsonUtility.FromJson<UserData>(loadData);
}

 

인벤토리 저장

  • 인벤토리는 각 인벤토리의 아이템 슬롯이 리스트로 저장되어있는 형태
  • 아이템 슬롯 클래스를 직렬화 클래스로 구현, 슬롯마다 아이템을 저장
  • 인벤토리를 불러올 때, 역직렬화된 데이터에 슬롯들이 리스트의 형태로 저장된 상태
    • 아이템 리스트를 반복문을 통해 슬롯에 정보를 입력해주어야함
public class Character : MonoBehaviour
{
    public UserData userData;
    public Inventory inventory;

    private void Start()
    {
        Load();
    }

    public void Save()
    {
        var saveData = JsonUtility.ToJson(userData);

        Debug.Log(saveData);

        Debug.Log(Application.persistentDataPath + "/UserData.txt");
        File.WriteAllText(Application.persistentDataPath + "/UserData.txt", saveData);
    }

    public void Load()
    {
        string loadData = File.ReadAllText(Application.persistentDataPath + "/UserData.txt");

        userData = JsonUtility.FromJson<UserData>(loadData);
    }
    
    public void SaveInventory()
    {
        var saveData = JsonUtility.ToJson(inventory);

        File.WriteAllText(Application.persistentDataPath + "/UserInven.txt", saveData);
    }

    public void LoadInventory()
    {
        string loadData = File.ReadAllText(Application.persistentDataPath + "/UserData.txt");

        inventory = JsonUtility.FromJson<Inventory>(loadData);
    }

}

[System.Serializable]
public class Inventory
{
    public int size;
    public List<Item> items = new List<Item>();
}

[System.Serializable]
public class Item
{
    public int itemId;
}

리팩토링

  • DataManager를 통해 저장 관리
  • 저장이 필요한 클래스에 대해 일일히 메서드를 만들어주기에는 문제가 있음
  • 저장, 불러오기 기능은 이름만 다르고 나머지 기능은 모두 같음

→ 제네릭을 사용하여 저장, 불러오기 기능 구현

public class DataManager : MonoBehaviour
{
    public static DataManager instance;
    private string savePath = Application.persistentDataPath;

    private void Awake()
    {
        instance = this;
    }

    //저장 기능
    public void SaveData<T>(T data)
    {
        string json = JsonUtility.ToJson(data);
        File.WriteAllText(savePath + $"/{typeof(T).ToString()}.txt", json);
    }

    //불러오기 기능
    public T LoadData<T>()
    {
        string loadJson = File.ReadAllText(savePath + $"/{typeof(T).ToString()}.txt");
        return JsonUtility.FromJson<T>(loadJson);
    }
}
public class Character : MonoBehaviour
{
    public UserData userData;
    public Inventory inventory;

    private void Start()
    {
        userData = DataManager.instance.LoadData<UserData>();
    }
}

 

서버 통신

  • 서버 개발자와 클라이언트 개발자 사이의 소통 필요, 하지만 서로 어떤 코드를 사용하였는지는 알 필요 없음
  • 전달받은 json 을 사용하여 알아서 개발하게됨
  • 서버 개발자의 설계를 따르는것이 일반적임
    • 요청, 응답을 모두 서버에 맞추어서 보내며, 역직렬화를 서버에 맞춰서 함

UnityWebRequest

  • 요청을 위해 사용하게되는 클래스

get

  • 4가지 구성요소
    1. 어디로 요청을 보낼것인가 - 주소
    2. 어떤걸 요청할것인가 - 목적
    3. 정당한 요청인가 - 인증
    4. 응답 대기 - 구현 방법은 여러가지, 일단은 코루틴 사용(await 를 사용하는 비동기방법도 있음)

호출시

public class ServerConnect : MonoBehaviour
{
    string url = "https://open.api.nexon.com/";
    string apiKey = "신청을 통해 받은 API 키 값 입력";

    private void Start()
    {
        StartCoroutine(GetOCID("탐색을 원하는 아이디 입력"));
    }

    IEnumerator GetOCID(string nickName)
    {
        //주소 + 목적
        string path = url + "사이트에서 지원하는 목적 내용 입력" + nickName;
        //관례적으로 www 사용
        UnityWebRequest www = UnityWebRequest.Get(path);

        //인증
        www.SetRequestHeader("헤더 이름", apiKey);

        //응답 대기
        yield return www.SendWebRequest();

        //응답받은 값 저장
        string result = www.downloadHandler.text;
        JsonUtility.FromJson<OCID>(result);
    }
}


[System.Serializable]
public class OCID
{
    public string ocid;
}

문제점

  1. 코루틴은 리턴값을 지정할 수 없어서 서버로부터 받은 json 값을 역직렬할 수 없음
  2. OCID를 받은 뒤, 이 OCID를 사용하여 추가 정보를 받아야하지만, 코루틴이 종료되기 전까지는 사용이 불가능함

해결법

  1. 제네릭을 사용, 매개변수로 UnityAction 을 받아서 호출하여 UnityAction을 통해서 데이터 받기
  2. OCID 요청이 완료되면 즉시 다음정보 요청

※요청할때마다 URL을 새로 쓰는것이 비효율적

→ 요청사항별로 클래스를 만들어서 사용