홈페이지에서 React와 Babel을 걷어낸 이유
Why a one-page studio site was shipping 4.2 MB of JavaScript, and what it ships now.
On 2026-09-05 we replaced the React + Babel-standalone setup behind oddduck.ooo with plain HTML. The old page downloaded 4,226 KB of JavaScript — Babel 3,064 KB, React DOM 1,055 KB, React 107 KB — and compiled its JSX in the browser before painting anything; on a wired line that alone took 512 ms. The new page ships about 11 KB of vanilla JS and paints in under 100 ms on a local server. Nothing visible changed.
oddduck.ooo 홈은 2026년 9월 5일부터 React 없이 정적 HTML로 서빙된다. 그 전에는 첫 픽셀이 나오기까지 자바스크립트 4,226 KB를 받고 브라우저에서 JSX를 컴파일했다. 지금은 11 KB다.
발단은 "접속이 너무 오래 걸리는 것 같다"는 한마디였다. 서버부터 의심했지만 서버는 멀쩡했다. 문제는 우리가 홈페이지를 만든 방식이었다.
무엇이 느렸나what was slow
사실 개편 전 홈은 HTML 본문이 사실상 비어 있었다. 화면은 외부 CDN에서 스크립트 세 개가 도착한 뒤에야 React가 그렸다. 화면을 만드는 코드는 미리 컴파일해 두지 않아서 방문자의 브라우저가 매번 컴파일했다(2026년 9월 5일 기준).
사실 2026년 9월 5일, 유선 회선에서 브라우저 캐시를 끄고 세 파일을 다시 받아 본 결과다.
| 파일 | 크기 | 다운로드 |
|---|---|---|
| @babel/standalone 7.29.0 | 3,064 KB | 290 ms |
| react-dom 18.3.1 (development) | 1,055 KB | 146 ms |
| react 18.3.1 (development) | 107 KB | 76 ms |
| 합계 | 4,226 KB | 512 ms |
세 가지가 겹쳐 있었다. 첫째, 개발용 빌드를 쓰고 있었다. 운영용 빌드면 React 두 개가 1,162 KB에서 140 KB 남짓으로 줄어든다. 둘째, Babel 3 MB는 순전히 JSX를 브라우저에서 컴파일하기 위한 비용이었다. 셋째, 본문이 비어 있어서 이게 다 도착하고 컴파일까지 끝나야 첫 픽셀이 그려졌다. 그 전까지 방문자는 크림색 빈 화면을 봤다.
유선에서 다운로드만 0.5초다. 대역폭이 그 5분의 1인 회선이라면 단순 계산으로 2.5초다. 여기에 3 MB짜리 스크립트를 파싱하는 시간과 JSX 컴파일 시간이 방문자 기기의 CPU 몫으로 더해진다. "오래 걸린다"는 체감과 정확히 맞는 구조였다.
참고로 서버 쪽은 문제가 아니었다. 사실 같은 날 직접 잰 우리 서버의 첫 바이트 응답은 100~270 ms였고 이미지 전부를 합쳐도 270 KB였다.
왜 그렇게 만들었나how it got there
처음 이 사이트는 디자인 프로토타입으로 시작했다. 서체와 카피를 바꿔 가며 보려고 화면 구석에 조정 패널(tweaks panel)을 달았다. 그걸 빨리 만들려고 React를 쓰고 JSX도 브라우저에서 컴파일했다. 빌드 단계를 두지 않는다는 원칙과 "빨리 보고 싶다"는 욕심이 만나면 이런 구조가 나온다. 프로토타입이 그대로 라이브가 됐을 뿐이다.
사실 그 조정 패널은 방문자에게 한 번도 렌더된 적이 없다. 그런데도 그 패널 코드 25.8 KB가 방문할 때마다 함께 컴파일되고 있었다.
무엇으로 바꿨나what replaced it
홈은 한 페이지짜리 소개 사이트다. 상태라고 할 것이 모바일 메뉴가 열렸는지 정도밖에 없다. 그래서 프레임워크를 바꾸는 대신 프레임워크를 없앴다.
- React가 만들던 화면을 HTML에 그대로 적었다. 브라우저는 받는 즉시 그린다.
- 메뉴, 모바일 드로어, 오리를 눌렀을 때 나는 꽥 소리는 작은 스크립트 하나로 옮겼다.
- 상태 줄과 키보드 단축키는 원래 있던 작은 스크립트에 그대로 두었다.
- 폰트는 스타일 안에서 줄줄이 불러오던 방식 대신 문서 머리에서 바로 받도록 바꿨다. 전에는 스타일을 다 받은 뒤에야 폰트를 순서대로 받았다.
- 조정 패널과 컴파일이 필요한 파일들은 지웠다.
빌드 도구는 여전히 없다. 파일을 고쳐 올리면 그대로 배포된다. 우리가 지키고 싶었던 것은 "빌드 없음"이었지 "React"가 아니었다.
결과result
사실 개편 뒤 로컬 서버(폰트 캐시된 상태)에서 First Contentful Paint 96 ms, DOMContentLoaded 53 ms. 라이브에서 첫 방문 기준 DOMContentLoaded 361 ms. 홈이 받는 스크립트는 둘, 합쳐 약 11 KB(2026년 9월 5일 측정).
| 전 | 후 | |
|---|---|---|
| 자바스크립트 | 4,226 KB + JSX 49 KB | 11 KB |
| 브라우저 내 컴파일 | 매 방문 | 없음 |
| 첫 픽셀까지 | 전부 도착·컴파일 후 | HTML 도착 즉시 |
| 빌드 단계 | 없음 | 없음 |
보이는 것은 하나도 바뀌지 않았다. 색, 서체, 오리, 꽥 소리, 마퀴, 모바일 드로어 전부 그대로다. 바뀐 것은 그것들이 화면에 오기까지 거치는 경로뿐이다.
배운 것what we keep
프로토타입 도구를 라이브에 그대로 두면 방문자가 그 비용을 낸다. 특히 "빌드 없이"라는 말은 "번들 없이"를 뜻하는 게 아니다. 브라우저에서 컴파일하는 것은 빌드를 없앤 게 아니라 방문자마다 빌드를 시키는 것이다.
그래서 우리 원칙에 한 줄이 늘었다. React를 다시 들이지 말 것. 화면이 바뀔 이유가 생기면 그때 다시 생각하겠지만 소개 페이지 하나에 4 MB짜리 컴파일러를 붙일 이유는 생기지 않을 것이다.