앤드류 클라크만의 고민은 아니었다. React Core Team의 모든 엔지니어가 똑같은 악몽을 꾸고 있었다. 그 악몽의 이름은 바로 ‘번들(Bundle)’이었다.
React 팀의 엔지니어, 조쉬 스토리(Josh Story)는 텅 빈 index.js
파일을 열었다. 그리고 단 한 줄의 코드를 작성했다.
ReactDOM.render(<h1>Hello, World</h1>, document.getElementById('root'));
가장 단순한 형태의 React 앱이었다. 이보다 더 간단할 수는 없었다. 하지만 그가 빌드(build) 명령어를 실행하자, 터미널은 순식간에 수십 줄의 로그를 쏟아내며 결과물을 뱉어냈다. 생성된 자바스크립트 파일의 크기는 결코 ‘Hello, World’ 수준이 아니었다.
“이게 말이 된다고 생각해?”
옆자리의 동료가 그의 모니터를 보며 헛웃음을 쳤다.
파일 안에는 ‘Hello, World’라는 텍스트뿐만 아니라, React와 ReactDOM 라이브러리 전체가 고스란히 담겨 있었다. 동적인 상태 변화를 위한 useState
의 로직, 컴포넌트의 생명주기를 다루는 useEffect
의 로직, 그 외에도 수많은 기능이 당장 사용되지도 않음에도 불구하고 하나의 파일로 꽁꽁 묶여 있었다.
마치 작은 편지 한 장을 보내기 위해 거대한 컨테이너선 전체를 빌리는 것과 같았다.
이것이 바로 번들이라는 괴물의 실체였다.
개발자들은 이 괴물을 길들이기 위해 필사적으로 싸웠다. 사용하지 않는 코드를 제거하는 트리 쉐이킹(Tree-shaking), 코드를 여러 조각으로 나누는 코드 스플리팅(Code-splitting) 등 다양한 기술이 등장했다. 하지만 그것은 괴물의 발톱을 다듬는 수준에 불과했다.
애플리케이션에 기능이 하나 추가될 때마다, 새로운 컴포넌트가 생겨날 때마다, 괴물은 그 코드를 먹이 삼아 몸집을 불렸다. 처음에는 수십 킬로바이트였던 번들은 어느새 수백 킬로바이트를 넘어 메가바이트 단위로 치솟았다.
이 거대한 코드 덩어리는 인터넷이라는 혈관을 타고 전 세계 사용자의 기기로 흘러 들어갔다. 최신형 컴퓨터와 기가비트 인터넷 환경의 개발자에게는 찰나의 순간이었지만, 구형 스마트폰으로 불안정한 공공 와이파이에 접속한 사용자에게는 영겁과도 같은 시간이었다.
팀 회의실의 화이트보드에는 복잡한 아키텍처 다이어그램이 가득했지만, 조쉬의 눈에는 거대한 괴물이 입을 벌리고 있는 모습으로 보였다.
“우리의 접근 방식 자체가 잘못된 건 아닐까?”
그가 입을 열었다.
“우리는 지금 ‘클라이언트에 무엇을 더 보낼까’가 아니라, ‘클라이언트에 무엇을 보내지 않을 수 있을까’를 고민해야 합니다. 단순히 텍스트만 보여주는 헤더 컴포넌트가 왜 인터랙션을 위한 모든 코드를 짊어지고 클라이언트까지 가야 하는 거죠?”
그의 말에 회의실은 순간 정적에 휩싸였다. 너무나 당연해서 아무도 의심하지 않았던 전제. ‘React 컴포넌트는 클라이언트에서 실행된다’는 대전제를 건드리는 발언이었기 때문이다.
지금까지의 노력은 모두 괴물을 굶기는 다이어트에 가까웠다. 하지만 조쉬의 질문은 달랐다.
그것은 괴물의 존재 자체를 부정하는, 새로운 차원의 질문이었다.
만약, 애초에 괴물을 만들지 않을 수 있다면? 컴포넌트가 클라이언트로 향하는 배에 올라타지 않는 방법이 있다면?
그 순간, 팀원들의 머릿속에서 희미했던 가능성이 조금씩 형태를 갖추기 시작했다. 이것은 단순한 최적화가 아니었다. React의 심장을 다시 설계하는 거대한 수술의 시작이었다.