ChakraCore를 JSON 디코더 및 인코더로 활용하기
고남현 @gnh1201@hackers.pub
기존 프로그래밍 언어와 JSON 형식 사이의 부자연성
JSON(JavaScript Object Notation)는 프로그래밍 언어를 가리지 않고 범용적으로 쓰이는 데이터 교환 형식이지만, JavaScript (ECMAScript) 엔진을 제외하고는 대부분의 프로그래밍 언어는 처음 설계했을 때 JSON 형식의 등장을 미리 예견하고 만든 것이 아니기 때문에, JSON을 지원하는 방법에 있어 완전히 해소하지 못하는 고유한 부자연성을 보인다.
그럼에도 불구하고 JSON이 프로그래밍 언어를 가리지 않고 널리 쓰이는 이유는, 고유의 부자연성을 완전히 해소하지는 못하더라도 최대한 감소시키는 노력이 반영된 우수한 라이브러리들, 또는 아예 각 프로그래밍 언어의 공식 런타임 자체에서 지원하고자 하는 노력이 반영된 우수한 결과물들이 이미 나와있기 때문이라 할 수 있다.
본 글에선 이러한 부자연성을 해결하기 위해 내가 했던 고민을 다루고 있다. 본 글에서는 특정 플랫폼 사례만 다루지만, 다른 환경에서도 비슷한 관점으로 적용할 수 있을 것이다.
.NET 환경에서의 JSON 입출력 고민
Microsoft의 .NET 프로그래밍 환경(C# 또는 .NET 런타임을 지원하는 언어)에서의 JSON 지원은 보통 아래와 같은 표준 라이브러리가 쓰인다.
- System.Text.Json
- System.Text.Json.Serialization
- Newtonsoft.Json
하지만 이번 프로젝트에서는 이러한 표준 라이브러리를 쓰지 않고 JSON을 입출력할 수 있는 합리적인 방법을 제시할 것이 요구되었다.
이러한 결정에는 다음과 같은 고민이 있었다.
- JSON 입출력을 지원하기 위한 문자열 처리, 마샬링 과정(리플렉션 등) 등 내부 구현을 위해 할당해야할 공간이 생각보다 크다. (별도로 로드해야 할 바이너리가 많아진다.) 이정도의 요구 공간이면 JavaScript 문법 처리에 있어 안정성이 검증된 임베디드 환경을 위한 JavaScript 엔진을 통채로 넣고도 오히려 기존보다 공간이 남거나 비슷할 수 있다.
- 공간 효율성을 높이기 위해 JSON을 입출력할 수 있는 공개된 간단한 구현을 적용하거나, 아니면 JSON 파서 및 직렬화 과정을 직접 구현하는 경우 JSON 처리에 있어서 신뢰성을 확보하기 어렵다.
- 현 프로젝트의 언어는 JavaScript가 아니지만, JSON 포맷은 JavaScript 문법을 기초로 하는 만큼 JSON 포맷의 처리 및 접근 방법에 있어 최대한 JavaScript 스타일을 유지하고 싶다. 표준 라이브러리는 확실히 JavaScript 스타일은 아니다.
- JavaScript 문법을 기반으로 하거나, JavaScript 문법에 영향을 받은 여러 종류의 요구사항(JSON도 그 중 하나)에 대한 잠재적 중복 구현 제거 효과를 기대할 수 있는 방식이 필요하다.
이러한 고민을 해결하기 위해 ChakraCore + 네이티브 호출(P/Invoke)을 적용한다는 결론에 도달하였다.
JSON 파싱에 ChakraCore 엔진 적용
이러한 고민은 결국 ChakraCore 엔진을 활용하여 JSON 입출력 클래스를 작성하는 결정에 도달하기에 이른다.
실제 연동이 어떻게 구현되었는지는 JsSerializer.cs (WelsonJS.Toolkit)에서 확인할 수 있다.
구현이 완료되고 나면 다음과 같이 JSON 입출력을 수행할 수 있다.
// 직렬화(인코딩)
using (var ser = new JsSerializer())
{
var doc = new Dictionary<string, object>
{
["ok"] = true,
["hex"] = hex,
["name"] = name, // may be null → serializer will emit "name": null
["rgb"] = new Dictionary<string, object>
{
["r"] = color.R,
["g"] = color.G,
["b"] = color.B
}
};
string json = ser.Serialize(doc, 0);
Console.WriteLine(json);
}
// 파싱(디코딩)
using (var ser = new JsSerializer())
{
string imageJson, xJson, yJson;
using (var ser = new JsSerializer())
{
int id = ser.Load(body);
// Required: "image" as JSON string
imageJson = ser.ExtractFrom(id, "image");
// Required: "point": { "x": <num>, "y": <num> }
xJson = ser.ExtractFrom(id, "point", "x");
yJson = ser.ExtractFrom(id, "point", "y");
Console.WriteLine(xJson);
Console.WriteLine(yJson);
ser.Unload(id);
}
}
기존 .NET 프로그래밍 환경에서 XML 포맷을 처리하는 방식과도 유사한 형태가 되었으며, JavaScript 스타일의 JSON 입출력 방법과도 가까워진 모습을 볼 수 있다.
다른 환경에서의 적용
다른 프로그래밍 환경에서 JSON 포맷과 관련된 유사한 고민이 있는 경우, 비슷한 방법을 사용해서 JSON 입출력 처리를 진행해볼 수 있을 것이다.
ChakraCore는 무엇인가?
ChakraCore는 Microsoft가 개발하고 오픈소스로 배포한 JavaScript 엔진으로, 당초 Microsoft의 차기 웹 브라우저(Microsoft Edge의 초기 버전)를 위해 개발되었으나, 자체 웹 브라우저 개발을 중단한 이후로는 최신 문법을 지원하는 경량 엔진이라는 특징을 살려 임베디드 사용 사례에 주력하고 있다.
현재 Microsoft는 ChakraCore의 공식 바이너리를 배포하지 않는다. ChakraCore를 오픈소스로 전환하면서 여전히 Microsoft가 주요 Maintainer이기는 하지만, 배포를 포함한 대부분의 결정은 오픈소스 커뮤니티의 자율성에 맡기는 것으로 보인다. ChakraCore의 바이너리가 필요한 경우 아래 링크를 참고해보자.