타임라인 프로젝트가 엔지니어링 팀 전체에 거대한 압박으로 다가오던 그 무렵, 또 다른 전선에서 조용한 전쟁이 벌어지고 있었다. 바로 페이스북의 심장, 뉴스피드였다.
뉴스피드는 더 이상 사용자가 페이지를 새로고침해야만 새 소식을 볼 수 있는 정적인 공간이 아니었다. 그것은 살아 숨 쉬는 유기체처럼, 실시간으로 변화해야 했다.
- 친구가 새로운 게시물을 올리면, 즉시 뉴스피드 최상단에 나타나야 했다.
- 누군가 내 글에 댓글을 달면, 그 댓글이 실시간으로 추가되어야 했다.
- 수백 명이 동시에 ‘좋아요’를 누르는 인기 게시물은, 그 숫자가 끊임없이 올라가야 했다.
문제는 이 모든 변화가 서버에서 비동기적으로, 예측 불가능한 순서로 밀려들어 온다는 점이었다. 기존의 jQuery와 일부 백본이 뒤섞인 코드는 이 실시간 데이터의 폭격을 감당하지 못했다.
“상태가 또 꼬였어요!”
뉴스피드 팀의 한 개발자가 외쳤다. 그의 화면에서는 방금 달린 댓글이 사라졌다가 다시 나타나고, ‘좋아요’ 숫자가 10에서 갑자기 5로 줄어드는 기현상이 벌어지고 있었다.
원인은 ‘경합 조건(Race Condition)’ 때문이었다.
- 사용자의 브라우저가 서버에 ‘댓글 목록’을 요청한다. (요청 A)
- 요청 A의 응답이 도착하기 전에, 다른 친구가 새 댓글을 달았다는 실시간 푸시 신호가 도착한다.
- 브라우저는 이 푸시 신호를 기반으로 새 댓글을 화면에 즉시 그려준다. (화면 상태: 댓글 5개)
- 잠시 후, 뒤늦게 요청 A의 응답이 도착한다. 이 응답은 푸시 신호가 있기 전의 상태를 담고 있다. (서버 데이터: 댓글 4개)
- jQuery 코드는 도착한 응답 데이터를 기준으로 화면을 덮어써 버린다. (화면 상태: 댓글 4개)
사용자의 눈에는 방금 생겼던 댓글이 갑자기 사라지는 것처럼 보였다. 데이터의 최종적인 ‘진실’이 무엇인지 누구도 보장할 수 없었다. 개발자들은 이런 문제를 막기 위해 요청마다 타임스탬프를 찍어 비교하거나, 복잡한 잠금(Locking) 메커니즘을 도입하려 했지만, 코드는 점점 더 누더기가 되어갈 뿐이었다.
이 혼란을 해결하기 위해, 조던 워크가 속한 팀은 새로운 내부 프로젝트를 가동했다. 그들의 접근 방식은 기존의 프론트엔드 팀과는 완전히 달랐다. 그들은 브라우저의 자바스크립트가 아닌, 서버 측의 PHP에 먼저 주목했다.
“문제는 클라이언트(브라우저)가 너무 많은 걸 하려고 한다는 겁니다.”
조던이 팀 회의에서 설명했다.
“만약 서버에서 이미 완벽하게 조립된 HTML 조각을 만들어서 보내준다면 어떨까요? 클라이언트는 그저 받은 조각을 끼워 넣기만 하는 거죠. 데이터가 어떻고, 상태가 어떻고 하는 복잡한 로직은 전부 서버에서 처리하는 겁니다.”
즉, 실시간 푸시 신호가 오면, 브라우저는 서버에 “방금 그 댓글, HTML로 만들어서 보내줘”라고 요청한다. 서버의 PHP 코드는 댓글 데이터를 포함한 완전한 HTML 마크업을 생성해서 돌려준다. 브라우저는 받은 HTML 덩어리를 뉴스피드의 적절한 위치에 그대로 삽입하면 끝이다.
이 방식은 클라이언트 측의 복잡도를 혁신적으로 줄여주었다. 브라우저는 더 이상 데이터의 진실성을 고민할 필요가 없었다. 서버가 보내준 것이 곧 진실이었다.
하지만 이 방식에도 명백한 한계가 있었다. 서버에서 HTML을 생성하는 것은 비용이 많이 들고, 매번 네트워크를 통해 HTML 문자열을 주고받는 것은 비효율적이었다. 그리고 결정적으로, PHP는 웹페이지를 동적으로 만드는 데 태생적인 한계를 가진 언어였다.
엔지니어들은 깨달았다. 서버에서 HTML을 다루는 더 세련되고 강력한 도구가 필요하다는 것을.
그 필요성이 바로 페이스북 내부에서 거대한 변화를 일으킬 새로운 기술, XHP의 탄생을 예고하고 있었다. 그것은 리액트라는 최종 목적지를 향한, 누구도 예상치 못했던 첫 번째 발걸음이었다.