Bencodex

Moreal·2024년 11월 17일

들어가며

안녕하세요, 이 글에서는 Bencodex라는 직렬화 포맷이 무엇이고, 어떤 장점이 있는지 설명하려고 해요.

Bencodex란

Bencode의 확장 포맷

Bencodex를 설명하기 위해서는 Bencode라는 포맷에 대해 설명해야 해요. Bencode는 BitTorrent, 흔히 토렌트라고 아는 P2P 파일 공유 프로그램에서 사용되는 직렬화 포맷이에요. .torrent 확장자를 가진 메타데이터 파일의 내용이 Bencode 형식으로 저장되어 있어요.

Bencode는 바이너리, 정수, 리스트, 딕셔너리 같은 타입들을 지원해요. Python 타입으로 표현하면 bytes, int, list, dict로 치환이 가능해요.

그리고 흔히 사용되는 null (None) 이나 유니코드 문자열(str), 그리고 불리언 값(True, False)은 지원하지 않아요. BitTorrent 구현을 위해 만들어진 직렬화 포맷이기에 필요한 타입들만 지원했을 것 같아요. 그리고 여러 언어에서 구현되기를 기대하며 간단한 스펙으로 만든 것도 있을 것 같아요. 하지만 그 중 불리언 타입은 시대적 배경에도 이유가 있는 것 같아요. BitTorrent의 첫 릴리스는 2001년이었고 Python으로 작성되었어요. 그리고 그 때는 Python 3가 릴리스된 2008년 보다도 7년 전인, Python 2가 사용될 시기고. 불리언 타입은 2002년에 PEP 285를 통해 Python 2에 추가 되었어요, 이는 BitTorrent가 릴리스 되고 1년 뒤의 일이기에 Bencode 스펙에 포함되긴 어려웠을 것 같아요.

하지만 현대적인 언어에서는 Bencode에서 지원하지 않는 타입들이 정말 많이 사용되고 있어요. 그래서 Bencodex는 이러한 미지원 타입들을 추가적으로 지원하면서도 Bencode의 장점을 그대로 살리는 것을 목표로 하고 있어요.

하나의 값, 하나의 표현

Bencode 및 Bencodex의 장점 혹은 특징은 값과 표현이 일대일 대응 한다는 점이에요.

다음과 같은 JSON 값이 있다고 해볼게요.

{
  "name": "name",
  "age": 1
}

JSON 에서는 위 값을 아래와 같이도 표현할 수 있어요.

{
  "age": 1,
  "name": "name"
}

분명 같은 값을 의미하지만, 직렬화된 결과는 서로 달라요. 이렇게 여러 결과가 존재할 수 있게 되어버리면, 해시를 사용하여 값을 비교하고자 할 때 어려움이 생기게 되요. 예를 들어 블록체인처럼 해시 및 서명을 활용하는 곳에서는 해시가 달라지면 안 되기에 더욱 중요한 부분이에요.

반면 Bencodex는 하나의 값에, 하나의 직렬화 결과만 존재해요. 이를 위해 직렬화 포맷 명세에서 여러가지 제약을 걸고 있어요. 예를 들어 아래와 같은 것들이에요.

  • 정수는 항상 필요한 값만 있어야 해요. 01, -01, -0 같은 값은 허용되지 않아요. 1, -1, 0 으로만 표현되어야 해요.
  • 딕셔너리에서는 키 기준으로 정렬되어 있어야 해요. 바이너리와 유니코드 문자열을 딕셔너리의 키로 받을 수 있는데, 둘이 혼재한다면 바이너리 키들이 먼저 앞에 와야해요.

블록체인 게임 네트워크를 만들 수 있도록 도와주는 라이브러리인 Libplanet에서는 이러한 장점을 활용하여 기능들을 구현하고 있어요.

구현체들

아래와 같은 언어 별 Bencodex 구현체들이 있어요.

마무리

Bencodex의 장점으로 일대일 대응을 이야기 했지만, 해시를 사용할 때 일대일 대응이 되지 않는 문제는 꼭 Bencodex를 사용하지 않더라도, 별도의 정렬 규칙을 정하거나, JSON Canonicalization Scheme 같은 RFC 구현체를 활용하여 해결할 수도 있어요.

자료들을 첨부하며 찾아보고 체크하고자 노력했지만, 잘못된 내용이 있을 수 있어요. 잘못된 내용이 있다면 댓글로 알려주세요.

0개의 댓글