티스토리 뷰

시리얼라이즈

시리얼라이즈(직렬화)는 클래스나 오브젝트를 스트링이나 일차원 배열형태로 변환하는 것이다. 스크립트에서 만든 클래스의 각종 변수들을 한줄로 늘어서게한것이다. 이렇게 한줄로 줄을 세워놓으면 그 데이타를 컴퓨터의 저장장치에 저장 하거나 네트워크로 전달하기에 편리하기 때문이다. 오브젝트나 클래스를 한줄로 직렬화 해주는 놈을 시리얼라이저라 부르고 직렬화된 값을 다시 오브젝트나 클래스 상태로 복원하는 작업을 디시리얼라이즈(deserialize)라고한다. 


시리얼라이즈 가능한 것

1. 클래스 스스로 시리얼라이즈 할 수있는 것아니다. 일단 [Serializable]를 붙여 줘야한다. 

2. 기본 데이터 타입 ( ‘int’, ‘float’, ‘double’, ‘bool’, ‘string’, etc) 가능함

3. [Serializable]를 붙인 구조체

4. 직렬화 가능한 필드의 배열

5. 직렬화 가능한 필드의 Type <T>

6. ’UnityEngine.Object’에서 파생한 오브젝트 참조


시리얼라이즈 할 수 없는 것

1. ’static’, ’const’, ’readonly’, ’fieldtype’

2. 추상클래스

3. 다형성은 지원 안함

4. NULL 지원 안함.


시리얼라이즈 제외

간단히 [Serializable]를 붙여주면 해당 클래스는 시리얼라이즈가 가능한 상태가 된다. 시리얼라이즈가 필요하지 않은 부분은 선택적으로 제외할 수있다. [NonSerialized]를 붙이면 시리얼라이즈 대상에서 제외된다.


[Serializable]

class MyClass

{

   public string name;

}


[Serializable]

class MyClass

{

   public int age;

   [NonSerialized]

   public string name;    //시리얼라이즈에서 제외됨

}


시리얼라이저

시리얼라이저는 오브젝트를 스트링 타입 또는 바이트 배열 형태로 시리얼라이즈한 후 이 값을 반환한다. 반환된 값을 클라이언트가 저장형태로 사용하거나 서버로 전달 형태로 사용할 수 있다.


시리얼라이저 예제

/*
 * 스트링으로 시리얼라이즈 & 디시리얼라이즈
 * 바이트배열로 시리얼라이즈 & 디시리얼라이즈
*/

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public class Serializer
{
    #region serialize

    //오브젝트 시리얼라이즈 후 결과 값을 스트링으로 변환하여 반환
    public static string ObjectToStringSerialize(object obj)
    {
        using (var memoryStream = new MemoryStream())
        {
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(memoryStream, obj);
            memoryStream.Flush();
            memoryStream.Position = 0;

            return Convert.ToBase64String(memoryStream.ToArray());
        }
    }

    //오브젝트를 시리얼라이즈 후 바이트 배열 형태로 반환
    public static byte[] ObjectToByteArraySerialize(object obj)
    {
        using (var memoryStream = new MemoryStream())
        {
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(memoryStream, obj);
            memoryStream.Flush();
            memoryStream.Position = 0;

            return memoryStream.ToArray();
        }
    }

    #endregion

    #region Deserialize

    //스트링 타입의 시리얼라이즈된 데이타를 디시리얼라이즈 후 해당 타입으로 변환하여 반환
    public static T Deserialize<T>(string xmlText)
    {
        if (xmlText != null && xmlText != String.Empty)
        {
            byte[] b = Convert.FromBase64String(xmlText);
            using (var stream = new MemoryStream(b))
            {
                var formatter = new BinaryFormatter();
                stream.Seek(0, SeekOrigin.Begin);
                return (T)formatter.Deserialize(stream);
            }
        }
        else
        {
            return default(T);
        }
    }

    //바이트 배열 형태의 시리얼라이즈된 데이타를 디시리얼라이즈 후 해당 타입으로 변환하여 반환
    public static T Deserialize<T>(byte[] byteData)
    {
        using (var stream = new MemoryStream(byteData))
        {
            var formatter = new BinaryFormatter();
            stream.Seek(0, SeekOrigin.Begin);
            return (T)formatter.Deserialize(stream);
        }
    }

    #endregion
}

설명

게임 중 생성된 인스턴스를 생성하고 게임이 진행 되면서 인스턴스가 가지는 값들이 변경되었다. 그리고 이 상태를 그대로 저장하기위해 또는 서버로 전송하기 위해 시리얼라이즈가 필요한 상황이다. 이때 인스턴스를 파라미터로 시리얼라이저에게 전달한다. 시리얼라이저는 전달받은 오브젝트를 시리얼라이즈 후 스트링 또는 바이트 배열 형태로 반환한다. 반환받은 데이타를 저장또는 서버로 전달이 가능하다. 인스턴스를 시리얼라이즈하기 위해서는 바이너리 포매터(BinaryFormatter)메모리 스트림(MemoryStream)이 필요하다. 시리얼라이즈 기능은 바이너리 포매터에 포함되어있다. 이것으로 시리얼라이즈 할 때 파라미터를 인스턴스와 메모리 스트림을 전달하면 메모리 스트림에 인스턴스가 직렬화되어 들어간다. 여기까지의 과정은 똑같고, 메모리 스트림을 어떻게 가공하느냐에 따라 반환 값이 달라진다. ToArray()를 사용하면 바이트 배열형태로 반환하고 ToArray() 한 값을 ToBase64String()로 컨버팅하게 되면 스트링 형태로 반환하게된다. 디시리얼라이즈는 이과정을 거꾸로한다고 생각하면 된다. 스트링 타입은 바이트 배열로 변환하고 바이트 배열은 메모리 스트림으로 들어간다. 이 메모리 스트림의 바이너리 포매터의 디시리얼라이즈 기능을 사용해 디시리얼라이즈한다. 최종적으로 해당 타입<T>으로 형변환 하여 반환하게 된다.


시리얼라이즈된 데이터를 에디터로 열어보면 위와같이 보인다. 해당 데이타를 저장이나 전송할 때 사용하게된다. 그전에 MD5SHA-1 등의 암호화를 거치기를 권장한다.

※ 주의 'base64String()'로 컨버팅 하지않고 그냥 ToString()을 하게되면 다른 플랫폼으로 전달 시 전달 된 곳에서 인식하지 못하는 경우도 있다. 반드시 base64 컨버팅하는 것을 추천한다.


프리팹(prefab)

유니티에서 사용하는 프리팹은 게임오브젝트 한개 혹은 여러개를 시리얼라이즈 한 것이다. 이것은 에디터에서만 생성과 수정이 가능하다. 에디터가 아닌 게임 중상태에서 프리팹은 일반 게임오브젝트와 구별되지 않는다. 프리팹 뿐만아니라 시리얼라이즈 개념은 인스팩터에도 적용된다. 인스펙터에 표시되는 정보들은 C# API 호출로 부터 발생하는 것이 아닌, 시리얼라이즈와 디시리얼라이즈로 표시되는 것이다.


시리얼라이즈 예제

게임 정보를 담당하는 클래스를 만들고 이것을 인스턴스화한다. UI에서 입력된 값들로 정보를 갱신한다. 저장버튼을 누르면 해당 인스턴스가 시리얼라이즈 되어 스트링형태로 저장된다. 로드 버튼을 누르면 반대의 과정이 일어난다. 스트링형태로 저장된 데이타를 디시리얼라이즈 과정을 거치고 최종적으로 게임 정보 인스턴스를 반환한다. 반환된 인스턴스의 정보들을 UI에 표시한다.



클래스 다이어그램


스크립트 설명

/*
 * UI 모음
 * 이름 입력, 표시
 * 리벨 입력, 표시
*/

using UnityEngine;
using UnityEngine.UI;

public class UIview : MonoBehaviour
{
    public Text textName;   //이름
    public Text textLevel;  //레벨

    public Text inputName;    //입력필드의 이름 값
    public Text inputLevel;   //입력필드의 레벨 값
}
화면에 표시되는 UI를 모아둠
/*
 * 게임 초기화
 * 게임 저장, 게임 로드 버튼 클릭 처리
*/

using UnityEngine;
using System;

public class Controller : MonoBehaviour
{
    public UIview uiView;       //UIview 컴포넌트 캐싱
    private SaveLoad saveLoad;  //저장/불러오기 객체
    private GameInfo gameInfo;  //게임 정보 객체

    void Start()
    {
        InitGame();
    }

    //게임 초기화
    private void InitGame()
    {
        //저장/불러오기 객체 초기화
        saveLoad = new SaveLoad();
        //게임 정보 객체 초기화
        gameInfo = new GameInfo("teddy", 1);

        //초기화한 게임 정보를 화면에 정보 표시
        uiView.textName.text = gameInfo.GetUserName();
        uiView.textLevel.text = gameInfo.GetUserLevel().ToString();
    }

    //저장 버튼 클릭
    public void OnclickSaveButton()
    {
        //입력한 정보를 화면에 표시
        //uiView.textName.text = uiView.inputName.text;
        //uiView.textLevel.text = uiView.inputLevel.text;

        //게임 정보 입력
        gameInfo.SetUserName(uiView.inputName.text);
        gameInfo.SetUserLevel(Convert.ToInt32(uiView.inputLevel.text));

        //입력한 정보를 저장
        saveLoad.SaveGameInfo(gameInfo);
    }

    //불러오기 버튼 클릭
    public void OnclickLoadButton()
    {
        //저장된 게임 정보 로드하여 게임 정보 객체에 할당
        gameInfo = saveLoad.LoadGameInfo();

        //불러온 게임 정보를 화면에 표시
        uiView.textName.text = gameInfo.GetUserName();
        uiView.textLevel.text = gameInfo.GetUserLevel().ToString();
    }
}
게임이 시작되고 정보들을 초기화한다. UI 버튼에 대한 입력을 받아서 각각의 기능을 처리한다.
/*
 * 저장할 게임 정보 클래스
*/

using System;

[Serializable]
public class GameInfo 
{
    private string userName;    //유저 이름
    private int userLevel;      //유저 레벨

    public GameInfo(string userName, int userLevel)
    {
        this.userName = userName;
        this.userLevel = userLevel;
    }

    //유저이름 가저오기
    public string GetUserName()
    {
        return this.userName;
    }

    //유저 레벨 가져오기
    public int GetUserLevel()
    {
        return this.userLevel;
    }

    //유저 이름 할당
    public void SetUserName(string userName)
    {
        this.userName = userName;
    }

    //유저 레벨 할당
    public void SetUserLevel(int userLevel)
    {
        this.userLevel = userLevel;
    }
}
게임에서 사용되는 정보들을 담은 클래스이다. 이 클래스에는 [Serializable] 이 붙어있어 시리얼라이즈가 가능하다
/*
 * 게임을 저장하고 로드합니다.
*/

using UnityEngine;

public class SaveLoad
{
    //저장/로드 할 때 필요한 키 값
    private readonly string SAVEKEY = "GAMEINFO_SAVE_KEY";

    //게임 정보 저장하기
    public void SaveGameInfo(GameInfo gi)
    {
        //객체를 스트링형태로 변환
        string saveData = Serializer.ObjectToStringSerialize(gi);
        //변환된 스트링 값을 PlayerPrefs에 저장
        PlayerPrefs.SetString(SAVEKEY, saveData);
    }

    //게임 정보 불러오기
    public GameInfo LoadGameInfo()
    {
        //PlayerPrefs에 저정된 스트링 형태의 게임 정보 가져오기
        string laodData = PlayerPrefs.GetString(SAVEKEY, string.Empty);
        //스트링형태의 정보를 게임 정보 타입으로 디시리얼라이즈 후 반환
        return Serializer.Deserialize<gameinfo>(laodData);
    }
}
게임 정보를 담은 인스턴스를 시리얼라이즈 요청하고 반환된 값을 프리퍼런스에 저장한다. 반대로 불러올 때에는 디시리얼라이즈 과정을 거쳐 게임 정보 인스턴스를 반환한다.


다운로드

SerializeSaveExample.unitypackage


참고

http://docs.unity3d.com/Manual/script-Serialization.html


댓글