톰은 텅 빈 회의실에 남아 화이트보드에 그려진 스파게티 코드의 흔적을 멍하니 바라보았다. 개발자들이 겪는 고통의 근원은 명확했다. 모두가 한 곳, 바로 ‘DOM(Document Object Model)’을 직접 건드리고 있기 때문이었다.
DOM. 문서 객체 모델.
이름은 거창했지만, 그 실체는 간단했다. 브라우저가 웹페이지의 HTML 코드를 읽어서 메모리상에 나무(Tree) 형태로 펼쳐놓은 일종의 설계도였다. 브라우저는 이 설계도를 보고 화면에 버튼과 글자, 이미지들을 그렸다.
jQuery는 이 살아있는 설계도를 수술하는 날카로운 메스와도 같았다. $('#notification_badge_count')
같은 명령은 설계도에서 ‘notification_badge_count’라는 이름표가 붙은 부품을 정확히 찾아냈다. 그리고 .text('1')
이나 .addClass('important')
같은 후속 명령은 그 부품의 모양이나 색깔을 즉석에서 바꿔버렸다.
이 방식의 가장 큰 문제는, 설계도 자체가 ‘상태 저장소’가 되어버린다는 점이었다. 현재 알림의 개수는 자바스크립트 변수가 아닌, DOM 요소인 <span>
태그의 텍스트 안에 ‘기록’되어 있었다.
이것이 왜 위험한가? 톰은 가상의 시나리오를 떠올렸다.
한 개발자가 ‘친구 목록’에 새로운 기능을 추가하라는 임무를 받는다. 기능의 내용은 간단하다. 친구 목록 중에서 ‘나와 함께 아는 친구’를 다른 색으로 강조 표시하는 것이다.
개발자는 jQuery를 이용해 코드를 짠다.
- 서버로부터 ‘함께 아는 친구’의 ID 목록을 받아온다.
- 현재 화면에 그려진 친구 목록(
<ul>
태그)을 훑으며 각 친구(<li>
태그)의 ID를 확인한다. - 만약 친구의 ID가 ‘함께 아는 친구’ 목록에 포함되어 있다면, 그
<li>
태그에highlight
라는 클래스를 추가한다. (.addClass('highlight')
)
겉보기에는 완벽한 로직이다. 하지만 바로 이때, 채팅창에서 새 메시지가 도착한다. 채팅 스크립트는 새로운 메시지가 왔다는 것을 알리기 위해, 친구 목록의 순서를 바꿔 ‘메시지를 보낸 친구’를 맨 위로 올리는 코드를 실행한다.
순식간에 DOM 구조가 바뀐다.
방금 전까지 ‘함께 아는 친구’를 강조하던 스크립트는 엉뚱한 사람을 강조하거나, 이미 순서가 바뀌어 버린 친구를 찾지 못해 기능을 제대로 수행하지 못한다.
두 스크립트는 서로의 존재를 전혀 알지 못한다. 그들은 단지 눈앞의 DOM이라는 단 하나의 설계도를 각자 자기 방식대로 수정하려 했을 뿐이다. 그 결과는 예측 불가능한 충돌과 버그다.
더 나쁜 것은 성능 문제였다. DOM을 한 번 변경할 때마다 브라우저는 전체 설계도를 다시 검토하고 화면을 새로 그리는 값비싼 작업을 수행해야 했다. 친구 50명을 강조하기 위해 DOM을 50번 건드린다면, 화면은 50번 깜빡이며 버벅거렸다.
톰은 깊은 한숨을 내쉬었다. 그는 조던 워크가 회의에서 던졌던 ‘선언’이라는 단어의 의미를 곱씹었다.
‘직접 설계도를 고치지 말자.’
‘그냥 우리가 원하는 최종 결과물만 이야기하는 거야.’
즉, ‘1번 친구는 일반, 2번 친구는 강조, 3번 친구는 일반…’ 과 같이, 친구 목록의 ‘완성된 상태’를 정의한 데이터를 만드는 것이다. 그리고 그 데이터를 어떤 ‘시스템’에 던져주면, 그 시스템이 가장 효율적인 방법으로 단 한 번에 DOM을 수정해 주는 방식.
개발자는 더 이상 DOM의 복잡한 구조나 다른 스크립트와의 충돌을 걱정할 필요가 없어진다. 오직 순수한 데이터, 즉 ‘상태’에만 집중하면 되는 것이다.
“그래….” 톰은 나지막이 중얼거렸다. “우리는 DOM을 너무 믿었어.”
그것은 거대한 깨달음이었다. DOM은 신뢰할 수 있는 정보의 원천이 아니었다. 그저 데이터의 최종 결과물을 보여주는 휘발성 강한 스크린일 뿐이었다. 진짜 정보는 다른 곳, 오직 데이터만이 존재하는 순수한 공간에 격리되어야 했다.
그 순간, 페이스북 프론트엔드에 ‘구조’를 도입하려는 첫 번째 움직임이 시작되었다. 그들의 시선은 당시 자바스크립트 커뮤니티에서 서서히 명성을 얻고 있던 라이브러리, Backbone.js로 향하고 있었다.