빌드가 끝나기를 기다리는 개발자들로 가득 찬 방에는 묘한 정적이 흐릅니다. 시선은 보조 모니터로 향하고, 엄지손가락은 스마트폰을 스크롤합니다. 누군가는 딱히 마시고 싶지도 않은 커피를 마시러 일어납니다. 현대적인 JavaScript 코드베이스에서 시간을 보내본 적이 있다면, 이 멈춤의 순간을 알고 있을 것입니다. 이것은 휴식이 아닙니다. 사고의 흐름이 끊기는 구멍입니다.
우리는 프레임워크에 대해 많은 이야기를 합니다. React, Vue, Svelte, 그리고 다음 주에 출시될 그 무엇이든 관심을 독차지합니다. 컨퍼런스는 프레임워크 발표 소식만으로 매진됩니다. 블로그 포스트는 문법적 설탕(syntax sugar)을 분석합니다. 하지만 이러한 사용자 중심의 소음 아래에서는, 코드를 작성하는 방식을 실제로 바꿀 만큼 근본적인 변화가 일어나고 있습니다. 혁명은 프론트엔드 프레임워크에서 오는 것이 아닙니다. 툴링 레이어(tooling layer)에서 일어나고 있으며, Rust와 Go로 작성되고 있습니다.
수년 동안 JavaScript 도구들은 JavaScript로 만들어졌습니다. 그것은 타당한 선택이었습니다. Babel은 한 세대에게 내일의 문법을 오늘 쓰는 법을 가르쳤습니다. Webpack은 분할된 코드를 브라우저가 처리할 수 있는 형태로 번들링했습니다. ESLint는 코드를 커밋하기 전에 버그를 잡아냈습니다. 이 도구들은 더 작은 규모의 웹을 위해 설계되었습니다. 수만 개가 아닌 수백 개의 모듈을 가정했습니다. 공유 UI 패키지의 변경이 수십 개의 애플리케이션에 영향을 미치는 모노레포(monorepo)가 아닌, 단일 레포지토리를 가정했습니다.
그러다 앱이 커졌습니다. 코드베이스는 거대한 레포지토리로 불어났습니다. 도구들은 그대로였고, 지연 시간(latency)이 스며들기 시작했습니다. 2초 걸리던 핫 리로드가 12초, 30초로 늘어났습니다. 점심 식사 전에 전체 테스트 스위트를 실행하는 것은 환상이 되었습니다. 린터는 수천 번 확인했던 파일에서도 버벅거렸습니다. 서류상으로는 모든 지연이 작아 보일 수 있습니다. 하지만 실제로 이러한 멈춤은 집중력을 산산조각 냅니다. 이는 작업을 몰아서 하게 만들고, 수정 사항이 제대로 작동하는지 확인하기 전에 망설이게 하며, 피드백 비용이 너무 높기 때문에 실험을 피하게 만듭니다.
차세대 툴링은 단순히 JavaScript의 방해를 받지 않음으로써 그 지연 시간을 해결합니다.
새로운 엔진룸
특정 작업들이 어떻게 되찾아오고 있는지 살펴보십시오.
**Transformation(변환)**은 예전에 Babel을 의미했습니다. JSX와 stage-3 제안들을 순수 ES5로 번역하는 범용 전처리기였습니다. 여전히 인상적인 소프트웨어이지만, 단일 스레드 JavaScript가 JavaScript를 파싱하는 방식입니다. 여기에 Rust 기반 툴체인인 OXC가 등장했습니다. Babel이 수행하는 동일한 작업을 처리하면서도, 벤치마크 결과 메모리를 70% 적게 사용하면서 약 40배 더 빠릅니다. 이것은 점진적인 개선이 아닙니다. 의식하며 사용하는 도구와, 실행 중인지조차 잊게 만드는 도구의 차이입니다.
**Bundling(번들링)**은 고통이 가장 극심했던 분야입니다. Webpack은 10년 동안 표준이었지만, 그 내부 구조는 다른 규모를 위해 구축되었습니다. 그 Rust 후계자인 Turbopack은 단순히 더 빠르게 재컴파일하는 것에 그치지 않습니다. 공격적인 메모이제이션(memoization)을 활용하여 무엇이 변경되었는지 정확히 파악하고, 해당 부분만 다시 빌드합니다. 대규모 애플리케이션에서 단일 컴포넌트를 변경한다고 해서 전체 그래프를 순회할 필요는 없습니다. Turbopack과 함께라면 빌드는 즉각적인 수준에 가까워집니다. 지켜볼 프로그레스 바조차 사라집니다. 볼 것이 없기 때문입니다.
**Testing(테스트)**은 그 나름의 특유한 지연을 동반합니다. Jest는 JavaScript 테스트를 재정의했지만, watch 모드에서는 키를 누를 때마다 코드베이스를 새로 배우는 듯한 느낌을 줄 수 있습니다. Vitest는 다른 아키텍처적 접근 방식을 취합니다. 처음부터 자체 의존성 트리를 구축하는 대신 Vite의 모듈 그래프를 재사용하기 때문에, watch 모드에서 Jest보다 약 8.5배 빠른 속도를 보여줍니다. 여기서 얻는 이점은 단순히 원시적인 속도만이 아닙니다. 바로 일관성(coherence)입니다. 테스트 러너와 개발 서버가 마침내 프로젝트의 상태에 대해 동일한 이해를 갖게 됩니다.
Linting(린팅) 또한 유사한 오버헤드를 겪습니다. ESLint의 유연성은 강력한 무기입니다. 그 규칙들은 AST에서 작동하는 단순한 JavaScript 함수입니다. 하지만 그 유연성에는 비용이 따릅니다. Rust로 작성된 Oxlint는 일반적인 사례로 범위를 좁혀 압도적인 속도를 보여줍니다. ESLint보다 50배에서 100배 더 빠르게 실행됩니다. 실질적인 효과는 에디터의 저장 애니메이션이 끝나기도 전에 린팅이 완료된다는 것입니다. 문제를 이미 해결했는데도 몇 초 동안 남아 있는 빨간 밑줄을 더 이상 참지 않아도 됩니다.
가장 상징적인 변화는 아마도 **타입 체크(type checking)**에서 일어나고 있을 것입니다. Microsoft는 현재 TypeScript 컴파일러를 Go 언어로 다시 작성하고 있습니다. 초기 벤치마크 결과는 놀랍습니다. 새로운 구현을 사용하면 VS Code의 로딩 속도가 약 8배 빨라지고, 타입 체크 자체는 약 10배 빨라집니다. 이것이 무엇을 의미하는지 생각해 보십시오. TypeScript는 JavaScript의 성공 신화입니다. JavaScript로 컴파일되는 언어이자, JavaScript 생태계의 타입을 체크하는 데 사용되는 도구입니다. 그런데 이제 그 자체의 컴파일러가 네이티브 시스템 언어로 이동하고 있습니다. JavaScript가 생태계가 요구하는 성능을 제공할 수 없기 때문입니다. 도구가 속도를 위해 스스로의 경로를 삼키고 있는 셈입니다.
이것이 React를 대체하는 것은 아닙니다. Next.js를 없애거나 TypeScript를 쓸모없게 만드는 것도 아닙니다. 프레임워크는 여전히 여러분의 컴포넌트 모델과 라우팅을 정의합니다. 이 새로운 도구들은 단지 그 밑바닥에 있는 모든 것을 더 빠르게 만들 뿐입니다. 그것들은 자동차가 아니라 도로입니다.
속도가 행동을 변화시킬 때
도구에 관한 대화는 종종 벤치마크 차트에서 멈추곤 합니다. 숫자는 비교하기 쉽기 때문입니다. 하지만 진짜 영향력은 인간의 행동에 있습니다.
피드백이 몇 초에서 밀리초 단위로 줄어들면, 단순히 작업을 더 빨리 끝내는 것에 그치지 않습니다. 작업을 수행하는 방식 자체가 달라집니다. 변경 사항을 쌓아두지 않게 됩니다. 코드 한 줄을 쓰고, 결과를 확인하고, 바로 수정합니다. 풀 리퀘스트(pull request)에서 요구하기 때문이 아니라, 테스트가 즉각적이기 때문에 테스트를 실행합니다. 되돌리는 데 비용이 들지 않기 때문에, 실패할지도 모르는 리팩터링을 시도해 봅니다. 기계가 다시 응답하기를 기다리는 대신, 문제 속에 계속 머물러 있게 됩니다.
심리학자들은 이를 '몰입(flow)'이라고 부릅니다. 몰입에는 행동과 결과 사이의 긴밀한 루프가 필요합니다. 기타리스트는 앰프가 모든 음을 지연시킨다면 연주할 수 없습니다. 화가는 붓의 움직임이 0.5초 늦게 반영된다면 색을 섞을 수 없습니다. 개발자도 다르지 않습니다. 지연 시간(latency)은 단순한 번거로움이 아닙니다. 그것은 사고에 매겨지는 세금입니다.
따라서 생산성 향상은 단순히 기술적인 문제가 아니라 습관의 문제입니다. 빠른 도구는 여러분이 실험하도록 훈련시킵니다. 느린 도구는 여러분이 망설이도록 훈련시킵니다. 1년이라는 시간이 흐르면, 이 차이는 완전히 다른 소프트웨어라는 결과로 누적됩니다. 즉각적인 피드백을 받는 팀은 더 자신 있게 결과물을 출시합니다. 시도하는 비용이 제로에 가깝기 때문에 작업을 더 작은 단위로 나눕니다. 버그가 20분 뒤 CI에서 발견되는 것이 아니라 그 순간에 포착되기 때문에 코드 리뷰 시간도 단축됩니다.
보이지 않는 작업
이것이 바로 헤드라인이 오해를 불러일으키는 이유입니다. 프레임워크는 글쓰기 쉽습니다. 로고도 있고, API도 있고, 트위터에서의 논쟁거리도 있습니다. 하지만 인프라스트럭처는 설계부터 보이지 않게 만들어져 있습니다. 번들러를 설정하는 것에 설레며 잠에서 깨는 사람은 없습니다. 그저 그것이 사라지기를 바랄 뿐입니다. 하지만 사라지는 것이야말로 훌륭한 인프라스트럭처가 하는 일입니다. 가시적인 레이어가 가볍게 유지될 수 있도록 무게를 대신 짊어지는 것입니다.
만약 여러분이 팀을 이끌거나 레거시 코드베이스를 유지 관리하고 있다면, 이 점을 우선순위에 반영해야 합니다. React에서 Vue로 마이그레이션하는 것은 컴포넌트 트리의 형태를 바꿀 수 있습니다. 하지만 Webpack에서 Turbopack으로, 또는 Babel에서 OXC로 마이그레이션하는 것은 여러분의 업무 방식 전체를 바꿀 수 있습니다. 후자는 경영진을 설득하기 더 어려운 이야기입니다. 새로운 홈페이지 데모 같은 것이 없기 때문입니다. 그저 빌드 터미널을 보며 한숨 쉬는 일이 사라진 팀이 있을 뿐입니다.
무엇이 실제로 여러분을 느리게 만드는지 점검하십시오. 만약 2015년에 만들어진 툴체인 위에서 현대적인 모노레포를 운영하고 있다면, 여러분은 보수적인 것이 아닙니다. 매일 '마찰 세금'을 지불하고 있는 것입니다. 해결책은 새로운 프런트엔드 패러다임을 배우는 것이 아닙니다. 엔진을 교체하는 것입니다.
프레임워크는 계속해서 등장할 것입니다. 계속해서 트위터의 화제가 되고 컨퍼런스의 기조연설 주제가 될 것입니다. 하지만 JavaScript를 작성할 때 느끼는 체감상의 진짜 변화는, 여러분의 시간을 귀하게 여기는 컴파일 언어들을 통해 내부적으로 일어나고 있습니다. 그것이 바로 혁명입니다. 리스트를 렌더링하는 새로운 방식이 아니라, 방해하지 않고 여러분이 생각에 집중할 수 있게 해줄 만큼 충분히 빠른 툴체인 말입니다.
