느린 초기 로딩과 네트워크 폭포수 문제에 대한 해답으로, 웹 커뮤니티는 일찌감치 ‘서버 사이드 렌더링(SSR, Server-Side Rendering)’이라는 기법을 사용해왔다. 서버에서 미리 페이지의 HTML을 그려서 보내주면, 사용자는 더 이상 텅 빈 흰 화면을 보며 기다릴 필요가 없었다. React 팀 역시 이 흐름에 발맞춰 SSR을 지원했다.
문제는 해결된 것처럼 보였다. 하지만 그것은 더 교묘한 함정의 시작일 뿐이었다.
React Core Team의 회의실, 한쪽 벽면을 가득 채운 모니터에 거대한 이커머스 사이트의 화면이 떠 있었다. SSR 덕분에 페이지는 눈 깜짝할 사이에 로드되었다. 화려한 메인 배너, 스크롤을 내리면 나타나는 수많은 상품 목록, 그리고 화면 우측 상단에 선명하게 보이는 ‘장바구니’ 버튼까지. 모든 것이 완벽해 보였다.
한 엔지니어가 마우스를 움직여 ‘장바구니’ 버튼을 클릭했다.
…아무 일도 일어나지 않았다.
다시 한번 클릭. 역시나 묵묵부답이었다. 화면은 분명히 존재했지만, 그것은 생명이 없는 박제와도 같았다. 사용자는 눈앞에 있는 버튼을 누를 수 없었다. 이 현상을 그들은 ‘죽은 화면(Dead Screen)’이라 불렀다.
원인은 ‘하이드레이션(Hydration)’이라는 과정에 있었다.
서버가 보내준 것은 뼈대뿐인 HTML, 즉 ‘껍데기’였다. 이 껍데기에 생명을 불어넣어 상호작용이 가능하게 만들려면, 클라이언트에서 다운로드한 방대한 양의 자바스크립트가 각 버튼과 입력창에 이벤트 핸들러(Event Handler)를 연결하고 상태(State)를 부여하는 작업이 필요했다. 이 과정을 물을 부어 생기를 되찾게 한다는 의미에서 ‘하이드레이션’이라 불렀다.
문제는 이 하이드레이션이 ‘전부 아니면 전무(All-or-Nothing)’ 방식으로 동작한다는 점이었다.
React는 애플리케이션의 최상단 컴포넌트부터 시작해, 트리 구조를 따라 내려가며 모든 컴포넌트의 하이드레이션을 순차적으로 진행했다. 만약 페이지 최하단에 있는 작은 푸터(Footer) 컴포넌트 하나가 아직 준비되지 않았다면, 사용자가 가장 먼저 누르고 싶어 하는 화면 상단의 ‘장바구니’ 버튼 역시 활성화되지 않았다.
전체 앱의 모든 자바스크립트가 다운로드되고, 모든 컴포넌트가 하이드레이션될 때까지, 애플리케이션 전체가 하나의 거대한 병목 구간에 갇혀버리는 셈이었다.
“이건 역설이야.”
회의실 구석에서 조용히 상황을 지켜보던 세바스찬 마크바게(Sebastian Markbåge)가 입을 열었다. 그의 목소리는 낮았지만, 회의실의 모든 시선을 집중시키기에 충분했다.
“우리는 사용자에게 더 빠른 첫 화면을 보여주기 위해 SSR을 도입했어. 그런데 그 결과로, 사용자는 눈앞에 뻔히 보이는 버튼조차 누를 수 없는, 더 나쁜 경험을 하게 됐지.”
그의 지적은 뼈아팠다. 속도를 위해 도입한 기술이 오히려 사용자의 즉각적인 상호작용을 막아버리는 아이러니. 이것이 바로 하이드레이션의 역설이었다. 사용자는 ‘로딩 중’이라는 정직한 표시보다, ‘다 된 것처럼 보이는데 아무것도 안 되는’ 상황에 훨씬 더 큰 좌절감을 느꼈다.
팀은 이 거대한 병목을 어떻게 해결해야 할지 막막했다. 전체 앱이 아닌, 사용자가 당장 상호작용하려는 그 부분부터 먼저 생명을 불어넣을 수는 없을까?
마치 거대한 건물 전체에 전기가 들어올 때까지 기다리는 것이 아니라, 내가 들어온 1층의 전등 스위치부터 먼저 켤 수 있게 만드는 것처럼.
그것이 가능하다면, 하이드레이션은 더 이상 병목이 아닌, 진정한 의미의 ‘생명을 불어넣는’ 과정이 될 수 있을 터였다. 그들은 이 역설을 깨부술 새로운 방법을 찾아야만 했다.