[C#] 직렬화 (Serialization)


Development note/C#  2019. 6. 27. 09:00

안녕하세요. 명월입니다.


이 글은 C#에서의 직렬화에 대한 글입니다.


사실 오늘 C# 어트리뷰트의 글을 수정하다가 직렬화에 대한 내용이 필요하다고 생각해서 작성했습니다.

링크 - [C# 강좌 - 22] 어튜리뷰트 - (Atturibute)


직렬화는 간단하게 이야기하면 메모리에 할당된?, 클래스의 객체를 바이너리화하는 것을 직렬화라고 합니다. 즉, 클래스 내부에 있는 데이터, 변수의 값들을 byte형식의 데이터로 만드는 것입니다.

이 직렬화는 예전에는 좀 사용했던 기억이 있는데, 요즘에는 객체 데이터를 json 형식으로 저장하는게 대세라 아무래도 최근에는 잘 사용하지 않습니다.

링크 - [C#] Newtonsoft.JSON를 이용한 Json 다루기


그러나 json의 경우는 pubilc 데이터를 String으로 변환하는 것이라 아무래도 한계가 있지만 직렬화의 경우에는 class의 상태 자체를 byte형식으로 변환하는 것이라 private과 상속을 받았으면 그 상위의 private 데이터까지 변환할 수 있습니다.

그럼 모든 것을 다 직렬화를 하는 것이 좋을 것이라 생각하는 데 직렬화는 몇가지 단점이 있네요.


첫번째는 아무래도 메모리에 있는 내용을 그대로 byte 형식으로 변환시킨 것이라 메모장으로 열어 볼 때, json의 경우는 문법이 있기 때문에 이해를 할 수 있지만 직렬화 시킨 데이터는 이해할 수가 없습니다. (반대로 데이터를 숨겨야할 필요성이 있다고 하면 훌륭한 암호화가 되겠네요.)


두번째는 직렬화 시킨 데이터를 저장하고 혹시 소스가 변경되면(변수 명과 변수가 추가) 아무래도 그 전에 직렬화해 놓은 것을 다시 사용할 수가 없습니다.


마지막으로 json의 형식은 언어와 관계없이 이식성이 있습니다만, 직렬화 데이터는 C#에서 만들었으면 오로지 C#에서 밖에 읽히지가 않습니다.


정확하게 이야기하면 클래스가 변경되거나 다른 플렛폼에서 읽어드리는 게 전혀 불가능한 것은 아닙니다만, 데이터가 유실되거나 없어지는 문제가 발생합니다. 즉, 버그의 발생 확율이 생기는 거죠. 프로그램을 만들때 이런 리스크를 가져가는 건 좋지 않습니다.

using System;
using System.IO;
// 직렬화를 위한 라이브러리
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace Example
{
  [Serializable]
  class Node
  {
    private string data;
    public Node(string data)
    {
      this.data = data;
    }
    
    public void Print()
    {
      Console.WriteLine(this.data);
    }
  }
  class Program
  {
    static void Main(string[] args)
    {
      Node node = new Node("Test");

      // 직렬화 클래스
      var formatter = new BinaryFormatter();
      // 클래스를 직렬화하여 보관할 데이터
      byte[] data;
      using(MemoryStream stream = new MemoryStream())
      {
        formatter.Serialize(stream, node);
        data = new byte[stream.Length];
        //스트림을 byte[] 데이터로 변환한다.
        data = stream.GetBuffer();
      }

      using(MemoryStream stream = new MemoryStream())
      {
        // byte를 읽어드린다.
        stream.Write(data, 0, data.Length);
        // Stream seek을 맨 처음으로 돌린다.
        stream.Seek(0, SeekOrigin.Begin);
        // 클래스를 역직렬화 하고 Node클래스의 Print함수 실행.
        ((Node)formatter.Deserialize(stream)).Print();
      }

      // 직렬화 데이터를 파일로 저장한다.
      using(FileStream stream = new FileStream("d:\\work\\Serializable.dat", FileMode.Create, FileAccess.Write))
      {
        formatter.Serialize(stream, node);
      }

      // 파일로 부터 직렬화 데이터를 읽어드린다.
      using (FileStream stream = new FileStream("d:\\work\\Serializable.dat", FileMode.Open, FileAccess.Read))
      {
        // 클래스를 역직렬화 하고 Node클래스의 Print함수 실행.
        ((Node)formatter.Deserialize(stream)).Print();
      }

      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}

Node라는 클래스를 직렬화해서 byte[]로 변환했습니다. 다시 byte[]된 데이터를 역 직렬화해서 Print함수를 호출하면 결과는 Test가 제대로 나옵니다.

처음에는 MemoryStream을 사용했지만 두번째는 실제 데이터를 파일로 만든 다음에 다시 읽어 왔습니다.


역시 결과는 Test가 나오겠습니다.

직렬화된 데이터를 메모장으로 열어봤습니다.

역시 사람이 해석하기엔 어려움이 있습니다.


여기까지 C#에서의 직렬화에 대한 설명이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.