본문으로 바로가기

객체 직렬화와 관련된 일반적인 사항들은 다음 사이트를 참고한다.
http://en.wikipedia.org/wiki/Serialization
http://msdn.microsoft.com/ko-kr/library/system.serializableattribute.aspx

Serialization(직렬화) : WikiPedia에 나와 있는 정의를 대충(?) 요약하면 다음과 같다.

In computer science, in the context of data storage and transmission, serialization is the process of saving an object onto a storage medium (such as a file, or a memory buffer) or to transmit it across a network connection link in binary form. The series of bytes or the format can be used to re-create an object that is identical in its internal state to the original object (actually, a clone).
This process of serializing an object is also called deflating or marshalling an object. The opposite operation, extracting a data structure from a series of bytes, is deserialization (which is also called inflating or unmarshalling).


직렬화는 객체를 저장매체(파일이나 메모리와 같은)에 저장하거나 바이너리(이진) 형태로 네트워크를 통해 전달하기 위해 저장하는 과정이다. 이(객체를 직렬화한) 연속된 바이트나 포맷은 다시 원래 객체가 가지고 있던 상태로 객체를 재생성하는데 사용할 수 있다. 이렇게 객체를 직렬화하는 과정은 디플레이팅(deflating) 또는 마샬링(marshalling)이라고도 부른다. 직렬화와는 반대로 연속된 바이트로부터 어떤 데이터 구조를 추출해내는 과정을 역직렬화(deserialization)라고 한다. 역직렬화는 인플레이팅(inflating) 또는 언마샬링(unmarshalling)이라 불리기도 한다.

간단한 직렬화 예제

C#에서 객체 직렬화가 가능한 클래스를 만들기 위해서는 다음 한 가지만 처리하면 된다.
SerializableAttribute 특성을 클래스에 적용한다.

다음은 클래스에 SerializableAttribute 특성을 적용하여 직렬화하고 역 직렬화하는 예제이다.

using System;
using System.Runtime.Serialization.Formatters.Soap; //SOAP Formatter를 이용하여 직렬화, 역직렬화할 경우에 사용한다.
//using System.Runtime.Serialization.Formatters.Binary; //Binary Formatter를 이용하여 직렬화, 역직렬화할 경우에 사용한다.
using System.IO;

 

namespace Infodigm
{
    [Serializable()]   //클래스에 SerializableAttribute 특성을 적용하여 직렬화 가능한 클래스로 지정한다.
    class SerializablePeople
    {
        private string _name; 

        private readonly string _socialSecurityNumber;

 

        public string Name //public 멤버는 직렬화에 포함된다.
        {
            get { return _name; }
            set { _name = value; }
        }

 

        public string SocialSecurityNumber
        {
            get { return _socialSecurityNumber; }
        }

 

        public SerializablePeople(string name, string socialSecurityNo)
        {
            _name = name;
            _socialSecurityNumber = socialSecurityNo;
        }

 

        public override string ToString()
        {
            return "Name = " + _name + ", SocialSecurityNo = " + _socialSecurityNumber;
        }
    }

 

    static class Program
    {
        static void Main(string[] args)
        {

            //클래스 오브젝트 생성
            SerializablePeople people = new SerializablePeople("홍길동", "111111-1111111");
            Console.WriteLine("원본 : " + people.ToString());

            //직렬화해서 저장할 파일스트림을 연다.
            Stream stream = File.Open("data.xml", FileMode.Create);

            //SOAP Formatter 개체를 만든다.
            SoapFormatter soapFormatter = new SoapFormatter();
            //BinaryFormatter binaryFormatter = new BinaryFormatter();

            //오브젝트를 SOAP Formatter로 직렬화하여 파일스트림으로 내보낸다.
            soapFormatter.Serialize(stream, people);
            //binaryFormatter.Serialize(stream, people);

            //파일 스트림을 닫는다.
            stream.Close();

            people.Name = "DESERIALIZED";
            Console.WriteLine("직렬화 후 원본변경 : " + people.ToString());
            people = null;

            //직렬화된 파일을 연다.

            stream = File.Open("data.xml", FileMode.Open);

            //파일로부터 역직렬화하여 원래 오브젝트로 캐스팅한다.
            SerializablePeople deSerializedPeople = (SerializablePeople)soapFormatter.Deserialize(stream);
            //SerializablePeople deSerializedPeople = (SerializablePeople)binaryFormatter.Deserialize(stream);
            stream.Close();
            Console.WriteLine("직렬화된 데이터 복원 : " + deSerializedPeople.ToString());

            Console.ReadLine();
        }
    }
}

※ SoapFormatter를 사용했을 때 주의할 점은 .NET Framework 버전의 호환이 되지않는다는 점이다.

이점과 관련해서는 MSDN에 다음과 같이 설명되어 있다

SoapFormatter는 .NET Framework 버전 간의 serialization 호환성을 지원하지 않습니다. 따라서 Framework 버전 1.1 형식과 2.0 형식 간의 serialization은 실패할 수 있습니다. 이 문제를 해결하려면 다음을 수행합니다. 1.1과 2.0 간의 호환성을 제공하는 BinaryFormatter를 사용하도록 변환합니다. 보관된 기존 데이터를 새 형식으로 변환합니다. serialize된 데이터의 생산자와 소비자를 모두 버전 2.0으로 변환합니다. 1.1에서 2.0으로 변경된 형식은 사용하지 않습니다.

다음 사이트에는 앞서 대충 살펴본 직렬화에 대한 좀 더 자세한 설명이 나온다.
http://www.codeproject.com/KB/cs/NetSerialization.aspx
위 사이트의 내용 중 Serialization을 이해하는데 도움이 될만한 내용만 추렸다.

직렬화의 장단점

직렬화의 주요한 장점은
DOM을 이용하지 않고 XML  문서를 수정할 수 있다.
어떤 어플리케이션으로부터 다른 어플리케이션으로 객체를 전달할 수 있다.
어떤 도메인으로부터 다른 도메인으로 객체를 전달할 수 있다.
객체를 XML 문자열의 형태로 방화벽을 거쳐(통과해) 전달할 수 있다(방화벽에 제약을 받지 않고 객체를 전달할 수 있다).

반면 직렬화의 주요한 단점은
직렬화와 역직렬화 과정에서 자원(CPU와 IO 장치)의 소모가 많다.
네트워크를 통해 객체를 전달할 경우 지연 문제가 발생할 수 있다(직렬화 과정은 상당히 느리다).
XML로 직렬화하는 것은 안전하지 않으며(XML 문서의 형태로 저장할 경우 다른 프로그램이나 사용자가 실수나 고의로 문서의 내용을 변경할 수도 있다.), 많은 저장 공간을 차지하고, public 클래스에 대해서만 직렬화가 이루어질 수 있으므로 private 클래스 또는 internal 클래스는 직렬화할 수 없다(프로그래머로 하여금 직렬화를 지원하는 클래스를 만들기 위해 불필요한 public 클래스를 양산할 수도 있다).

SerializableAttribute 특성
Part II에서 직렬화 가능한 클래스를 만들기 위해서는 클래스에 SerializableAttribute 특성을 적용해야 한다고 했다. 이 특성이 적용된 클래스는 직렬화가 가능하다. 하지만, 그 클래스의 멤버 중 NonSerializableAttribute 특성이 적용된 멤버를 제외한 나머지 모든 멤버가 직렬화가 가능해야 한다. NonSerializableAttribute 특성이 적용된 멤버는 직렬화할 때 무시된다. SerializableAttrbute 특성이 적용된 클래스의 private 멤버와 public 멤버는 기본적으로 직렬화된다.

        [Serializable]
        public class Employee
        {
            public int empCode;
            public string empName;
        }

직렬화의 유형

직렬화는 다음과 같은 유형으로 나뉜다.

Binary Seralization
SOAP Serialization
XML Serialization
Custom Serialization
Binary Serialization

Binary Serialization은 출력 스트림에 데이터를 기록하는 메커니즘으로 이 출력 스트림은 객체를 자동으로 재구성하는데 사용할 수 있다. binary라는 용어가 저장매체에 저장되어 있는 객체와 동일한 복사본을 생성하는데 필요한 필수적인 정보라는 뜻을 가지고 있다. binary serialization과 XML serialization간의 주목할만한 차이는 binary serialization이 인스턴스의 identity(instance identity)를 가지고 있는 반면에 XML serialization은 그렇지 않다는 점이다. 다시 말하자면, binary serialization은 객체의 모든 상태값을 저장하고 있는 반면, XML serialization은 객체가 가지고 있는 데이터의 일부분만을 저장하고 있다는 것이다. binary serialization은 동일한 객체를 여러 곳에서 참조하는 그래프(graphs-object graphs; 역자주: object graphs의 정확한 의미를 모르겠다. 다만, 여기서는 순환참조와 같이 복잡한 참조 관계를 의미한다고 보면 이해하는데 큰 무리가 없을 것 같다)를 다룰 수 있다; XML serialization은 각 참조를 어떤 유니크한 객체에 대한 참조로 바꾼다.(?? XML serialization will turn each reference into a reference to a unique object).

관리되는 환경(managed environment)에서의 Binary Serialization의 주요한 장점은 객체를 직렬화한 그대로 역직렬화할 수 있다는 점이다(객체가 원래 가지고 있던 값들을 그대로 복원할 수 있다). 더구나, 속도가 빠른만큼 더 나은 성능을 보일 뿐만 아니라 복합적인 객체를 지원하고, 읽기전용(readonly) 속성과 순환참조(circular references)도 지원하는 등 더 강력하다. 하지만, 다른 플랫폼으로 이식하는 것이 쉽지 않다는 단점이 있다.

다음 코드는 Binary Serialization의 예이다.

        public void BinarySerialize(string filename, Employee emp)
        {
            FileStream fileStreamObject;

            try
            {

                fileStreamObject = new FileStream(filename, FileMode.Create);
                BinaryFormatter binaryFormatter = new BinaryFormatter();
                binaryFormatter.Serialize(fileStreamObject, emp);
            }

            finally
            {
                fileStreamObject.Close();
            }
        } 


        public static object BinaryDeserialize(string filename)
        {
            FileStream fileStreamObject;

            try
            {
                fileStreamObject = new FileStream(filename, FileMode.Open);
                BinaryFormatter binaryFormatter = new BinaryFormatter();
                return (binaryFormatter.Deserialize(fileStreamObject));
            }

            finally
            {
                fileStreamObject.Close();
            }
        } 

SOAP Serialization
SOAP 프로토콜은 서로 다른(heterogeneous) 아키텍쳐를 가지는 어플리케이션 간의 통신에 이상적이다. .NET에서 SOAP Serialization을 이용하려면 어플리케이션이 System.Runtime.Serialization.Formatters.Soap을 참조해야 한다. SOAP Serializaion의 기본적인 장점은 이식성이다. SoapFormatter는 객체를 직렬화해서 SOAP 메시지로 바꾸거나, SOAP 메시지로를 파싱해서 직렬화된 객체를 추출해낸다.

다음은 SOAP Serialization의 예제이다.
        public void SOAPSerialize(string filename, Employee employeeObject)
        {
            FileStream fileStreamObject = new FileStream(filename, FileMode.Create);
            SoapFormatter soapFormatter = new SoapFormatter();
            soapFormatter.Serialize(fileStreamObject, employeeObject);
            fileStreamObject.Close();
        } 


        public void SOAPSerialize(string filename, Employee employeeObject)
        {
            FileStream fileStreamObject = new FileStream(filename, FileMode.Create);
            SoapFormatter soapFormatter = new SoapFormatter();
            soapFormatter.Serialize(fileStreamObject, employeeObject);
            fileStreamObject.Close();
        } 

XML Serialization
MSDN에 따르면, "XML serialization은 어떤 오브젝트의 공용 필드와 속성 또는 메서드의 매개변수와 반환값을 명시적인 XML 스키마 정의 언어 문서(XSD; XML Schema definition language)에 맞는 XML 스트림으로 변환(직렬화)한다. XML serialization의 결과로 전송이나 저장을 위해 순차적인 포맷(여기서는 XML)으로 변환된, 공용 속성과 필드들을 가진 강력한 형식의 클래스(strongly typed classes)가 만들어진다. XML은 개방형 표준이기 때문에, 그 XML 스트림은 어떤 플랫폼이 어떤 프로그램에서도 처리될 수 있다."라고 되어 있다.

.NET에서 XML Serialization을 구현하는 것은 아주 쉽다. serializatin과 de-serialization을 위해 필요한 기본 클래스는 XmlSerializer이다. Web 서비스는 통신에 SOAP 프로토콜을 사용하는데, 이 XmlSerializer 클래스를 이용하여 반환형과 매개변수를 모두 직렬화한다. XML Serialization은 Binary Serialization과 비교하면 상당히 느리다.

[XmlAttribute("empName")] //클래스의 멤버에 대해 개별적으로 XmlAttribute 특징을 적용할 수 있다.
        public string EmpName
        {
            get
            {
                return empName;
            }

            set
            {
                empName = value;
            }
        }

 

        public void XMLSerialize(Employee emp, String filename)
        {
            XmlSerializer serializer = null;
            FileStream stream = null;

            try
            {
                serializer = new XmlSerializer(typeof(Employee));
                stream = new FileStream(filename, FileMode.Create, FileAccess.Write);
                serializer.Serialize(stream, emp);
            }

            finally
            {
                if (stream != null)
                    stream.Close();
            }
        }


        public static Employee XMLDeserialize(String filename)
        {
            XmlSerializer serializer = null;
            FileStream stream = null;
            Employee emp = new Employee();

            try
            {
                serializer = new XmlSerializer(typeof(Employee));
                stream = new FileStream(filename, FileMode.Open);
                emp = (Employee)serializer.Deserialize(stream);
            }

            finally
            {
                if (stream != null)
                    stream.Close();
            }
            return emp;
        } 

Formatters 활용하기
formatter는 객체의 직렬화 포맷을 결정한다. 다시 말하자면, 객체를 스트림으로 직렬화하거나 스트림을 역직렬화하여 객체를 재생성해내는 것을 제어한다. formatter는 객체를 네트워크를 통해 전송하기 전에 암호화하고 적합한 포맷으로 직렬화하는데 사용된다. formatter는 IFormatter라는 인터페이스를 노출시킨다(구현하고 있다). IFormatter의 중요한 메서드로는 실제 직렬화와 역직렬화를 수행하는 Serialize와 De-Seialize 메서드가 있다. .NET은 BinaryFormatter와 SoapFormatter라는 2개의 formatter 클래스를 제공한다. 이 두 클래스 모두 IFormatter 인터페이스를 구현하고 있다.

Binary Formatter
Binary formatter는 이진(binary) 인코딩을 이용한 직렬화하는 방법을 제공한다. BinaryFormater 클래스는 .NET의 Remoting기술에서 흔히 사용되는 이진 직렬화(binary serialziation)를 책임지고 있다. 이 클래스는 데이터가 방화벽을 통해 전달되어야 할 경우에는 적합하지 않다.

SOAP Formatter
SOAP formatter는 SOAP 프로토콜을 이용하는 객체의 직렬화에 사용된다. SOAP formatter는 Soap envelop을 생성는데 사용되고 결과를 생성하기 위해 객체 그래프(object graph; 인스턴스들 간의 참조 관계)이용한다(?? It is used to create a Soap envelop and it uses an object graph to generate the result). SOAP formatter는 객체를 직렬화해서 SOAP 메시지로 바꾸거나 SOAP 메시지를 파싱해 직렬화된 객체를 추출해내는 역할을 한다. SOAP formatter는 .NET에서 WebServices에 널리 사용된다.

기억해야 할 것은 어떤 형(클래스)에 SerializableAttribute 특성을 적용하면, 그 클래스의 모든 인스턴스 필드(private, public, protected, 기타)가 자동적으로 직렬화된다.
XMLSerializer는 ISerializable 인터페이스를 사용하지 않으며, 대신 IXmlSerializable 인터페이스를 사용한다. XmlSerializer 클래스는 BinaryFormatter클래스가 ISerializable 인터페이스를 이용하여 private 필드도 직렬화하는 것에 반하여, 해당 클래스의 public 속성들만 직렬화할 수 있다.
SerializableAttribute 특성은 어떤 클래스를 직렬화할 수 있게 만들기 위해서는 반드시 필요하다. 그 클래스가 ISerializable 인터페이스를 구현하든 하지 않든 상관없이.
어떤 클래스를 직렬화할 때, 다른 클래스를 참조하는 객체가 그 클래스에 포함되어 있어도 그 객체가 직렬화가 가능하다고 표시되어 있으면 같이 직렬화된다. public, private 또는 protected 멤버를 포함하여 모든 멤버들이 직렬화된다.
심지어 binary serialization을 이용하면 순환참조(circular references)도 직렬화할 수 있다.
읽기전용 속성은 그 속성이 컬렉션 클래스의 객체인 경우를 제외하고는 직렬화되지 않는다는 점을 주의해야 한다(Note that read only properties are not serialized except the collection class objects). binary serialization을 이용하면 읽기전용 속성도 직렬화 할 수 있다.
XmlSerializer를 이용하여 어떤 클래스를 직렬화할 때, 그 클래스의 특정한 속성을 직렬화하지 않을 필요가 있다면, 그 속성에 XmlIgnoreAttribute 특성을 적용하여야만 한다. SoapFormatter를 사용하는 경우에는 SoapIgnoreAttribute 특성을 사용하여 직렬화하지 않도록 처리할 수 있다.
XmlSerializer를 Web Service에 이용할 경우에 XmlSerializer는 각 타입(형)에 대해 최초로 호출될 때마다 최적화된 in-memory 어셈블리를 생성한다. 이 작업은 많은 시간이 걸린다. 이 문제를 해결하기 위해서 sgen.exe 툴을 이용하여 그 serialization 어셈블리를 미리 생성해 놓는 방법을 사용할 수 있다.

※ 객체 그래프(Object Graph)란?
Object Graph는 Wikipedia에 다음과 같이 설명되어 있다.
An Object Graph is a view of an object system at a particular point in time. Whereas a normal data model details the relationships between objects, the object graph relates the instances.
객체 그래프는 어떤 특정 시점의 객체 시스템에 대한 뷰이다. 보통 Data Model이 객체들 간의 관계를 상세히 설명하는데 비해, 객체 그래프는 인스턴스들 간의 관계를 설명한다.
즉, 객체 그래프는 어떤 특정 시점의 인스턴스들 간의 참조 관계를 말하는 것으로 볼 수 있겠다.

'Development > C#' 카테고리의 다른 글

비주얼스타일과 텍스트 랜더링 설정  (0) 2010.07.21
문화권 식별자  (0) 2010.07.21
ThreadPool and Socket Programming  (0) 2010.02.05
GAC(Global Asembly Cash)  (0) 2009.12.08
C# 3.0 Preview: Extension Method와 나머지  (0) 2009.12.08