바이너리의 단점

마지막 업데이트: 2022년 1월 1일 | 0개 댓글
  • 네이버 블로그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 트위터 공유하기
  • 카카오스토리 공유하기
첫번째는 BinaryFormatter 로 객체를 바이너리 형식으로 직렬화/역직렬화 해줍니다. 두번째는 SoapFormatter 로 객체를 XML 포맷으로 SOAP 형식으로 직렬화/역직렬화 해줍니다. 객체 직렬화 과정은 내부적으로 상당히 복잡한 과정을 거치지만, 자동으로 내부적으로 해주므로 규칙만 익히면 됩니다.

Bypass CRC check for fuzzing with pintools

tcp, zip, tar, png등 다양한 데이터 형식에서는 crc 체크를 통해서 파일의 무결성을 바이너리의 단점 검증한다. 만약 crc 체크가 실패하면 프로그램이 그냥 종료된다. 퍼저에 의해 무작위로 변이된 데이터가 crc를 맞출 수 있을 확률이 지극히 낮다. 이러한 문제를 해결하기 위해서 2가지 방법을 쓸 수 있다.

    에 crc 체크를 맞춰주는 코드를 넣어준다.
    1. 장점1 : Crash reproduce가 쉽다.
    2. 장점2 : crc뿐만 아니라, 다른 형식에 대해서도 적용할 수 있기 때문에 더 범용적이다.
    3. 단점1 : 하나의 포맷이 아니라, 다수의 포맷을 처리하는 프로그램의 경우, 프로그램에서 어떠한 방식으로 파일의 포맷을 결정하는지 리버싱해야 한다.
    4. switch(format(data))
    5. 단점2: 데이터 포맷에 대한 이해가 필요하다.

    2. Fuzzing할 바이너리에서 crc 체크 코드를 삭제해준다.

    1. 장점1 : 쉽다.
    2. 장점2 : 자동화를 할 여지가 많다.
    3. 단점1 : 바이너리를 임의로 패치했기 때문에, 실제로는 reproduce되지 않는 크래시일 수 있다.
    4. 단점2 : 다수의 데이터 포맷을 처리하는 경우, 가장 처음 체크하는 포맷 외에는 제대로 검증되지 않을 수 있다.
    5. if is_zip(data): #==> No format error, so always True. unzip(data) elif is_tar(data):#==> unfuzzable untar(data)

    소스코드가 있다면, crc코드를 간단하게 삭제할 수 있지만, strip된 바이너리에서는 crc 체크 구문을 찾기 힘들 수 있다. Fuzzing할 바이너리에서 crc 체크 코드를 삭제하기 위해서는 바이너리를 리버싱한 후, 어디에서 crc 체크가 이루어지는지 찾아야 한다. 하지만, 데이터의 crc값이 무엇인지 알고 있다면, pintools를 이용해서 crc 체크가 이루어지는 위치를 자동으로 찾을 수 있다.

    .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 멤버는 기본적으로 직렬화됩니다.

    Binary Seralization 유형

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

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

    다음 코드는 Binary Serialization의 예입니다.

    SOAP Serialization 유형

    SOAP 프로토콜은 서로 다른(heterogeneous) 아키텍쳐를 가지는 어플리케이션 간의 통신에 이상적입니다. .NET에서 SOAP Serialization을 이용하려면 어플리케이션이 System.Runtime.Serialization.Formatters.Soap을 참조해야 합니다.

    SOAP Serializaion의 기본적인 장점은 이식성입니다. SoapFormatter는 객체를 직렬화해서 SOAP 메시지로 바꾸거나, SOAP 메시지로를 파싱해서 직렬화된 객체를 추출해낼 수 있습니다.

    다음은 SOAP Serialization의 예제입니다.

    XML Serialization 유형

    “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과 비교하면 상당히 느리다고 할 수 있습니다.

    Formatters 활용

    formatter는 객체의 직렬화 포맷을 결정합니다. 다시 말하자면, 객체를 스트림으로 직렬화하거나 스트림을 역직렬화하여 객체를 재생성해내는 것을 제어합니다. formatter는 객체를 네트워크를 통해 전송하기 전에 암호화하고 적합한 포맷으로 직렬화하는데 사용됩니다.

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

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

    SOAP formatter는 SOAP 프로토콜을 이용하는 객체의 직렬화에 사용됩니다. SOAP formatter는 객체를 직렬화해서 SOAP 메시지로 바꾸거나 SOAP 메시지를 파싱해 직렬화된 객체를 추출해내는 역할을 합니다. SOAP formatter는 .NET에서 WebServices에 널리 사용되고 있는 실정입니다.

    Code Playground

    트리(Tree)는 이름 그대로 나무를 거꾸로 뒤집어 놓은듯한 형태이다. 자료구조가 하나의 뿌리에서 뻗어나가는 형상을 하고 있다. 트리 구조는 우리 일상에서 흔히 볼 수 있는 계층적인 구조(Hierarchical Structure)를 컴퓨터에서 표현한 구조이다. 한 가족의 족보나 회사의 조직도 등이 바로 트리 구조이다. 족보같은 경우 조상으로부터 자식, 자식의 자식, 자식의 자식의 자식 형태로 이루어져있기 때문이다.

    컴퓨터의 파일 시스템이나 웹 페이지의 DOM 구조도 트리 구조이다.

    트리 구조는 대개 위 그림과 같은 구조이다.

    트리의 각 요소들은 Node라고 부르고 가장 상위의 노드를 Root Node라고 부른다. 반대로 최하위 노드는 Leaf Node 혹은 Terminal Node라고 한다. Leaf Node는 자식 노드가 없는 트리 구조의 가장 하단에 있는 노드들을 말한다.

    한 노드에 연결된 서브 트리의 개수를 차수라고 한다. 예를 들어서 B 노드의 차수는 2개이다. 만약에 한 트리 내의 차수가 모두 2개 이하라면 그 트리를 이진 트리(Binary Tree)라고 부른다.

    현재 노드에 연결되어있는 상위 노드는 Parent Node이고 그 하위의 자식 노드는 Child Node이다. 같은 부모 노드를 갖는 노드는 형제 노드, 즉 Sibling Node이다. 이렇게 트리 구조의 용어는 정말로 가계도와 비슷하다.

    Level은 루트 노드부터 해당 노드까지 경로를 찾는 데 방문한 총 노드의 수를 말한다. 트리의 최대 레벨 수는 트리의 height(높이) 혹은 depth(깊이)라고 한다. 위 그림의 트리 구조는 Level 1부터 Level 3까지 있고 트리의 높이는 3이다.

    Binary Search Tree

    트리는 종류가 매우 많지만 그 중에서도 가장 유명한 구조 중 하나다 Binary Search Tree(이진 탐색 트리)이다.

    Binary Search Tree는 이진 트리이기때문에 모든 노드의 차수는 2개 이하여야만 한다.

    그리고 모든 왼쪽 자식 노드는 부모의 값보다 작은 값을 가져야하고

    모든 오른쪽 자식 노드는 부모의 값보다 큰 값을 가져야한다는 특징이 있다.

    Tree structure in which a node can have zero, one, or two subtrees.
    Every node in the left has a smaller value than its parent node.
    Every node in the right has a larger value than its parent node.

    즉, 6, 2, 9, 5, 8, 3, 1이란 데이터를 이진 탐색 트리로 만들면 위의 그림처럼 된다.

    가장 먼저 들어간 6이 Root Node가 된다.

    2는 6보다 작기 때문에 6의 왼쪽 자식 노드가 된다.

    9는 6보다 크기 때문에 6의 오른쪽 자식 노드가 된다.

    5는 6보다 작고 2보다 크기 때문에 2의 오른쪽 자식 노드가 된다.

    8은 6보다 크고 9보다 작기 때문에 9의 왼쪽 자식 노드가 된다.바이너리의 단점

    이런 규칙으로 이루어져있기 때문에 이진 탐색 트리는 평균적으로 이진 탐색과 똑같이 빠른 시간 복잡도를 가진다.

    Big O - average

    • Insertion: O(log n)
    • Deletion: O(log n)
    • Search: O(log n)

    만약 로그 시간이 이해가 안간다면 아래 링크를 참조해보자.

    What does the time complexity O(log n) actually mean? - By

    Knowing the complexity of algorithms beforehand is one thing, and other thing is knowing the reason behind it being like that. Whether you’re a CS graduate or someone who wants to deal with optimization problems effectively, this is something that you must

    Big O - worst case

    • Insertion: O(n)
    • Deletion: O(n)
    • Search: O(n)

    이진 탐색 트리는 대개의 경우 빠른 속도를 자랑하지만 몇 가지 단점이 있는데, 하나는 데이터를 임의 접근(random access)할 수 없다는 것이다. 예를 들어서 이 트리의 6번째 원소를 찾고싶다고 말 할 수 없다.

    또한 트리가 균형이 맞지 않는 최악의 경우에는 데이터의 삽입과 삭제, 검색이 모두 O(n)만큼 소요된다. 이진 탐색 트리의 성능이 떨어지는 경우는 다음 그림과 같은 경우이다.

    이렇게 데이터가 한쪽으로 치우쳐있으면 연결 리스트와 별 차이가 없는 구조가 되어 이진 트리의 장점이 사라진다. 이러한 이진 탐색 트리의 단점을 해결하기 위해 AVL 트리, 레드-블랙 트리, 2-3 트리 등 여러 가지 트리 구조가 존재한다.

    바이너리의 단점

    변일수 * 안창원 ** 박종근 * 조희남 * Desmons Frederic* 김성운 ***

    현재 XML 은 가장 널리 받아들여지고 있는 문서 형식 중의 하나로 그 사용 분야는 점차 확대되어 가고 있다 . 그러나 XML 의 성공은 XML 이 의도되지 않은 곳에까지 사용되는 결과를 낳았고 , 뜻하지 않던 XML 의 문제점이 거론되기 시작하였다 . 문제점 중 하나는 XML 이 너무 장황 (바이너리의 단점 verbose) 하기 때문에 과부하가 크다는 점이다 . 이는 소형기기나 네트워크 환경에서 치명적인 약점이 될 수 있다 . 이 문제를 해결하기 위해 XML 문서의 바이너리 형식에 대한 연구가 진행되기 시작하였다 . 하지만 이것은 민감한 문제이다 . XML 문서의 이진화는 XML 의 본래 취지에 역행하는 것이라고 생각하는 사람도 적지 않다 . 이들은 XML 의 이진화가 과거와 같이 독자적인 데이터 형식을 정의하게 해 폐쇄적인 환경으로 돌아가게 되는 것을 두려워한다 . XML 의 개방성을 유지하면서 내재된 문제점을 해결하기 위해서는 바이너리 XML 에 대한 표준이 필요하다 . 본 고에서는 바이너리 XML 과 관련된 연구 동향과 표준화 추진 현황을 소개하고자 한다 . ▧

    XML[1] 은 운영 환경과는 독립적인 문서 형식을 정의할 수 있다는 장점으로 인해 많은 사용자층을 확보할 수 있었고 , 현재 가장 주목받는 문서 형식 중의 하나가 되었다 .

    그러나 양날의 검처럼 XML 의 장점은 특정 분야에서는 단점으로 작용하고 있다 . XML 은 상호운용성 (Interoperability) 을 확보하고 보다 쉬운 접근이 가능하게 하기 위해 인간이 읽을 수 있는 텍스트 형식을 취하고 있다 . 이는 실제로 데이터 처리 과정에는 필요 없는 여분의 데이터를 포함하고 있다는 뜻이 되며 , 텍스트 형식을 취하게 됨으로 생기게 되는 저장 공간의 손실도 존재한다 . 일반적인 컴퓨팅 환경에서 이 정도의 과부하는 문제가 되지 않는다 . 하지만 네트워크 환경이나 임베디드 시스템 같이 상대적으로 느린 매체에 자주 접근하거나 자원이 제한된 소형 기기의 경우 XML 문서의 과부하는 전체 응용 프로그램의 성능을 결정짓는 중요한 요소가 될 수 있다 . 이에 대한 예로 웹 서비스를 생각해 볼 수 있다 . 웹 서비스는 이기종간의 상호 운영성을 보장하기 위해 메시지 형식으로 XML 형식을 취하고 있다 . 그러나 상호 운영성에 대한 대가는 적지 않다 . XML 형식의 메시지 교환은 XML 의 내재된 단점으로 인해 전체 응용 프로그램의 성능을 저하시킬 정도로 큰 과부하를 일으키고 있다 [14].

    이 때문에 업계에서는 XML 문서의 바이너리 형식에 대해 요구하기 시작하였다 . 이를 받아들여 W3C[6] 에서는 바이너리 XML 에 대한 정당성을 확인하기 위해 바이너리 XML 에 대한 쓰임새 (Usecase) 를 분석하고 취합하는 워킹 그룹 (XML Binary Characterization Working Group) 을 운영하고 있다 [2]. 바이너리 XML 의 이점은 분명하다 . 데이터를 네이티브 (native) 형식으로 저장함으로 문서의 크기를 줄이고 XML 문서의 파싱 (parsing) 과정에 따른 과부하를 줄임으로 성능을 향상시킬 수 있다 . 그러나 논란의 여지는 적지 않다 . XML 의 최대 장점은 상호운용성인데 이를 포기하고 바이너리 형식을 취한다는 것은 과거로의 회귀를 뜻하기 때문이다 . 현재로서 이에 대한 가장 현실적인 접근 방법은 XML 문서의 바이너리 인코딩 표준을 마련하는 것이다 .

    본 고에서는 현재 여러 분야에서 동시 다발적으로 일어나고 있는 바이너리 XML 연구 동향과 표준화 추진 현황을 다룬다 .

    II. 바이너리 XML 연구 동향

    XML 을 이진화함으로 성능을 향상시키고자 하는 노력이 3 가지 측면에서 기울여지고 있다 . 그것들은 압축 , 문서 구조의 분리 , 인코딩 등 총 3 가지로 분류될 수 있다 .

    XML 문서의 단점을 보완하기 위한 한가지 방법은 XML 문서를 압축하는 것이다 . 서론에서 지적되었듯이 XML 문서는 사람이 읽을 수 있는 문서 형식을 지향하기 때문에 모든 데이터가 텍스트 형식으로 저장된다 . 이것은 저장 공간을 낭비하는 원인이 된다 . 예를 들어 숫자 100 을 저장한다고 가정해 보자 . 이것을 네이티브 형식으로 저장하고자 하면 단지 한 바이트 (byte) 면 된다 . 하지만 텍스트 형식으로 저장하기 위해서는 문자 하나당 2 바이트씩 총 3 개의 문자로 6 바이트를 필요로 한다 . 즉 네이티브 형식보다 무려 6 배의 저장 공간을 차지한다는 뜻이다 . 게다가 XML 문서의 각 요소들은 한 쌍의 태그로 둘러싸인다 . 결국 XML 문서의 크기는 불필요하게 커질 수 밖에 없다 . 문서가 디스크나 네트워크와 같이 상대적으로 느린 매체를 통해 전송되어야 할 경우 , 문서의 크기는 성능에 치명적인 요소일 수 밖에 없다 . 따라서 문서의 크기를 줄이는 것은 상당히 도움이 된다 . 과부하를 느린 매체에서 상대적으로 빠른 CPU 로 옮김으로 속도를 향상시키기 때문이다 . 다시 말해서 , 느린 매체에 다량의 정보를 넘겨주고 처리가 끝나기를 기다리는 대신 , 상대적으로 처리 속도가 빠른 CPU 가 넘겨 주어야 할 정보의 양을 줄이도록 함으로 성능 상의 이득을 볼 수 있다는 뜻이다 . 그래서 일부 웹 서비스에서는 XML 바이너리의 단점 문서를 송신하기 전에 압축 과정을 거친다 . 단 수신측에서는 메시지를 읽기 위해 반드시 해제 과정을 거쳐야 한다 .

    Gzip[3] 은 GNU 프로젝트의 일환으로서 compress 를 대체하면서 , 더 나은 성능을 보이며 동시에 특허에 의해 제한되지 않는 압축 알고리즘을 제공하고 있다 . Gzip 알고리즘은 여러 언어로 이식되었으며 대부분의 라이브러리들이 오픈 소스로 제공되기 때문에 별다른 노력 없이 적용할 수 있다는 장점이 있다 . 그러나 매 연산 시 압축과 해제 과정이 수반되어야 한다는 것이 단점으로 작용한다 . 이것은 비단 Gzip 만의 문제라고는 할 수 없으며 압축에 의한 접근 방식이 어쩔 수 없이 가지게 되는 단점이다 .

    XMill[4] 은 XML 문서에 특화된 압축 알고리즘이다 . XMill 은 엔트로피 (Entropy) 기반의 압축 방식을 취하고 있다 . 엔트로피 부호화의 기본 원리는 데이터 심볼들의 통계적 발생 빈도에 따라 각각의 심볼들을 적절한 길이의 부호로 표현하는 것이다 . 데이터 심볼들의 발생 확률에 따라 엔트로피라고 불리는 심볼 당 평균 정보량이 결정되는데 , 엔트로피 부호화의 목표는 심볼 당 평균 부호 길이가 엔트로피에 가까이 가도록 하는 것이다 .

    엔트로피 부호화에는 허프만 방식 , 산술 방식 , LZW 방식이 있다 . 이중에서 XMill 에 사용된 방식은 허프만 방식이다 . 이것은 고정길이 부호를 가변길이 부호로 바꾸는 것으로서 빈도수가 높은 심볼에는 짧은 부호를 , 빈도수가 낮은 심볼에는 긴 부호를 부여함으로 평균 부호 길이를 본래 심볼의 고정 길이보다 짧게 하는 것이다 .

    영문 텍스트의 경우 , 압축하지 않은 원문은 글자 당 7 비트를 차지하는데 이를 허프만 방식을이용해 부호화하면 자주 발생하는 글자들은 대략 3 비트 정도의 낮은 비트로 , 빈도수가 낮은 기호들은 10 비트 이상의 높은 비트로 부호화시켜 심볼 당 평균 비트 수를 원래의 그것보다 낮추는 것이다 . XML 문서는 구조상 반복되는 구문이 많기 때문에 허프만 방식은 큰 효과를 나타낼 수 있다 . 실제로 XMill 은 Gzip 보다 두 배 이상의 나은 성능을 보이고 있다 .

    앞에서 우리는 압축 방식의 가장 큰 문제점으로 압축과 해제 과정에 따른 과부하를 지적하였다 . 만약 해당 문서의 모든 요소가 사용된다면 효과를 기대할 수 있지만 문서의 일부 요소만을 필요로 한다면 과부하는 더 커진다고 볼 수 있다 . 실제 필요로 하는 정보 대비 요구되는 처리 능력을 고려해 볼 때 확실히 압축에 의한 접근 방식은 비효율적이다 . 이에 대한 해결책으로 대두된 것이 ‘ 문서 구조의 분리 (Separation of Structure and Content) ’ 다 . 이 방식에서는 데이터를 압축하기 전에 문서 구조를 추출하고 콘텐츠에 대한 인덱스를 유지한다 . 그리고 콘텐츠는 용량을 줄이기 위해 압축된다 . 압축에는 gzip 이나 deflate[19] 같은 일반적인 압축 기법들이 사용된다 .

    문서 구조를 추출함으로써 갖게 되는 장점은 압축 해제 과정을 선택적으로 할 수 있다는 데에 바이너리의 단점 있다 . 먼저 원하는 콘텐츠의 위치를 문서 구조를 통해 파악하고 , 그 다음에 해당 위치의 콘텐츠만을 선택적으로 압축 해제함으로 압축 해제에 따른 과부화를 최소한으로 유지할 수 있다 . 그러나 XML 문서 전체의 요소가 전부 사용되어야만 하는 상황이라면 문서 구조 추출에 의한 이점은 대부분 상쇄되어 압축 기법과 별로 차이가 없어진다는 것이 단점이다 .

    다음은 ‘ 문서 구조의 분리 ’ 접근 방식을 취한 몇 가지 예들에 대한 설명이다 .

    Millau[5] 인코딩 형식은 WAP(Wireless Application Protocol)[11] 바이너리 XML 형식에 대한 확장이다 . 이 형식은 XML 문서의 크기를 줄이기 위해 고안되었다 . 태그와 속성은 토큰으로 변환된다 . 그리고 문서 구조와 콘텐츠를 분리해 각각의 데이터를 서로 다른 스트림으로 전송한다 . 문서 구조는 WBXML 인코딩 형식에 의해 변환된다 . 반면 콘텐츠는 일반적인 압축 알고리즘에 의해 압축된다 . 사용자는 모든 문서를 전송받을 필요가 없다 . 문서 구조와 콘텐츠가 서로 다른 스트림으로 전송되기 때문에 , 먼저 전송받은 문서 구조를 조사한 후 , 원하는 부분만을 전송받으면 된다 . 이것은 데이터 전환에 의한 과부화를 없애는 역할을 한다 . 이 형식은 네트워크 상에서 XML 문서를 주고 받을 때 , 특히 문서의 일부분만을 참조하고자 할 때 유용하다 .

    XCpaqs[15] 는 XML 문서의 압축 방식이면서 XPath 를 지원한다는 점이 특징이다 . XCpaqs 의 전체적인 구조는 다음과 같다 .

    ( 그림 1) 에서 볼 수 있듯이 ‘ Structure and context separator ’ 는 문서 구조와 콘텐츠를 분리한다 . 문서 구조를 통해서 Xpath 경로와 해당 경로의 데이터 타입이 결정된다 ( 좌측 ). 콘텐츠는 문서 구조 분석기에 의해 결정된 데이터 타입에 따라 적절한 압축기로 압축된다 ( 우측 ). 그리고 두 결과를 조합하는 것으로 모든 과정을 마무리한다 . XCpaqs 는 데이터 타입에 따라 압축 방식을 바꿀 수 있다는 특징을 가지고 있다 .

    본래의 XML 문서의 의미와 구조가 보존될 수 있도록 고유의 인코딩 포맷을 정의하는 방법도 있다 . XML 문서의 데이터들은 각각의 데이터 타입에 맞는 네이티브 형식으로 인코딩되기 때문에 공간이 절약된다 . 하지만 일반 압축 기법의 압축률과 비교할 때 인코딩으로 인해 절약되는 공간의 크기는 미약하다 . 그러나 이것이 곧 인코딩 기법의 성능이 압축 기법보다 낮다는 뜻은 결코 아니다 . 인코딩 기법의 장점은 단순히 공간을 절약하는 데에 있지 않다 . 인코딩된 바이너리 XML 문서를 Pre-parsed XML 이라고도 부르는데 , 여기에 함축된 의미는 XML 문서의 파싱 과정이 더는 필요하지 않다는 것이다 . 미리 파싱된 형태로 데이터를 저장함으로 매 접근 시마다 텍스트 문자열들을 파싱하고 데이터를 추출하는 과정을 거칠 필요가 없다는 말이다 . XML 문서가 가지는 단점 중의 하나는 데이터가 인간 친화적인 형태로 기록되었기 때문에 컴퓨터가 이해하기 위해서는 파싱 과정을 통해 데이터를 추출하고 변환해야만 한다는 데에 있다 . 이것은 가볍지 않은 처리 과정이며 , 임베디드 기기처럼 자원이 제한된 기기의 경우에는 특히나 더 그렇다 . Pre-parsed XML 에서는 파싱 과정이 제거되었기 때문에 CPU Usage 나 Memory footprint 측면에서 상당한 이득이 있으며 , 문서에 대한 접근 속도는 비약적으로 향상된다 . 이러한 장점들은 단순히 크기를 줄임으로 얻게 되는 성능상의 이득을 충분히 능가할 수 있으리라 여겨진다 . 특히 한 XML 문서에 대해 반복적인 접근이 필요한 경우 파싱 과정을 제거함으로 인해 생기는 이득은 훨씬 커진다 .

    다음은 인코딩 방법을 적용한 예들이다 .

    가 . Fast Infoset

    Fast Infoset[8] 을 이해하기 위해서는 XML 스펙에 대해 먼저 살펴보는 것이 도움이 된다 . XML 스펙에는 XML Information Set[17] 스펙이 존재한다 . 이는 XML 이 표현할 수 있는 정보를 추상적으로 정의한 것으로서 XML 이 어떤 정보를 가질 수 있는지에 대한 내용만 있을 뿐 어떻게 표현할지에 대해서는 전혀 언급이 없다 . 일종의 관념적인 모델이다 . 이에 대한 실제화를 XML[16] 스펙에서 담당하고 있다 . 달리 생각하자면 우리가 일상적으로 보게 되는 각괄호 () 로 이루어진 XML 문서는 XML Information Set 의 구현물이라고 생각할 수 있다 . Fast Infoset 은 XML Information Set 의 또 다른 구현물이며 이진 표현이다 .

    그렇다면 이진 코드는 어떻게 생성되는가 ? Fast Infoset 은 거의 20 년 동안 통신업계에서 사용되어 안정성과 실효성이 증명된 ASN.1(Abstract Syntax Notation One)[7] 이라는 추상화 구문을 이용하고 있다 . ASN.1 은 프로토콜의 설계를 위해 고안된 언어로서 프로토콜을 정의하는 추상화 구문과 실제 전송 라인에서 어떤 비트 패턴으로 표현될지에 관한 전송 구문을 완전히 분리시켜 놓았다 . ASN.1 에 의해 표현된 추상적인 프로토콜은 인코딩룰에 의해 실질적인 비트 패턴으로 변환된다 . BER(Binary Encoding Rule), PER(Packed Encoding Rule), XER(XML Encoding Rule) 등 다양한 인코딩 룰이 정의되어 있어 적합한 룰을 가져다 쓰기만 하면 된다 . 고유의 비트 패턴을 정의하고자 한다면 ECN(Encoding Control Notation) 을 통해 새로운 룰을 정의할 수도 있다 . 다음은 Fast Infoset 을 통한 XML Information Set 의 인코딩 과정을 관념적으로 표현하고 있다 [9].

    Fast Infoset 은 문서의 크기를 줄이고자 테이블과 인덱싱 기법을 사용하고 있다 . 처음 나타나는 문자열을 테이블에 저장해 놓고 동일한 문자열이 나타날 경우 인덱스로 대치함으로 XML 문서의 크기를 줄인다 .

    Fast Infoset 을 적용하였을 경우 문서의 압축률은 압축 기법에 비해 별로 좋지는 않다 . 하지만 성능상의 향상을 체감할 수 있을 정도는 되며 , 부가적으로 일반 XML 문서나 압축된 XML 문서에 비해 접근 성능이 비약적으로 향상되는 것을 확인할 수 있다 [12].

    XBIS(XML Binary Information Set)[10] 는 본래 텍스트로 이루어진 XML 문서로의 완전한 전환을 지원하는 고유의 인코딩 포맷을 정의하고 있다 . XBIS 의 장점은 간결함에 있는데 , 그것은 일반 XML 문서의 반복적인 구문을 활용하는 데에 기인한다 . XBIS 는 반복되는 구문을 단 한번만 정의하고 반복될 때마다 원래의 구문을 참조하는 핸들을 사용함으로 문서의 크기를 줄인다 . 게다가 XBIS 는 데이터를 사용하기 쉬운 형식으로 변환시켜 놓음으로 문서에 대한 접근 속도를 향상시켰다 . XBIS 의 접근 방식은 Fast Infoset 과 상당히 유사하다 . 하지만 XBIS 는 표준에 근거하지 않은 독자적인 형식이라는 데에 그 한계점이 있다 . 참조 구현은 자바로 작성되었으며 SAX2(Simple API for XML2)[18] 인터페이스를 구현하고 있다 .

    W3C 에서는 업계의 요구를 받아들여 XBC-WG(XML Binary Characterization Working Group) 를 구성하였다 . 현재 이 워킹 그룹의 역할은 바이너리 XML 의 쓰임새를 파악하여 바이너리 XML 관련 권고안 (Recommendation) 의 기초를 세우는 작업을 담당하고 있다 . 이 말을 달리 표현하자면 , 바이너리 XML 과 관련된 스펙은 W3C 내에 아직 존재하지 않는다는 뜻이다 . 다만 바이너리 XML 의 필요성에 주의를 기울이기 시작하였다는 점에 유의할 수 있다 .

    XBC-WG 에서는 Binary XML 이 반드시 만족해야 하는 최소한의 요구사항을 결정하기 위해 현재 XML 이 잘 사용되지 않는 영역의 쓰임새를 분석하였다 . 이를 위해 각종 속성을 정의하고 , 각 쓰임새별 요구사항을 이들 속성으로 표현하였으며 , 이를 XML 에서 제공하는 속성과 비교함으로 바이너리 XML 에서 제공해야 할 최소한의 요구사항으로 도출하였다 . 앞으로 규정될 바이너리 XML 에 대한 어렴풋한 윤곽을 잡을 수 있도록 XBC-WG 가 정의한 최소한 요구 사항들을 다음에 열거하였다 [13].

    - 전송 독립성 (Transport Independence)

    - 인간 언어와의 중립성 (Human Language Neutral)

    - 기술료 무료 (Royalty Free)

    - 플랫폼 중립성 (Platform Neutrality)

    - 콘텐츠 형식 관리 (Content Type Management)

    - XML 스택으로의 통합 가능성 (Integratable into XML Stack)

    결론적으로 말해서 W3C 에서도 바이너리 XML 에 대한 필요성을 인지하고 있으며 그 기술적 가능성과 요구사항에 대한 조사를 수행하였고 , 바이너리 XML 에 대한 표준에 W3C 가 적극적으로 참여하여 상호 호환성을 확보해야 함에 동의하고 있다 .

    현재 Fast Infoset 에 대한 스펙 (ISO/IEC24824-1) 은 초안 단계에 있다 . 안타깝게도 아직 공개되지 않았으며 TIES(Telecom Information Exchange Services) 사용자만이 스펙을 볼 수 있도록 제한되어 있다 . 로드맵에 의하면 Fast Infoset 은 FDIS(Final Draft International Standard) 를 위한 무기명 투표 중에 있으며 , 2005 년 6 월 중순경에 권고안이 발표될 것으로 예정되어 있다 .

    XML 은 지금까지 대단히 성공적이었다고 할 수 있다 . 어떤 플랫폼에도 종속되지 않은 독립적인 데이터 표현 방식은 이기종 간의 데이터 전송 수단으로 매우 유용함이 증명되어 왔다 . 하지만 성공의 이면에는 XML 문서에 내재된 단점의 그늘이 드리워지고 있었다 . XML 의 성공은 XML 의 사용이 의도되지 않은 분야로까지 확대되게 하였고 결국 성능과 관련된 이슈를 문제로 부각시켰다 . 이를 해결하기 위해서는 XML 문서를 이진화해야 한다 . 그리고 XML 의 장점인 상호운용성을 유지하기 위해 인코딩 형식에 대해 확립된 표준이 필요하다 .

    W3C 에서는 XML 의 이진화에 대한 요구사항을 파악하고 정당성을 파악하기 바이너리의 단점 위한 절차에 들어갔다 . 하지만 결과가 언제 나올지는 미지수다 . 하지만 여기에서 우리는 하나의 중요한 움직임에 주목할 필요가 있다 . ITU-T 에서 ISO/IEC 와 함께 XML 의 이진화에 대한 스펙을 정의하고 있다는 점이다 . Fast Infoset 이 바로 그것이다 . 아직은 공개되지 않은 초안의 형태이지만 , 올해 중반기 쯤에는 그 모습을 볼 수 있을 것으로 예상한다 . 게다가 관련 바이너리의 단점 구현물도 이미 존재하며 성능상의 이점은 확인되었다 . ASN.1 이라는 이미 확립된 표준을 기반으로 한 응용 표준이기 때문에 적용 또한 어렵지 않을 것이다 . 아직 확정된 것이 없어 섣불리 판단을 내리긴 힘들겠지만 , Fast Infoset 이 바이너리 XML 표준의 강력한 후보 중에 하나가 되리라는 것만은 틀림 없다 .

    [1] Extensible Markup Language, http://www.w3.org/XML/

    [2] XML Binary Characterization Working Group, http://www.w3.org/XML/Binary/

    [3] Gnu ZIP, http://www.gzip.org/

    [4] XMill, http://sourceforge.net/projects/xmill/

    [5] Girardot, M., Sundaresan, N., “ Efficient representation and streaming of XML content over the Internet medium, ” Multimedia and Expo, 2000. ICME 2000. 2000 IEEE International Conference on Vol.1, No.30 July 2 Aug. 2000, pp.67-70.

    [6] World Wide Web Consortium, http://www. w3c.org/

    [7] Abstrax Syntax Notation One, http://asn1.elibel.tm.fr/en/index.htm

    [8] Fast Infoset, http://asn1.elibel.tm.fr/en/xml/#fast-infoset

    [9] John, Larmouth., “ ASN.1 Complete, ” , http://www.oss.com/asn1/larmouth.html

    [10] XML Binary Information Set, http://sourceforge.net/projects/xmill/

    [11] WAP, http://www.openmobilealliance.org/tech/affiliates/wap/wapindex.html

    [12] Paul, Sasndoz., Alessando, Triglia., Santiego, Pericas-Geertsen., “ Fast Infoset ” ,

    [13] 박종근 , “ XML Binary 특성화 분석 , ” 한국전자통신연구원 , 관리번호 TM031020050734, 2005, 5. 30.

    [14] Fast Web Service, http://java.sun.com/developer/technicalArticles/WebServices/fastWS/

    [15] Hongzhi Wang., Jianzhong Li., Jizhou Luo; Zhenying He., ” XCpaqs: compression of XML document with XPath query support, ” Information Technology: Coding and Computing, 2004. Proceedings. ITCC 2004. International Conference on Vol.1, No.5-7, Apr. 2004, pp.354-358.

    슭의 개발 블로그

    지난 4개월 동안 Go로 2개의 프로젝트를 진행하게 되었다.
    한 프로젝트는 비동기적인 웹 서버를 작성하는데, 실행파일이 네이티브 바이너리로 나올 필요가 있었다. C나 C++은 웹서버를 만드는 것도, 비동기적인 작업을 하는 것도 간단하게 되지 않기에 Rust와 Go 중에서 고민하다가 Go를 사용하기로 했다.
    다른 한 프로젝트는 큰 파일을 다운받아서 라인 파싱하고, rest API로 데이터를 요청하고, 그래프를 만들어 자료를 분석하여야 했다. 워낙 큰 파일을 파싱해야 해서 JavaScript는 사용하기 싫었고, 며칠을 밤낮으로 돌아가야 하는 코드인데 별도의 검증을 할 수 없는 Python으로는 실행 중 버그가 발생했을 때 손실되는 시간이 크기 때문에 사용하기는 싫어서 바이너리의 단점 Java, Scala, Go 중에서 고민하다가 Go를 쓰기로 했다.
    이번 글에서는 Go를 사용하면서 느꼈던 장단점에 대해서 간단히 써보도록 하겠다.

    학습 속도가 빠르다.

    Go는 공식 튜토리얼만 읽으면 다른 사람이 쓴 코드를 읽고 쓰는데 아무런 문제가 없다. 물론 Go의 튜토리얼 문서가 잘 돼 있기는 하지만, 다른 언어에 비해 크게 뛰어난 것은 아니다. 그냥 Go에는 프로그램을 작성하는데 필수적인 최소 기능들만 들어가 있기 때문이다.

    Channel 기반 언어

    Go는 대표적인 채널 기반 언어이다. 스레드라는 것을 명시적으로 주지 않고 goroutine을 생성하면 알아서 스레드를 생성해주고 적절한 스레드에 goroutine을 할당한다. goroutine 사이의 커뮤니케이션을 전부 채널을 통해서 한다면, 귀찮은 동기화 문제를 신경 쓰지 않아도 된다.

    네이티브 바이너리가 나온다.

    결과를 배포하는 입장에서 네이티브 바이너리가 나온다는 것은 매우 큰 장점이다. 요즘은 대부분 서버에 파이썬이나 JVM이 설치되어 있지만, 역시 배포는 네이티브 바이너리로 하는 것이 가장 편하다.

    컨벤션이 통일되어 있다.

    Go는 컨벤션에 대한 논쟁이 전혀 없다. go에는 컨벤션에 맞춰서 코드를 수정해주는 go fmt라는 기능이 내장되어 있다. 개발자는 그저 go fmt을 돌리기만 하면 되기 때문에, 별도로 스타일 논쟁이 발생할 일이 없다. 코드를 올리기 전에 go fmt을 돌리는 것조차 귀찮은 사람을 위해서 vim-go 같은 플러그인이 있어서 개발자는 스타일을 신경 쓰지 않고 코드를 작성하기만 해도 컨벤션에 맞출 수 있다.

    인터페이스 기반의 폴리모피즘

    Go에서 폴리모픽한 코드를 작성하는 유일한 방법은 인터페이스를 인자로 넘기는 것이다. 이 덕분에 인터페이스 단위의 추상화가 자연스럽게 이루어진다.

    verbose 한 코드

    Go는 템플릿도 매크로도 없다. 이는 Go의 코드를 단순하게 만들어주는 장점도 있다. 하지만 템플릿도 매크로도 없어서 단순한 코드가 반복해서 등장한다.

    type introspection

    모든 폴리모피즘이 인터페이스를 기반으로 돌아가기 때문에 구체적인 구조체 타입별로 다른 동작을 하기 위해서는 type introspection을 통해서 타입을 체크해야한다. 그래서 Go는 정적 타입 언어임에도 불구하고 아래처럼 동적 언어에서나 볼법한 코드가 나온다.

    interface<>

    게다가 표준 라이브러리는 interface<>의 사용을 적극적으로 권장한다. 예를 들어 list 같은 자료 구조의 경우 템플릿이 존재하지 않기 때문에 특정 타입의 값을 담는 자료구조를 만들 수 없어서 interface<> 타입의 값을 추가하고 interface<> 타입의 값을 리턴하는 자료구조를 만들 수밖에 없다. 이러한 자료구조 때문에 type introspection을 자주 사용할 수밖에 없고, 동적 타입 언어에서나 볼법한 코드가 나오게 된다.

    어설픈 최적화

    Go는 함수를 inline 시키는 최적화를 하지 않는다. 다른 언어라면 별문제 없을 수도 있다. 하지만 Go는 함수를 사용할 일이 많다.
    예를 들면 자원 관리나, recover 등을 위해서 defer를 사용할 일이 많은데 defer는 함수 단위로 호출된다. 따라서 특정 시점에서 자원을 해제시키고 싶다면 새로운 함수를 만들어 새 안에서 defer 함수를 호출해야 한다. 이 모든 함수가 인라이닝 되지 않기 때문에 실제로 스택 프레임을 변경시켜가며 함수를 호출했다 해제하는 일을 한다.

    라이브러리 버전 관리

    Go는 언제나 하나의 실행파일을 컴파일한다. 정적 라이브러리나 동적 라이브러리를 배포할 수 없고, 소스를 가져와서 컴파일하는 방식을 사용한다. 이는 딱히 나쁜 방식이 아니다. 현대의 패키지 매니저 중 많은 것들이 이런 방식을 취한다.
    문제는 Go에는 라이브러리의 버전을 명시할 방법이 없다는 것이다. 만약 사용하고 있던 라이브러리가 버전업을 해서 인터페이스가 변경되거나 버그가 발생하거나 해서 옛 버전을 사용해야 할 경우 문제가 되는 라이브러리뿐 아니라 모든 라이브러리의 버전업을 할 수 없다.

    변화에 민감한 스타일과 문법

    Go는 컨벤션이 정해져 있는 것이 장점이라고 위에서 말했다. 컨벤션이 정해져 있다는 사실은 꽤 편하다. 하지만 몇몇 부분에서 잘못된 스타일을 사용한다.
    예를 들면 Go는 필드의 타입 부분을 인덴트를 맞춘다. 따라서 아래와 같이 짧은 이름의 필드만 있던 구조체에 긴 이름의 필드가 추가되면 긴 이름에 맞춰서인덴트를 바꾼다. 이런 컨벤션은 보기 좋아 보일 수도 있다. 하지만 이런 스타일은 이력을 더럽히기 때문에 좋지 않다. 위와 같은 변경이 있으면 실제로는 필드가 한 개 추가된 것이지만, 버전 관리 도구에서는 한 개의 필드를 지우고 2개의 필드를 추가한 것처럼 나오게 된다. 지난번 포스트에서도 말했듯이 코드의 스타일은 사용하는 도구를 따라가야 하고, 개발에서 버전 관리 도구는 때놓을 수 없으므로 버전 관리 도구에 친화적인 스타일을 사용하는 것은 중요하다.

    Go는 장점이 분명하다. 러닝 커브가 완만하므로 팀원에 누가 들어오더라도 쉽게 적응할 수 있다. 하지만 이 점을 제외하면 단점이 장점보다 많다. 다만, 지금까지 썼던 네이티브 바이너리가 나오는 언어 중에서 가장 학습하는데 걸리는 시간이 짧았다는 점은 중요하다고 생각한다. 네이티브 바이너리가 나오는 것이 가장 중요한 요구 사항이라면 Go를 고려해볼 만 하겠지만, 그 외의 상황에서 Go를 사용하지는 않을 것 같다.

    • 공유 링크 만들기
    • Facebook
    • Twitter
    • Pinterest
    • 이메일
    • 기타 앱

    장단점을 잘 정리해주신, 진짜 제대로 사용하고 쓴 글로 느껴지네요.
    잘 읽었습니다. 많은 참고가 되었습니다.

    댓글 쓰기

    이 블로그의 인기 게시물

    [C++] enum class - 안전하고 쓰기 쉬운 enum

    C++ 03 까지의 enum 은 여러 가지 문제를 가지고 있었다. 그래서 그 문제들을 해결하기 위해 C++ 11은 enum class 라는 것을 새로 만들었다. 이제부터 기존의 enum 에 어떤 문제가 있었고, 이것을 enum class 에서 어떻게 해결하였는지 살펴볼 것이다. 우선 기존의 enum 은 전방 선언할 수 없었다. 그 이유는 enumerator에 어떤 값이 들어있을지 알 수 없으면 그 크기를 정할 수 없기 때문 이다. 하지만 enum class 는 underlying type을 명시하지 않으면 int 타입과 같은 크기의 변수로 선언되고, int 값 안에 들어가지 못할 값을 지정하면 컴파일 에러를 발생시킨다. 만약 int 를 벗어난 범위의 값을 사용하고 싶다면, underlying type을 명시해주어야 한다. 기존 enum 의 또 다른 문제는 enumerator의 이름의 범위가 한정되지 않는다는 것이다. 예를 들어 아래와 같은 코드를 보자. IO 함수의 결과와 Parse 함수의 결과를 enum 으로 표현해 보았다. 하지만 이 코드는 컴파일되지 않는다. IOResult 의 Error , Ok 가 ParseResult 의 Error , Ok 와 겹치기 때문이다. 이를 해결하기 위해서는 다음과 같이 enumerator의 이름을 다르게 하거나 아래와 같이 namespace를 이용해야 했다. 하지만 enum class 는 enumerator의 이름이 enum class 안으로 한정되기 때문에 이런 복잡한 과정이 필요 없이 그저 enum class 를 선언하여 사용하면 된다. 무엇보다 기존 enum 의 가장 큰 문제는 정수형 변수로 암시적으로 변환되는 약 타입(weak type) 변수라는 것이다. 하지만 enum class 는 정수형 변수로 암시적 변환이 되지 않는다. enum class 를 정수형 변수처럼 사용하려고 하면 컴파일 에러를 발생시킨다. 만약 정수형 변수로 사용하고 싶으면 static_cas

    [MongoDB] ObjectId에 대해서

    지난번에 shard key를 설명한 글 을 썼을 때 댓글 로 ObjectId 에 관한 얘기가 나왔었다. 그래서 sharding과 큰 연관은 없지만, 이번 기회에 ObjectId에 관해서 먼저 설명하고 가는 게 좋을 것 같아서 sharding에 관한 것은 뒤로 미루고 이번에는 ObjectId에 대해 먼저 설명하고 가도록 하겠다. ObjectId란 무엇인가 ObjectId는 같은 document 내에서 유일함이 보장되는 12 byte binary data다. 전통적인 centralized 되어 있는 시스템이라면 한 collection 내에서 유일함을 보장하는 것을 쉽게 할 수 있다. 하지만 sharding을 하는 MongoDB에서 유일함을 보장하는 것은 기존과는 다른 솔루션이 필요하다. 그리고 이 방법을 설명하는 게 사실상 ObjectId의 모든 것을 설명하는 것이다. 왜 ObjectId를 사용하는가 전통적인 RDBMS 에서 Primary key 를 만들 때는 DB 서버로 data를 보내서 중복되지 않는 key를 골라서 1) 그 값을 key로 저장하는 방식을 이용한다. 하지만 MongoDB와 같은 분산 database에서는 key를 서버에서 만들지 않고 클라이언트에서 만든다. 그 이유는 MongoDB가 query를 날릴 shard를 결정하는 방식 을 보면 알 수 있다. MongoDB는 자신이 필요한 shard에게만 query를 요청한다. 다시 말해서 client에 해당하는 mongos 2) 가 config server 의 data를 토대로 어떤 shard가 어느 범위의 값을 가졌는지를 저장하고 있다가 query를 요청할 때 자신이 필요로 하는 shard에게만 요청한다. 따라서 shard key 에 해당하는 data가 미완성인 상태로 서버에 저장하도록 요청할 수 없고, client가 ObjectId를 생성하여 값을 저장하도록 요청한다. 3) ObjectId의 구성 ObjectId는 크게 4부분으로 구성되어 있다. Ob

    RAII는 무엇인가

    RAII 는 C++에서 자주 쓰이는 idiom으로 자원의 안전한 사용을 위해 객체가 쓰이는 스코프를 벗어나면 자원을 해제해주는 기법이다. C++에서 heap에 할당된 자원은 명시적으로 해제하지 않으면 해제되지 바이너리의 단점 않지만, stack에 할당된 자원은 자신의 scope가 끝나면 메모리가 해제되며 destructor가 불린다는 원리를 이용한 것이다. 원래는 exception 등으로 control flow가 예상치 못하게 변경될 때를 대비하기 위해서 쓰이던 기법이다. 아래의 예제를 보자. 첫 번째 코드는 위험하다. thisFunctionCanThrowException() 함수에서 exception을 발생시킨다면 resource를 해제하지 못한다. 두 번째 코드는 일단은 resource를 해제하고 있다. 하지만 보기에 좋은 코드는 아니다. 그리고 유지하기도 어려워진다. 세 번째 코드는 RAII를 위해 c++ 11의 스마트 포인터인 unique_ptr을 이용하는 방법이다. unique_ptr은 소멸할 때 자신이 들고 있는 메모리를 해제시켜주기 때문에 함수 밖으로 나가면 resource가 해제되는 것이 보장된다. 여기서 말하는 자원은 단순히 heap 메모리만을 말하는 것이 아니다. heap 메모리 이외에도 파일이나 db와 같은 것들도 전부 RAII를 이용해 안전하게 바이너리의 단점 사용할 수 있다. 여기에 더 나아가서 특정 scope를 벗어나면 반드시 실행돼야 하는 코드들도 RAII를 이용해 처리할 수 있다. 즉, 다른 언어에서 finally에 해당하는 구문을 RAII를 이용해서 처리할 수 있다. 실제로 C++의 아버지이자 RAII라는 용어를 처음 만든 Bjarne Stroustrub는 c++에 finally를 집어넣지 않는 이유를 "RAII가 있는데 굳이 있을 필요가 없다." 라고 말하고 있다.


0 개 댓글

답장을 남겨주세요