.NET 객체 직렬화 방법

.NET 객체 직렬화는 주로 다른 도메인간 객체를 전송하기 위해 많이 이용하는 기법입니다. 객체의 상태를 지속시키거나 전송 할 수 있는 연속적인 바이트의 배열로 변환하는 직렬화(Serialization)와 반대로 저장장치로부터 다시 객체의 상태로 되돌리는 역직렬화(deSerialzation) 작업으로 나눌 수 있습니다.

이때 직렬화와 역직렬화 작업 수행시 중간에 포매터(Formatter)를 선택해야 하는데 .NET 에서는 기본적으로 두가지의 포매터를 제공해줍니다.

첫번째는 BinaryFormatter 로 객체를 바이너리 형식으로 직렬화/역직렬화 해줍니다. 두번째는 SoapFormatter 로 객체를 XML 포맷으로 SOAP 형식으로 직렬화/역직렬화 해줍니다. 객체 직렬화 과정은 내부적으로 상당히 복잡한 과정을 거치지만,  자동으로 내부적으로 해주므로 규칙만 익히면 됩니다.
객체직렬화

객체 직렬화의 목적

.NET 객체 직렬화는 기본적으로 두가지 목적으로 사용됩니다. 객체의 상태를 저장매체에 기록해 두었다가 나중에, 그 객체의 상태 그대로 다시 꺼내오는 목적과 .NET 리모팅 기술에서 어플리케이션 도메인간에 객체가 갖는 데이터 자체를 전송해야 하는 경우입니다.

객체 직렬화의 장단점

직렬화의 주요한 장점은 DOM을 이용하지 않고 XML 문서를 수정할 수 있으며 어떤 어플리케이션으로부터 다른 어플리케이션으로 객체를 전달할 수 있다는 장점이 있습니다. 더불어 객체를 XML 문자열의 형태로 직렬화 하게 되면 80번 포트로 방화벽을 거쳐 전달 할 수 있는데 이는 방화벽에 제약을 받지 않고 객체를 전달할 수 있다는 의미가 됩니다.

반면 직렬화의 주요한 단점은 직렬화와 역직렬화 연산 과정에서 자원(CPU와 IO 장치)의 소모가 많다는 것입니다. 또한, 네트워크를 통해 객체를 전달할 경우 직렬화 과정이 느려서 지연 문제가 발생할 수 있습니다. 마지막으로 XML로 직렬화하는 것은  XML 문서를 다른 프로그램이나 사용자가 실수나 고의로 문서의 내용을 변경할 수 있어서 안전하지 않으며, 많은 저장 공간을 차지하고, public 클래스에 대해서만 직렬화가 이루어질 수 있으므로 private 클래스 또는 internal 클래스는 직렬화할 수 없다는 단점이 존재합니다.

직렬화 개체의 선언

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

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

Binary Seralization 유형

Binary Serialization은 출력 스트림에 데이터를 기록하는 메커니즘으로 이 출력 스트림은 객체를 자동으로 재구성하는데 사용할 수 있습니다. binary라는 용어가 저장매체에 저장되어 있는 객체와 동일한 복사본을 생성하는데 필요한 필수적인 정보라는 뜻을 가지고 있습니다.

binary serialization과 XML serialization간의 주목할만한 차이는 binary serialization이 인스턴스의 identity(instance identity)를 가지고 있는 반면에 XML serialization은 그렇지 않다는 점입니다. 다시 말하자면, binary serialization은 객체의 모든 상태값을 저장하고 있는 반면, XML serialization은 객체가 가지고 있는 데이터의 일부분만을 저장하고 있다는 것으로 풀이됩니다.

다음 코드는 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는 객체를 네트워크를 통해 전송하기 전에 암호화하고 적합한 포맷으로 직렬화하는데 사용됩니다.

.NET은 BinaryFormatter와 SoapFormatter라는 2개의 formatter 클래스를 제공하는데 이 두 클래스 모두 IFormatter 인터페이스를 구현하고 있습니다.

Binary Formatter

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

SOAP Formatter

SOAP formatter는 SOAP 프로토콜을 이용하는 객체의 직렬화에 사용됩니다. 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)도 직렬화할 수 있습니다.
  • 읽기전용 속성은 그 속성이 컬렉션 클래스의 객체인 경우를 제외하고는 직렬화되지 않는다는 점을 주의해야 합니다. binary serialization을 이용하면 읽기전용 속성도 직렬화 할 수 있습니다.
  • XmlSerializer를 이용하여 어떤 클래스를 직렬화할 때, 그 클래스의 특정한 속성을 직렬화하지 않을 필요가 있다면, 그 속성에 XmlIgnoreAttribute 특성을 적용 하여야만 합니다. SoapFormatter를 사용하는 경우에는 SoapIgnoreAttribute 특성을 사용하여 직렬화하지 않도록 처리할 수 있습니다.
  • XmlSerializer를 Web Service에 이용할 경우에 XmlSerializer는 각 타입(형)에 대해 최초로 호출될 때마다 최적화된 in-memory 어셈블리를 생성합니다. 이 작업은 많은 시간이 걸리는데 이 문제를 해결하기 위해서 sgen.exe 툴을 이용하여 그 serialization 어셈블리를 미리 생성해 놓는 방법을 사용할 수 있습니다.

댓글 남기기