맷은 채팅창의 깜빡임 버그를 해결하기 위해 며칠 밤낮으로 매달렸다. 그는 결국 이벤트의 연쇄 반응을 정리하고 순서를 제어하는 복잡한 로직을 추가하여 문제를 해결했다. 코드는 전보다 훨씬 더 보기 흉해졌지만, 어쨌든 버그는 잡았다.
하지만 안도의 한숨을 내쉰 것도 잠시, 새로운 요구사항이 그의 앞에 떨어졌다.
“채팅창에서 사용자 이름을 클릭하면, 그 사람의 간단한 프로필 정보를 보여주는 작은 팝업(Pop-up)을 띄워주세요.”
간단해 보이는 기능이었다. 맷은 늘 하던 대로 백본(Backbone.js)의 뷰를 활용했다. ChatUserView
에 클릭 이벤트를 추가하고, 클릭이 발생하면 팝업을 보여주는 코드를 작성했다.
여기서 문제가 발생했다. ‘팝업이 열려있는지, 닫혀있는지’에 대한 정보, 즉 이 UI의 상태(State)를 어디에 저장해야 할까?
첫 번째 선택지는 UserModel
에 isProfilePopupOpen: true
같은 속성을 추가하는 것이었다. 하지만 이건 말이 안 됐다. 사용자의 프로필 정보라는 본질적인 데이터 모델에, 단순히 UI의 표시 여부를 저장하는 것은 모델의 순수성을 해치는 일이었다.
결국 맷은 두 번째 선택지를 택했다. ChatUserView
객체 자체에 새로운 속성을 추가하는 것이었다.
// ChatUserView 내부 코드 (개념)
var ChatUserView = Backbone.View.extend({
initialize: function() {
// 뷰가 처음 만들어질 때, 팝업은 닫혀 있는 상태.
this.isPopupOpen = false;
},
events: {
'click .username': 'togglePopup'
},
togglePopup: function() {
this.isPopupOpen = !this.isPopupOpen; // 뷰의 상태를 직접 변경
this.render(); // 상태가 바뀌었으니 다시 그린다
},
render: function() {
// ... 기존 렌더링 코드 ...
if (this.isPopupOpen) {
// 팝업을 보여주는 HTML을 추가
}
}
});
이제 상태는 두 곳에 나뉘어 존재하게 되었다. 애플리케이션의 핵심 데이터(사용자 이름, 온라인 여부)는 Model
에, 그리고 UI의 일시적인 상태(팝업 표시 여부)는 View
에 저장되었다. ‘진실의 원천(Single Source of Truth)’이라는 백본의 이상이 깨지기 시작한 순간이었다.
이 작은 균열은 곧 거대한 균열로 이어졌다.
며칠 뒤, 또 다른 버그 리포트가 접수되었다.
[버그] 채팅창: 프로필 팝업을 열어놓은 상태에서 다른 채팅방으로 이동하면, 이전에 열어뒀던 팝업이 사라지지 않고 화면에 그대로 남아있는 문제.
원인은 명확했다. 다른 채팅방으로 이동할 때, ChatRoomModel
의 데이터는 바뀌었지만, ChatUserView
의 isPopupOpen
상태는 아무도 건드려주지 않았기 때문이다. 뷰가 자신만의 독립적인 상태를 가지기 시작하면서, 전체 시스템의 통제에서 벗어난 것이다.
이 문제를 해결하기 위해, 이제 ChatRoomView
(상위 뷰)는 채팅방이 바뀔 때마다 모든 자식 뷰인 ChatUserView
를 일일이 찾아다니며 “얘들아, 혹시 팝업 열려 있으면 다 닫아라!” 라고 직접 명령을 내려야만 했다.
this.subviews.forEach(view => view.closePopup());
이 코드를 작성하던 맷은 등골이 서늘해지는 것을 느꼈다. 이건 jQuery 시절의 악몽과 다를 바 없었다. 상위 객체가 하위 객체의 내부 구현을 속속들이 알아야 하고, 직접 상태를 변경하도록 명령하는 방식. 그들이 벗어나려고 발버둥 쳤던 바로 그 패턴으로 회귀하고 있었다.
그의 뒤에 서서 이 모든 과정을 지켜보던 조던 워크가 입을 열었다. 그의 목소리는 그 어느 때보다 진지했다.
“우리의 근본적인 질문이 틀렸습니다, 맷.”
“네?”
“우리는 계속 ‘상태를 어디에 둬야 할까?’ 라고 묻고 있어요. 모델에 둘까, 뷰에 둘까. 하지만 진짜 질문은 이것이어야 합니다. ‘애플리케이션의 모든 상태를 단 한 곳에만 둘 수는 없을까?’”
조던의 눈빛은 흔들림이 없었다.
“사용자 데이터, 채팅방 정보, 심지어 어떤 팝업이 열려있는지 같은 아주 사소한 UI 상태까지 전부. 모든 것을 단 하나의 거대한 데이터 덩어리로 관리하는 겁니다. 그러면 뷰는 아무런 상태도 가질 필요가 없어요. 그저 그 거대한 데이터를 보고 자신을 그리는 역할만 충실히 하면 되는 거죠. 생각도, 기억도 없는 껍데기처럼요.”
모든 상태의 중앙 집중화.
그것은 너무나 거대하고, 급진적인 아이디어였다. 하지만 맷은 그 혼란스러운 아이디어 속에서, 자신들이 겪고 있는 문제의 본질을 관통하는 날카로운 통찰력을 느낄 수 있었다. 상태가 흩어져 있기에 예측이 불가능하다면, 하나로 모으면 된다. 지극히 단순하지만, 아무도 시도하지 않았던 발상이었다.