자바스크립트 도구를 “더 빠른” 언어로 다시 작성하는 것에 대해 회의적인 이유
저는 자바스크립트 코드를 많이 작성해 왔습니다. 그리고, 자바스크립트를 좋아합니다. 게다가 더 중요한 것은, 자바스크립트를 이해하고, 최적화하고, 디버깅하는 기술을 발전시켜 왔고 이를 포기하고 싶지 않습니다.
그래서 모든 Node.js 도구를 Rust, Zig, Go 등과 같은 “더 빠른” 언어로 다시 작성하려는 현재의 유행에 대해 걱정스러운 마음이 드는 것이 자연스러울지도 모릅니다. 오해하지 마세요. 이런 언어들은 멋집니다! (지금 제 책상에는 Rust 책 한 권이 있고, 재미로 Servo에 조금 기여하기도 했습니다) 하지만 결국, 저는 자바스크립트의 세세한 부분들을 배우는 데 제 경력의 많은 부분을 투자해 왔고, 자바스크립트는 제가 가장 편안하게 다루는 언어입니다.
그래서 저의 편견(그리고 아마도 한 가지 기술에 대한 과도한 투자)을 인정합니다. 하지만 생각해 볼수록, 제 회의적인 시각이 실제적이고 객관적인 우려들에 기반한다고 느꼈기에 때문에 이번 포스트에서 이를 다뤄보고자 합니다.
성능
제가 회의적인 이유 중 하나는 자바스크립트 도구를 더 빠르게 만들 모든 가능성을 다 탐구했다고 생각하지 않기 때문입니다. 마르빈 하게마이스터(Marvin Hagemeister)는 ESLint, Tailwind 등을 자바스크립트 자체만으로도 얼마나 많이 개선할 수 있는지 보여주며 이를 훌륭하게 증명했습니다.
브라우저 세계에서 자바스크립트는 대부분의 작업에 대해 “충분히 빠르다”는 것을 증명했습니다. 물론 WebAssembly가 존재하지만, 전체 웹사이트를 구축하기보다는 주로 틈새시장에 속하는 CPU 집약적인 작업에 사용된다고 말하는 것이 타당할 것입니다. 그렇다면 자바스크립트 기반 CLI 도구들이 왜 그렇게 서둘러 자바스크립트를 버리려고 하는 걸까요?
대규모 재작성
성능 격차는 몇 가지 다른 요인들에서 비롯된다고 생각합니다. 첫 번째로는 앞서 언급한 쉽게 개선할 수 있는 부분일 겁니다. 오랫동안 자바스크립트 도구 생태계는 빠른 것이 아닌 작동하는 것을 구축하는 데 초점을 맞춰왔습니다. 시간이 지나자 우리는 API 설계가 대체로 안정화된 성숙 단계에 도달했고, 모두가 그저 “같은 것이지만 더 빠른” 것을 원합니다. 따라서 기존 도구를 즉시 대체할 수 있는 새로운 도구들이 폭발적으로 등장했습니다. Rollup용 Rolldown, ESLint용 Oxlint, Prettier용 Biome 등이 이에 해당합니다.
하지만 이런 도구들이 반드시 더 빠른 언어를 사용하기에 반드시 더 빠른 것은 아닙니다. 그저 1) 성능에 유의하며 작성되었고, 2) API가 성숙 단계에 있어서 작성자들이 전체적인 설계를 고민하는 데 개발 시간을 쓸 필요가 없기 때문에 더 빠를 수 있습니다. 심지어 테스트를 작성할 필요도 없습니다! 이전 도구의 기존 테스트 스위트를 사용하면 됩니다.
제 경력에서, 저는 종종 A에서 B로의 재작성이 속도 향상을 가져오고, 이어서 B가 A보다 빠르다는 의기양양한 주장을 보아왔습니다. 하지만 라이언 카르니아토(Ryan Carniato)가 지적하듯이, 재작성이 종종 더 빠른 것은 그것이 재작성이기 때문입니다. 다시 만들 때는 첫 번째보다 더 많은 것을 알고 있고, 성능에 더 신경을 쓰기 때문입니다.
바이트코드와 JIT
두 번째 부류의 성능 격차는 브라우저가 자동으로 처리해 줘 우리가 거의 의식하지 않는 기능들에서 비롯됩니다. 바이트코드 캐시와 JIT(Just-In-Time 컴파일러)가 이에 해당합니다.
웹사이트를 두 번째나 세 번째로 로드할 때 자바스크립트가 올바르게 캐싱되어 있다면, 브라우저는 더 이상 소스 코드를 파싱하고 바이트코드로 컴파일할 필요가 없습니다. 그저 디스크에서 바이트코드를 직접 로드합니다. 이것이 바이트코드 캐시의 작동 방식입니다.
게다가, 함수가 “핫”(자주 실행됨)하다면, 기계어 코드로 더 최적화됩니다. 이것이 JIT의 작동 방식입니다.
Node.js 스크립트의 세계에서는 바이트코드 캐시의 혜택을 전혀 받지 못합니다. 노드 스크립트를 실행할 때마다, 전체 스크립트가 처음부터 파싱되고 컴파일되어야 합니다. 이것이 자바스크립트와 비자바스크립트 도구 간의 성능 차이가 큰 이유입니다.
하지만 뛰어난 Joyee Cheung 덕분에, Node에는 이제 컴파일 캐시 기능이 추가되었습니다. 환경 변수를 설정하면 즉시 더 빠른 Node.js 스크립트 로딩을 할 수 있게 되었습니다.
export NODE_COMPILE_CACHE=~/.cache/nodejs-compile-cache저는 제 모든 개발 장비의 ~/.bashrc에 이를 설정했습니다. 언젠가 기본 Node 설정이 되기를 희망합니다.
JIT의 경우, 이 기능은 (안타깝게도) 대부분의 Node 스크립트에서는 효과를 보기 어렵습니다. 함수가 “핫”이 되려면 여러 번 실행되어야 하는데, 서버 측에서는 일회성 스크립트보다는 장기간 실행되는 서버에서 JIT 최적화가 적용될 가능성이 높습니다.
그리고 JIT는 정말 놀라운 효과를 보여줄 수 있습니다! Pinafore에서 저는 자바스크립트 기반 blurhash 라이브러리를 Rust(Wasm) 버전으로 교체하는 것을 고려했습니다. 하지만 같은 함수를 다섯 번째 실행할 때쯤에는 두 버전 간의 성능 차이가 거의 사라진다는 것을 발견했습니다. 이것이 바로 JIT의 힘입니다.
어쩌면 결국 Porffor 같은 도구가 Node 스크립트의 AOT(Ahead-Of-Time) 컴파일에 사용될 수 있을지도 모릅니다. 하지만 그동안에는 JIT 환경에서 여전히 네이티브 언어가 자바스크립트에 대해 우위를 가질 것입니다.
또한 언급해야 할 점은 Wasm이 순수 네이티브 도구에 비해 성능 손실이 있다는 것입니다. 이것이 네이티브 도구들이 CLI 환경에서는 인기를 끌고 있지만, 브라우저 프런트엔드에서는 그렇지 않은 이유 중 하나일 수 있습니다.
기여와 디버깅 가능성
앞서 암시했듯, 이 주제가 “모든 것을 네이티브로 재작성하자” 운동에 회의를 갖게 된 주요 원인입니다.
자바스크립트는 제 생각에 서민적인 언어입니다. 타입에 대해 매우 관대하고(이것이 제가 타입스크립트의 열렬한 팬이 아닌 이유 중 하나입니다), (Rust 같은 것에 비해) 배우기 쉽고, 브라우저에서 지원하기 때문에 이를 다룰 수 있는 사람들의 범위가 매우 넓습니다.
수년간, 우리는 자바스크립트 생태계에서 라이브러리 작성자와 라이브러리 소비자 모두가 대부분 자바스크립트를 사용해 왔습니다. 저는 이것이 가능하게 만든 것들을 우리가 너무 당연하게 여긴다고 생각합니다.
첫째로 기여하기 훨씬 쉽습니다. Matteo Collina의 말을 인용하면
대부분의 개발자는 자신들이 의존성을 디버깅하고, 수정하고, 고칠 수 있는 기술을 가지고 있다는 사실을 무시합니다. 이런 라이브러리들은 신비로운 존재들이 만든 것이 아니라 본인들과 같은 개발자 동료들이 유지보수 하는 것입니다.
하지만 자바스크립트 라이브러리 작성자들이 자바스크립트가 아닌 다른 언어(더 어려운!)를 사용한다면 이런 접근성은 사라집니다. 그때는 일반 개발자들이 느끼기에 신비로운 존재들이 될 수도 있습니다!
둘째로, 자바스크립트 라이브러리는 로컬에서 쉽게 수정할 수 있습니다. 저는 사용 중인 라이브러리에서 버그를 찾거나 기능을 개발할 때 로컬 node_modules 폴더에서 직접 코드를 수정한 적이 많습니다. 하지만 네이티브 언어로 작성된 라이브러리라면 소스 코드를 내려받아서 직접 컴파일해야 하므로 진입 장벽이 훨씬 높습니다.
(물론 타입스크립트가 널리 사용되면서 이런 접근성이 다소 어려워진 것은 사실입니다. 하지만 타입스크립트는 원래 자바스크립트와 크게 다르지 않기 때문에 브라우저 개발자 도구에서 “pretty print” 기능만 사용해도 상당히 많은 것을 할 수 있습니다. 게다가 대부분의 Node 라이브러리는 코드가 압축되어 있지 않아서 읽기가 어렵지 않습니다.)
이와 연결되는 것이 디버깅의 편의성입니다. 자바스크립트 라이브러리를 디버그 할 때는 익숙한 브라우저 개발자 도구나 Node.js 디버거를 그대로 사용할 수 있습니다. 중단점을 설정하고 변수를 확인하며, 내 코드를 다루듯이 라이브러리 코드도 분석할 수 있습니다. Wasm에서도 디버깅이 아예 불가능한 것은 아니지만, 완전히 다른 기술과 도구가 필요합니다.
결론
자바스크립트 생태계를 위한 새로운 세대의 도구가 있다는 것은 훌륭하다고 생각합니다. Oxc와 VoidZero 같은 프로젝트들이 어디로 향하는지 보는 것이 기대됩니다. 기존의 기득권 도구들은 실제로 지나치게 느리며 아마 경쟁을 통해 개선 될 것입니다. (저는 특히 전형적인 eslint + prettier + tsc + rollup lint+build 사이클이 별로라고 생각합니다.)
그렇다고 해서, 자바스크립트가 본질적으로 느리거나 우리가 이를 개선할 모든 가능성을 다 탐구했다고 생각하지는 않습니다. 때때로 저는 Uint8Arrays를 비트 벡터로 사용하는 것과 같은 놀라운 기술을 사용한 Chromium 개발자 도구의 최근 개선 사항들과 같은 진정으로 성능에 초점을 맞춘 자바스크립트를 보면, 우리가 겨우 표면만 긁었다고 느낍니다. (정말로 열등감을 느끼고 싶다면, Seth Brenith의 다른 커밋을 보세요. 그것들은 와일드합니다.)
일반적인 자바스크립트 개발자는 빌드 도구에 문제가 생길 때마다 속수무책이 될 것입니다. 우리는 차세대 웹 개발자들이 더 많은 일을 할 수 있도록 돕는 대신, 그들이 문제 해결을 포기하도록 만들지도 모릅니다. 주니어 개발자가 익숙한 자바스크립트 에러 메시지 대신 낯선 세그폴트 오류를 마주한다면 어떤 기분일지 생각해 보세요.
물론 저는 이미 시니어 개발자이므로 자바스크립트만 고집할 이유가 없습니다. 기술 스택의 모든 부분을 깊이 이해하는 것이 제 역할이기도 합니다.
하지만 우리가 거의 같은 결과를 얻을 수 있는 덜 위험한 다른 길이 있음에도, 의도하지 않은 결과를 가진 알 수 없는 길로 나아가고 있다는 느낌을 지울 수 없습니다. 하지만 이 거대한 흐름은 속도를 늦출 기미를 보이지 않기에 끝에 도달해서야 결과를 알게 될 것 같습니다.
글 번역은 여기까지입니다. 아래는 이 글에 대한 Hacker News의 반응을 AI로 요약해 가볍게 작성해 봤습니다.
반응
찬성 의견 (빠른 언어로 재작성 지지)
실제 성능 경험담: “Node.js로 짠 작은 배치 스크립트를 ChatGPT로 Go 버전 만들어서 돌렸더니, 10만 건도 처리 못 하던 게 같은 머신에서 100만 건까지 처리됐다.” 또 다른 개발자는 “코드 포매터 관리하는데 Ctrl+S 누를 때마다 실행되니까 몇백 밀리초라도 느려지면 개발자 플로우랑 생산성에 직접 영향 준다”고 토로합니다.
기술적 한계 지적: “알고리즘 문제 다 해결해도 언어가 느리면 거기서 끝이야. 성능 천장에 막힌 거지. 개인적으로는 그 천장이 자바스크립트보다 높은 언어에서 작업하고 싶다.”라고 이야기합니다.
병렬처리의 현실적 차이: Rust의 Rayon을 예로 들며 ".iter()만 .par_iter()로 바꾸면 컴파일도 잘 되고 6-7배 스피드업 바로 나온다"고 구체적 방법까지 제시합니다.
반대 의견 (자바스크립트 도구는 자바스크립트로)
디버깅의 현실: “툴체인이 예상대로 안 돌아갈 때 node_modules 안에 console.log 찍어서 문제 해결하는 경우가 정말 많아. StackOverflow나 Google, GPT로도 안 되는 상황에서 정말 큰 도움이 됐는데, Rust로 짜여있으면 그런 기회조차 없지."라고 한계를 이야기합니다.
접근성 우려: “지금 나한테는 Rust 설치가 별거 아니지만, 커리어 초기에는 장벽이었을 거야. 경계선상의 개발자들을 생각해야 해. 간단한 거는 디버그할 수 있는 사람도 복잡한 건 못 하거나 안 하려고 할 수 있거든.”라고 접근성을 우려하기도 합니다.
자바스크립트 발전 가능성: “자바스크립트 도구를 빠르게 만드는 모든 가능성을 다 써보지도 않았다고 생각해”라며 Chrome 개발자 도구에서 Uint8Arrays를 비트 벡터로 쓰는 “정신 나간 기법들”을 예로 들어 “아직 겉 핥기만 한 수준”이라고 주장합니다.
중립/균형 관점
상황별 선택의 지혜: “타입스크립트를 모든 곳에 쓰려다가, 다른 언어를 적재적소에 활용하기 시작하니 삶이 훨씬 편해졌어. 처음에는 가장 능숙한 언어 하나로 고집하고 싶었는데, 얼마나 쉬워질 수 있는지 보고 바로 마음 바뀜.”이라며 상황에 맞춰 적절하게 선택하라고 합니다.
재작성 효과에 대한 냉정한 분석: “재작성할 때 큰 성능 향상을 주장하는 사람들이 놓치는 요소가 있어. 같은 언어로 재작성해도 비슷한 결과 나올 가능성 높거든.” 언어 변경보다는 재설계 효과일 수 있다는 지적도 있습니다.
실용적 타협점: “웹서버나 UI 대부분은 충분히 빠르긴 해”라면서도 “빌드 도구 영역에서 esbuild 보면 네이티브 언어의 성능 향상은 확실하다”고 인정하는 균형 잡힌 시각이 주류입니다.
