최근 새로운 자바스크립트 서버 사이드 런타임인 Bun이 발표되었습니다. Bun은 Node.js에 비해 엄청난 속도 향상을 약속했습니다. 이 글에서는 작은 애플리케이션 대신 복잡한 애플리케이션을 사용해 실제 성능을 확인해봤습니다.
Bun 웹사이트(https://bun.sh/)를 보면 Node.js 사용을 대체하는 흥미로운 대안처럼 보이게 합니다. Bun은 Node.js와 같은 아이디어이지만, 훨씬 더 나은 성능을 약속합니다. Node.js와 마찬가지로, Bun은 웹 브라우저에서 자바스크립트 엔진을 서버 사이드 자바스크립트 플랫폼으로 패키징하고 완전한 호환성을 위해 Node.js API를 구현할 것을 약속합니다.
크롬의 V8 엔진을 사용하는 대신 애플의 사파리 브라우저의 자바스크립트코어(JavaScriptCore) 엔진을 사용합니다. 이 엔진은 V8 보다 더 빠르다는 평가를 받고 있으며, 이 부분이 첫 번째 장점이라고 주장되고 있습니다. 또 다른 장점으로는 Bun 시스템이 C++나 Rust가 아닌 새로운 언어인 Zig으로 작성된다는 것입니다. Zig는 몇몇 기술적 사양 덕분에 더 빠를 것으로 예상합니다.
또 다른 흥미로운 지점은 타입스크립트와 JSX 트랜스파일러가 Bun에 내장되어 있어 타입스크립트 코드를 매우 쉽게 실행할 수 있다는 점입니다.
Bun팀은 N-API를 사용해 네이티브 코드 Node.js 패키지 실행을 지원하는 것을 포함해 Node.js 패키지와 90% 호환을 주장합니다. 또한, Node.js와 동일한 node_modules
인프라와 패키지 검색 알고리즘을 사용하므로 기존 Node.js 생태계와 즉시 호환됩니다.
대조적으로 (Node.js의 대안으로 불리는)Deno는 Node.js 패키지 생태계와 호환되지 않으므로 큰 단점이 있습니다.
Node.js 개발자들은 기존 생태계와 호환되지만, 훨씬 더 빠른 이 대안을 가지고 무엇을 할 수 있을까요?
이미 유튜브에는 Bun을 처음 시도하는 여러 영상이 있습니다. 제가 본 모든 비디오는 몇 가지 간단한 명령을 실행하는 것을 보여줍니다. 그러고는 "맙소사. 정말 빠르군요!" 라고 합니다.
잘 알려진 지나치게 단순한 성능 테스트의 오류가 있습니다. Bun으로 간단한 스크립트를 실행하는 것이 실제 애플리케이션에서 Node.js보다 매우 빠르다는 것을 의미하나요? 그것이 오류입니다. Bun이 실제로 빠르다는 것을 확인하기 위해서는 몇 가지 간단한 예시보다 더 심층적인 테스트가 필요합니다.
제 생각에는 호환성과 성능을 모두 테스트하기 위해 복잡한 케이스에 Bun을 사용하는 것이 더 좋아 보입니다.
저는 이미 여러 웹 사이트를 구축한 정적 웹사이트 생성기(AkashaCMS)를 개발했습니다. 몇몇 웹사이트는 꽤 큽니다. 예를 들어 techsparx.com
는 1600개가 넘는 웹 페이지 즉, 블로그 게시물이 있습니다. AkashaCMS는 여러 템플릿 엔진을 지원하며 서버 사이드 jQuery 같은 DOM 조작 및 기타 여러 가지 작업을 수행합니다.
제 노트북(Core i7 및 16GB 메모리가 탑재된 Dell Latitude E7250)으로 Node.js를 실행하면 배포될 준비가 된 HTML로 웹 사이트를 렌더링하는데 30분 정도 걸립니다. Bun이 주장한 대로라면 10분이면 되어야겠죠?
확인해야 할 두 가지 중요한 시나리오가 있습니다.
- Bun이 AkashaCMS를 실행할 수 있나요?
techsparx.com
에 있는 모든 것을 렌더링할 수 있나요? - Bun은 Node.js를 사용할 때보다 더 빨리 웹사이트를 렌더링할 수 있나요?
제 첫번째 테스트는 AkashaCMS 자체를 실행하는 것이 아닌, Node.js 패키지와 관련된 테스트 케이스를 실행하는 것이었습니다. 저는 Mocha를 사용해 테스트 케이스를 구축했는데, 아쉽게도 Bun은 일부 코드와 호환되지 않았습니다. 일부 패키지는 호환성을 확인하기 위해 process
객체에서 값을 찾고 있습니다. Bun은 예상 값을 제공하지 않았고 Mocha가 사용한 일부 패키지는 다양한 호환성 테스트에서 충돌했습니다. 저는 Bun 이슈 리스트에 이 내용을 보고 했고, 이미 해결이 진행중인 것 같습니다.
또 다른 이슈는 Bun이 Github에 존재한 패키지에 대해 package.json
을 지원하지 않는다는 것입니다. 저는 npm에 굳이 배포할 필요가 없다고 생각되는 패키지를 Github 의존에 등록합니다. 이 이슈 또한 Bun에 이미 리포팅되어 있습니다. 저처럼 npm 배포하지 않고 Github을 직접 로드하는 경우에는 사용하지 못합니다.
Bun을 통해 npm을 사용해 패키지를 설치하고 코드를 실행하는 것이 완벽하게 동작한다는 것은 확인했습니다. 뭐가 됐든간에 Bun은 아직 "beta" 소프트웨어입니다..
첫 번째 테스트 시나리오에서 Bun은 techsparx.com
웹사이트를 거의 문제없이 렌더링 했습니다.
이 웹사이트는 몇몇 Github 의존을 사용합니다. 즉 npm install
을 통해 의존을 다운로드 한다는 것 입니다.
일반적으로 이 경우 저는 빌드 프로세스에서도 사용하기 위해 package.json
의 scripts
를 사용합니다. 이 방법은 과정을 기록하기에 편리한 방법이며, 이전 글에서 논의한 내용은 다음과 같습니다. (어떻게 npm/yarn/Node.js package.json를 빌드 도구로 사용할 수 있을까요?)
Bun은 package.json
의 빌드 스크립트를 직접 실행할 수 있지만, 아래 스크립트는 akasharender
명령을 실행하며, 이 명령은 node 명령을 사용하여 실행되는 스크립트 입니다. 저는 대신 이 명령을 손으로 실행했습니다.
$ bun run node_modules/akasharender/cli.js -- copy-assets config.js
$ bun run node_modules/akasharender/cli.js -- render config.js
이렇게 하면 cli.js
를 직접 실행할 수 있습니다 --
는 명령 매개변수가 Bun에 의해 수정되지 않고 akasharender
에 전달되도록 합니다. 첫 번째 명령은 렌더링된 출력 디렉터리에 asset 파일을 복사하고, 두 번째 명령은 웹 페이지 및 기타 파일을 해당 디렉터리에 렌더링합니다.
Node.js에서 실행 동사는 명령에 필요하지 않으며 --
도 필요하지 않습니다. 그렇지 않으면 동일한 명령어입니다.
첫 번째 시도는 훌륭하게 통과되었습니다. AkashaCMS를 올바르게 실행하고 techsparx.com
웹사이트를 렌더링했습니다.
제가 보여드릴 성능 수치를 이해하려면 AkashaCMS에 대해 이해하는 것이 조금은 도움이 될 수 있습니다.
AkashaCMS 프로젝트는 네 가지 종류의 입력 디렉터리, asset, 문서 및 두 가지 종류의 템플릿 디렉터리를 갖고 있습니다. asset 디렉터리의 파일은 렌더링된 출력 디렉터리에 복사되는 반면 문서 디렉터리의 파일은 렌더링 프로세스를 통해 실행됩니다. 생성된 출력 디렉터리에는 웹 사이트 작성자가 필요한 HTML/CSS/JS/Image 파일의 혼합된 결과가 포함됩니다.
따라서 copy-assets
은 다음과 같은 단계를 따릅니다.
- asset 디렉터리에서 파일 찾기
- 효율적인 파일 복사 작업 사용(
fs-extra
패키지의fs.copy
)
파일 복사 루프는 fastq
패키지를 사용해 최대 10개의 동시 복사 작업을 실행합니다. 출력 디렉터리에 이미 있는 파일 복사는 건너뛰고 시도하지 않습니다.
이와 대조적으로 render
명령은 렌더링 코드, 템플릿 엔진 등 여러 작업을 실행합니다.
테스트 환경은 5세대 Core i5, 16GB 메모리 및 HDD에 저장된 파일을 갖춘 Intel NUC입니다.
측정은 time
명령을 사용해 수행했습니다. 아래 열은 실제 소요된 시간을 의미하는 real, 사용자 모드 코드의 CPU 소비를 의미하는 사용자 user, 커널 코드의 CPU 소비를 의미하는 sys가 있습니다.
아래 결과는 Bun 0.1.2 버전을 사용해 총 6번의 copy-assets
를 수행했습니다.
real 0m7.326s user 0m5.241s sys 0m1.319s
real 0m4.445s user 0m5.277s sys 0m1.101s
real 0m4.847s user 0m5.346s sys 0m1.137s
real 0m4.874s user 0m5.345s sys 0m1.217s
real 0m4.847s user 0m5.420s sys 0m1.128s
real 0m4.867s user 0m5.472s sys 0m1.167s
그리고 Node.js 18.5.0 버전을 사용해 6번의 copy-assets
를 한 결과를 보겠습니다.
real 0m4.686s user 0m5.105s sys 0m1.279s
real 0m4.549s user 0m5.160s sys 0m1.224s
real 0m4.659s user 0m5.246s sys 0m1.309s
real 0m4.884s user 0m5.127s sys 0m1.285s
real 0m4.658s user 0m5.316s sys 0m1.148s
real 0m4.968s user 0m5.174s sys 0m1.292s
다른 말로 하면, 결과는 거의 같습니다.
techsparx.com
에서는 2000개 이상의 파일이 복사됩니다.
우리는 방금 파일 복사가 많은 테스트에서 Bun이 Node.js에 비해 더 좋은 결과를 보여주지 못한 것을 증명했습니다. 다음 단계는 마크다운 파일을 HTML로 렌더링한 다음 HTML에서 템플릿을 처리하는 더 복잡한 작업을 수행하는 방법을 확인할 것 입니다.
이 경우 사용되는 기본 패키지는 다음과 같습니다.
- 마크다운은 markdown-it v12.x 를 사용해 렌더링합니다.
- 서버 사이드 DOM 처리는 Cheerio v1.0.0-rc.10를 사용했습니다.
- EJS (v3.1.x)와 Nunjucks (v3.2.x)를 혼합해 템플릿 처리를 수행합니다.
copy-assets
명령어와 마찬가지로, fastq
는 한 번에 여러 렌더링을 병렬로 실행하는 데 사용됩니다.
Bun 0.1.2 버전을 사용해 techsparx.com
를 렌더링한 결과는 다음과 같습니다.
real 26m1.263s user 25m20.973s sys 0m27.235s
real 25m50.435s user 25m40.301s sys 0m24.955s
real 25m53.489s user 25m58.259s sys 0m25.791s
Node.js 18.5.0 버전을 사용해 techsparx.com
를 렌더링한 결과는 다음과 같습니다.
real 25m46.665s user 25m42.305s sys 0m26.839s
real 26m6.209s user 27m38.675s sys 0m25.880s
real 25m42.731s user 27m29.677s sys 0m25.977s
copy-assets
과 비슷하게 두 플랫폼 모두 거의 동일한 시간이 소요되었습니다.
이 워크로드(workload)의 경우 Bun은 Node.js와 거의 동일한 성능을 갖고 있습니다.
Bun 팀은 성능에 대해 큰 주장을 했기 때문에, 우리는 무슨 일이 일어나고 있는지 고려해야 합니다. Bun이 아직 Beta이고 버그가 한두개 있을 수 있다는 인식을 넘어서서 말입니다.
물론, 이것이 최고의 성능 테스트는 아닙니다. 거의 20년 전 저는 Sun Microsystems의 Java SE팀에서 일했고, 성능 향상을 위한 업무를 수행하는 그룹과 시간을 보냈습니다. 그들은 특정 워크로드에 대한 긴 테스트 목록을 갖고 있으며, 각 테스트는 하나의 특정 성능 측정에 초점을 맞췄습니다. 이런 식으로 팀은 릴리즈 N이 문자열 성능 n%
를 향상했다고 말할 수 있습니다.
AkashaCMS 같은 애플리케이션을 실행하는 것은 플랫폼의 상당한 부분을 수행하지만, 깔끔한 집중 시나리오 테스트가 아닙니다. 오늘날 유튜브에서 발견되는 지나치게 단순한 Bun 시연은 우리가 Bun의 성과를 이해하는 데 도움이 되지 않듯이, 이 테스트 또한 그렇습니다. Node.js 팀 내에 벤치마킹 팀이 있었지만, 작업 공간은 수년간 유휴 상태였습니다. 하지만, 저는 그들이 성능을 측정하기 위해 정확한 테스트 집합을 개발했다고 생각합니다.
저는 Node.js에서 일련의 벤치마크를 수행하는 것으로 보이는 패키지(https://github.com/majimboo/node-benchmarks)를 발견했습니다. Node.js 18.5.0 버전을 사용해 이 패키지를 설치하려고 했지만 microtime
패키지를 설치하지 못했습니다. 해당 패키지의 package.json
의 microtime
버전을 3.1.x 버전을 사용하면 테스트를 실행할 수 있지만, 결과를 어떻게 해석해야 할지 모르겠습니다. 어떤 경우든 해당 제품의 테스트는 이런 목적에 적합한 정렬입니다.
Bun이 Node.js보다 정말 빠른지, 어떤 기능 영역에서 Bun이 더 빠르거나 느린지 이해하려면 적절한 벤치마크 테스트 비교가 필요합니다. 두 플랫폼 모두 정확히 동일한 코드를 실행하는 것을 목표하므로 각각에 대해 동일한 벤치마크/성능 테스트를 실행해 상대적 성능을 측정할 수 있습니다.
라이언 달이 2009년 Node.js를 처음 출시했을 때, 그는 다른 플랫폼과 비교한 성능 벤치마크를 제시하며 Node.js를 전 세계에 팔았습니다. IIRC의 테스트는 메모리 풋프린트와 초당 HTTP 작업 수와 같은 유용한 지표에 초점을 맞췄습니다.
끝으로, 저는 Bun에 깊은 인상을 받았습니다. Node.js를 복제하는 것을 목표로 하는 새로 발표된 프로젝트이며, 이미 다소 복잡한 애플리케이션을 처리할 수 있기 때문입니다.