가상 DOM의 가치가 재평가받기 시작하면서, 개발자들은 리액트가 가진 또 다른 핵심 철학, ‘단방향 데이터 흐름(One-way Data Flow)’에 주목하기 시작했다. 처음에는 이 개념이 앵귤러JS 같은 프레임워크가 제공하는 편리한 ‘양방향 데이터 바인딩(Two-way Data Binding)’에 비해 불편하고 구식이라고 생각하는 사람들이 많았다.
양방향 데이터 바인딩은 마법과도 같았다.
개발자가 <input ng-model="user.name"> 이라고 선언해두면,
user.name데이터가 바뀌면<input>의 값이 자동으로 바뀐다.- 사용자가
<input>에 새로운 값을 입력하면,user.name데이터가 자동으로 바뀐다.
데이터(Model)와 화면(View)이 언제나 완벽하게 동기화되었다. 개발자는 그 사이에서 일어나는 일을 신경 쓸 필요가 없었다. 이 방식은 간단한 폼(Form)을 만들 때 놀라울 정도의 생산성을 보여주었다.
하지만 많은 개발자들이 양방향 데이터 바인딩의 ‘어두운 면’을 경험하기 시작했다. 애플리케이션의 규모가 커지고, 하나의 데이터가 여러 뷰에 의해 동시에 변경되기 시작하면서, 혼돈이 찾아왔다.
한 앵귤러 개발자는 자신의 블로그에 고통스러운 경험담을 공유했다.
“우리 대시보드에는 10개의 필터가 있습니다. 각 필터는 filterOptions라는 하나의 거대한 객체에 양방향으로 바인딩되어 있죠. 문제는, 필터 A를 변경하면 필터 B와 C의 선택 가능 항목이 바뀌어야 하는 복잡한 의존성이 있다는 겁니다. 필터 A의 변경이 filterOptions를 바꾸고, 그 변화가 다시 필터 B, C에 영향을 미치고, 그 영향이 또 다른 연쇄 반응을 일으킵니다. 데이터가 어디서 어떻게 흐르는지, 지금 이 변화가 대체 누구로부터 시작되었는지 추적하는 것은 불가능에 가깝습니다. 우리는 이 현상을 ‘폭포수 효과(Cascading Effect)’라고 부르며 두려워하고 있습니다.”
그의 글은 수많은 개발자들의 공감을 샀다. 양방향 데이터 바인딩이 주는 편리함의 대가는, 예측 불가능성이라는 값비싼 청구서로 돌아오고 있었다.
바로 그때, 리액트의 단방향 데이터 흐름이 대안으로 떠올랐다.
리액트에서는 양방향 데이터 바인딩이 원천적으로 불가능했다. 데이터는 언제나 위에서 아래로만, 부모에서 자식으로만 흘렀다. 만약 자식 컴포넌트(input)에서 데이터 변경이 필요하다면, 절대 데이터를 직접 수정할 수 없었다.
대신, 다음과 같은 명시적인 패턴을 따라야 했다.
- 부모 컴포넌트가
state에username을 가지고 있는다. - 부모는 자식
<input>에게value={this.state.username}이라는props를 내려준다. (데이터 흐름: 아래로) - 부모는 또한
onChange={this.handleUsernameChange}라는 콜백 함수를props로 함께 내려준다. - 사용자가
<input>에 글자를 입력하면,onChange이벤트가 발생한다. <input>은 부모에게 받은handleUsernameChange함수를 호출하며, “사용자가 이런 값을 새로 입력했습니다” 라고 알린다. (데이터 흐름: 위로, 단 이벤트 콜백을 통해)- 부모의
handleUsernameChange함수 안에서,this.setState({ username: ... })를 호출하여 자신의state를 갱신한다. state가 바뀌었으므로, 리렌더링이 일어나고, 새로운username값이 다시 자식<input>의valueprops로 내려간다.
이 과정은 양방향 데이터 바인딩에 비해 번거롭고 장황해 보였다.
하지만 이 ‘번거로움’ 속에 놀라운 안정감이 숨어 있었다.
데이터를 변경할 수 있는 유일한 주체는 데이터를 소유한 부모 컴포넌트뿐이었다. 모든 데이터 변경은 setState라는 단 하나의 통로를 거쳐야만 했다. 데이터가 어디서, 어떻게, 왜 바뀌었는지에 대한 모든 흐름이 코드 상에 명확하게 드러났다. 숨겨진 마법은 없었다. 모든 것이 예측 가능했다.
양방향 데이터 바인딩의 혼돈에 지쳐있던 개발자들은 리액트의 이 ‘지루할 정도로 명시적인’ 방식에서 깊은 안정감을 느끼기 시작했다. 그들은 약간의 편리함을 포기하는 대신, 애플리케이션 전체의 동작을 자신의 통제 아래 둘 수 있는 예측 가능성을 얻게 된 것이다.
단방향 데이터 흐름은 더 이상 낡고 불편한 방식이 아니었다. 복잡한 애플리케이션을 지탱하는 가장 튼튼하고 신뢰할 수 있는 기둥으로 재평가받기 시작했다.


