이번 글에서는 '왜 리액트는 단방향 데이터 흐름을 채택했을까?'에 대해 고민 해 보고자 합니다.
언제나 그렇듯 새로운 개념과 기술의 탄생은 이전의 고질적인 문제점들은 보완하기 위해 탄생한다는 관점에서 배경과 맥락을 함께 살펴보려합니다.
데이터 바인딩은 MVC(Model-View-Controller) 아키텍처에 대한 기본적인 이해가 필요합니다.
데이터 바인딩은 구조화된 애플리케이션의 데이터와 비즈니스 로직을 담당하는 모델(Model)과 사용자 인터페이스(UI)를 구성하고 데이터를 표시하는 뷰(View) 간에 데이터를 어떻게 처리하고 동기화하는지에 대한 프로세스를 나타냅니다. 이는 모던 프론트엔드 프레임워크, 라이브러리에서 데이터와 UI 간에 어떻게 일관성을 유지하고 효율적으로 처리하는지를 나타내는 중요한 부분 중 하나입니다.
대표적으로 React는 단방향 데이터 바인딩을, Vue.js, AngularJS는 단방향 데이터 바인딩과 양방향 데이터 바인딩을 모두 지원한다고 알려져있습니다.
단방향 데이터 바인딩과 양방향 데이터 바인딩은 데이터의 흐름과 동기화 방식에서 서로 다른 접근 방식을 가지고 있습니다.
집중하는 부분에 따라 얻어가는 것과 잃는 것이 존재하듯 두 방식에도 장단점이 존재합니다.
양방향 데이터 바인딩(Two-way Data Binding) 은 데이터의 변경이 모델과 뷰 간에 양방향으로 전파되어 동기화되는 방식을 의미합니다.
전통적인 MVC(Model-View-Controller) 및 MVVM(Model-View-ViewModel) 아키텍처에서 사용되며,
사용자와의 상호작용에 따라 입력한 데이터가 즉시 모델에 반영되고, 모델의 변경이 UI에 즉시 반영되어 양방향으로 동기화됩니다.
이러한 방식은 1) 필연적으로 모델과 뷰 사이의 강한 의존성과 결합도를 만들어 내며, 2) 데이터의 양이 많아질수록 성능 저하를 초래할 수 있습니다. 3) 작은 규모의 애플리케이션에서는 코드 복잡성을 낮추며 개발 생산성을 높일 수 있지만, 규모가 커질수록 데이터의 흐름을 추적하기 어려워지고, 복잡성 또한 증가하기 때문에 확장성 있는 애플리케이션을 만드는 것이 어렵다는 문제점을 가지고 있습니다.
이러한 양방향 데이터 바인딩의 일부 고질적인 문제를 보완하고,
애플리케이션의 관리 용이성을 향상시키는 데 중점을 두어 고안된 개념이 '단방향 데이터 바인딩(One-way Data Binding)' 입니다.
단방향 데이터 바인딩이란 데이터의 변경이 모델에서 뷰 단방향으로만 전파되어 동기화되는 방식을 의미합니다.
다만, 모델에서의 데이터 변경은 뷰로 전파되어 동기화되지만 뷰에서의 데이터 변경은 모델로 역전파 되지 않으며 명시적으로 동기화가 필요합니다.
이러한 방식은 데이터의 흐름을 한 방향으로 유지함으로써 1) 데이터의 흐름을 예측 가능하고, 추적을 가능하게 만들어주며 2) 애플리케이션의 상태 관리를 단순화하고 효율적으로 만듭니다. 3) 또한 모델과 뷰의 관심사를 명확하게 분리하여 의존성과 결합도를 낮추는 방식으로 문제를 해결합니다.
리액트는 컴포넌트 계층구조를 따라 아래로 흐르는 단방향 데이터 흐름(one-way data flow) (또는 단방향 바인딩(one-way binding))을 따릅니다.
이는 리액트의 핵심 원칙 중 하나로, 일관성 있고 예측 가능한 애플리케이션을 구현하는 데 중요한 역할을 합니다.
'왜 리액트는 단방향 데이터 흐름(one-way data flow)을 채택했을까?'
위에서 이미 단방향 데이터 흐름을 통해 해결하고자 하는 문제점이 무엇이었을지 어림짐작할 수 있었지만,
배경과 맥락을 통해 조금 더 이해하며 글을 마무리하려 합니다.
MVC는 정말 눈 깜짝할 사이에 복잡해진다.
리액트의 초기 개발 당시, 페이스북 팀은 대규모 애플리케이션에서 기존의 MVC 아키텍처의 한계를 인식하게 되었고 당면한 문제를 기술적으로 해결하기 위해 노력했습니다.
2014년 5월, "Hacker Way: Rethinking Web App Development at Facebook" 세션에서 '일정 수준 이상의(sufficiently) 코드 베이스, 일정 수준 이상의 조직에서 MVC는 눈 깜짝할 사이에 복잡해진다.' 라는 언급을 하며 예시로 한 다이어그램을 공개했습니다.
발표에 따르면, MVC 아키텍처의 '양방향 데이터 흐름'은 모델과 뷰의 개수가 많아질수록 시스템의 복잡도를 기하급수적으로 증가시키고, 데이터의 흐름을 예측하기 어렵게 만든다고 주장합니다. 실제로 페이스북 팀은 알림(notification) 버그등의 문제를 겪으며 'MVC 아키텍처가 대규모 애플리케이션에 적합하지 않으며, 예측 가능하고, 확장성 있는 더 나은 품질의(quality) 소프트웨어를 개발하기 위해서는 시스템 아키텍처를 개선하는 것이 필요하다.'라는 결론에 도달했습니다. 이에 페이스북 팀은 '단방향 데이터 흐름'을 기반으로 한 Flux 아키텍처를 공개하게 되었으며, 이러한 경험과 과정은 리액트의 컨셉에도 영향을 미쳤으리라 유추해봅니다.
“어떻게(how)" 페이지를 렌더링 하는지가 아닌 “페이지가 어떻게 보여야 하는지(what)“를 작성하는 것
리액트는 선언적이고 효율적인 UI 구축을 위해 개발된 라이브러리입니다.
불과 몇 년 전만 하더라도 jQuery와 같은 라이브러리를 사용하여 직접 DOM을 조작하고 UI를 업데이트하는 명령적인(Imperative) 방식이 일반적이었습니다. 이러한 방식은 간단한 기능에도 코드 복잡도가 크게 증가할 수 있고, 직접적인 DOM 조작으로 인해 예기치 않은 오류가 발생할 가능성이 존재했지만 크로스 브라우징 이슈 없이도 DOM API를 쉽고 직관적으로 다룰 수 있는 기능들을 제공하여 많은 인기를 끌었습니다.
그러나 시간이 흘러 웹 애플리케이션의 규모가 커지면서 사용자와의 상호작용에 따라 동적으로 UI가 변경되어야 하는 요구사항이 증가함에 따라 DOM을 직접, 빈번하게 조작하는 이러한 방식은 성능 문제로 이어질 수 있었습니다.
리액트는 이러한 문제점을 극복하고, 효율적이고 안정적인 UI 구축을 위해 단방향 데이터 흐름(one-way data flow) 기반으로 한 선언적인(Declarative) 방식을 선택하였습니다.
개발자가 각 컴포넌트가 어떤 역할을 하고 어떻게 구성되어야 하는지, 그리고 "페이지가 어떻게 보여져야 하는지"를 정의했다면
컴포넌트의 계층 구조에 따라 상위 컴포넌트에서 하위 컴포넌트로 흐르면서 상태 변화를 추적하고,
이에 맞게 렌더링하여 DOM을 업데이트하는 부분은 리액트에게 맡기는 방식으로 말이죠.
결과적으로 1) 상태 변화를 예측 가능하게 만들어 애플리케이션의 유지보수를 간편하게 만들어줄 뿐만 아니라 2) 컴포넌트와 데이터를 분리하여 컴포넌트의 독립성을 보장하고, 3) "렌더링할 책임"을 개발자에서 리액트로 넘김으로써(IoC) 개발 생산성과 확장성을 향상시킬 수 있게 되었습니다.
이외에도 리액트는 가상 DOM(Virtual DOM), 최적화 기능 등을 활용하여 사용자와의 상호작용을 통해 실제 DOM과 동기화되는 과정에서 발생할 수 있는 불필요한 DOM 조작을 최소화하여 변경 사항을 효율적으로 처리함으로써 성능 문제를 해결하였습니다.
글을 작성하며 개인적으로 프론트 아키텍처의 가장 최근 트렌드는? 이라는 테오님의 글을 재미있게 읽었습니다. 2022년에 작성된 글이지만 프론트엔드의 변천사를 쉽고 재미있게 설명해 주신 것 같아 흐름을 이해하며 인사이트를 넓히기에 좋은 글이라는 생각이 들었습니다.
단순히 'A는 B이다.'와 같은 방식으로 쉽고 빠르게 개발 지식을 습득하는 것에는 분명 한계가 존재한다고 생각하는 편입니다.
시간을 들이더라도 맥락을 이해하고, 어떤 논리적인 근거로 선택되었는지에 대한 사고를 직접 해보는 것이 중요하다는 생각에 단순한 개념에도 긴 글을 작성하게 되었습니다. 잘 적어보고자 덜어내고 수정하다 이쯤에서 글을 마쳐봅니다.