0w0

flux와 가상 DOM에 대해서

React 다루다보면 Flux를 만난다. 그래서 이에 대해 조사해보기로 했다.

가상 DOM

먼저 Flux를 만나기 전에, React하면 떠오르는 친구인 가상 DOM을 보자.

공식문서에 의하면

Virtual DOM (VDOM)은 UI의 이상적인 또는 “가상”적인 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 “실제” DOM과 동기화하는 프로그래밍 개념입니다. 이 과정을 재조정이라고 합니다.

이 접근방식이 React의 선언적 API를 가능하게 합니다. React에게 원하는 UI의 상태를 알려주면 DOM이 그 상태와 일치하도록 합니다. 이러한 방식은 앱 구축에 사용해야 하는 어트리뷰트 조작, 이벤트 처리, 수동 DOM 업데이트를 추상화합니다.

“virtual DOM”은 특정 기술이라기보다는 패턴에 가깝기 때문에 사람들마다 의미하는 바가 다릅니다. React의 세계에서 “virtual DOM”이라는 용어는 보통 사용자 인터페이스를 나타내는 객체이기 때문에 React elements와 연관됩니다. 그러나 React는 컴포넌트 트리에 대한 추가 정보를 포함하기 위해 “fibers”라는 내부 객체를 사용합니다. 또한 React에서 “virtual DOM” 구현의 일부로 간주할 수 있습니다.

한 마디로, DOM과 별개로 가상의 표현을 두고, 그것을 추상적으로 조작하며 재조정하는 것이다.

추가: DOM은 그래프이론에서 온 것으로 애플리케이션이 복잡해지고 조작이 많아지면 페이지 렌더링 시에 계산량이 폭발해서 성능 저하가 일어난다. 그래서 변경 전, 변경 후를 가지고 차이만 갱신하면 적은 계산량으로 렌더링할 수 가능하다는 아이디어에서 출발한 것으로 UI의 가상적인 표현을 메모리에 저장하고, React를 통해 실제 DOM과 동기화하는 개념

DOM

그럼 애초에 DOM은 무엇일까?

MDN를 보자

문서 객체 모델(The Document Object Model, 이하 DOM) 은 HTML, XML 문서의 프로그래밍 interface 이다. DOM은 문서의 구조화된 표현(structured representation)을 제공하며 프로그래밍 언어가 DOM 구조에 접근할 수 있는 방법을 제공하여 그들이 문서 구조, 스타일, 내용 등을 변경할 수 있게 돕는다. DOM 은 nodes와 objects로 문서를 표현한다. 이들은 웹 페이지를 스크립트 또는 프로그래밍 언어들에서 사용될 수 있게 연결시켜주는 역할을 담당한다.

웹 페이지는 일종의 문서(document)다. 이 문서는 웹 브라우저를 통해 그 내용이 해석되어 웹 브라우저 화면에 나타나거나 HTML 소스 자체로 나타나기도 한다. 동일한 문서를 사용하여 이처럼 다른 형태로 나타날 수 있다는 점에 주목할 필요가 있다. DOM 은 동일한 문서를 표현하고, 저장하고, 조작하는 방법을 제공한다. DOM 은 웹 페이지의 객체 지향 표현이며, 자바스크립트와 같은 스크립팅 언어를 이용해 DOM 을 수정할 수 있다.

한 마디로, 형식있는 (웹)문서를 조작하는 표현이다.

가상 흔히 쓰는 방법은 이하와 같이 무엇인가 선택하거나, 만들어서 요소를 확인/추가/변경/삭제 등등을 하는 것

const element = document.querySelector('무엇인가');
element.children;
const div = document.createElement('div');
div.textContent = 'test';

Flux

데이터 흐름을 처리하는 방법이다.

또한 Facebook이 클라이언트 사이드의 Web 애플리케이션을 구축하기 위해 사용하고 있는 애플리케이션 아키텍처이다.

단방향 모델이며, MVC는 금방 복잡화되기 때문에 facebook(현 Meta)는 MVC 패턴을 버리고 Flux 패턴을 선택했다.

왜 그렇게 했는가 영상에서는 이렇게 설명한다. (물론 facebook인 MVC를 오독을 했다는 의견도 있다.)

MVC를 작은 애플리케이션에 적용했지만 이런 식으로 복잡해졌다.

MVC

이렇게되면 모델관련 뷰가 대량으로 추가되어 복잡화가 폭발했다.

이러한 애플리케이션은 모델과 뷰 사이의 쌍방향 데이터 흐름이 만들어질 가능성이 있으므로, 이해하거나 디버그가 어려워진다. 그렇기에 Flux에 의한 설계를 대안으로 선택했다.

Flux Basic

Flux + Action

이 그림에서

Store는 애플리케이션의 모든 데이터를 포함하고 있으며,

DispatcherMVC 그림의에서 Controller를 갈아끼우므로 어떤 Action이 일어날 때,

어떻게 Store를 갱신할까 결정하는 것이다. Store가 변경될 때는 View도 동시에 갱신되어 그것과 함께 Dispatcher가 처리될 Action를 발생할 수 있다. 이로인해 시스템의 컴포넌트 사이에 단방향 데이터흐름만 생긴다는 보장한다.

복수의 Store, View를 갖는 시스템도, 하나의 Store, View를 갖은 시스템 모두 같다 볼 수 있다.

왜냐면 데이터는 단일한 방향으로만 흐르며, 각 Store, View가 서로 직접 영향을 미치지 않기 때문이다.

Facebook이 직접 밝힌 상세한 설명을 보자.

dispatcher는 Flux 애플리케이션 모든 데이터 흐름을 관리하는 중앙 허브이다.

이는 본질적으로는 Store 안에 콜백을 등록하는 곳이다.

각 Store는 각 자신을 등록한 제공을 제공한다.

이 dispatcher가 어떤 액션에 대응할 때 애플리케이션 안에 모든 Store는, 그곳에 등록되어 있는 콜백에 의해 액션으로인해 생긴 데이터를 송신하는 것이다.

애플리케이션 성장에 따라, 결정된 순서로 등록된 콜백 실행을 하므로, Store 간의 의존관계를 관리하는 dispatcher는 더욱 불가결한 것이 된다.

Store는 다른 Store 갱신이 완료할 때까지 갖을 수 있다.

그리고 그 후, 자신을 갱신한다.

Store는 애플리케이션 상태와 로직을 포함한다.

Store 역할은 고전적인 MVC에 의해 Model의 역할과 닮아있지만,

여러 객체 상태를 관리해서, 하나의 객체 인스턴스가 아닌 것이 차이이다.

또한 Backbone 프레임워크 콜랙션과 같지는 않다.

ORM 형식 객체 집합을 관리하는 것보다 단순하며,

Store는 애플리케이션 안에 특정 도메인에 대한 애플리케이션 상태관리한다.

일반적인 사용 예제는 이러하다

Flux React Example

여담으로 observer 패턴이다.

:::note dispatcher? : 사전적 의미 발송자

dispatcher는 스토어 사이 의존관계를 관리하는 것.

Flux에서는 View / Model의 증가 대신에 Action과 Store가 증가한다.

그러기에 모든 데이터 처리가 dispatcher에 집약된다.

등록된 콜백함수에 페이로드를 브로드캐스트하기 위함.

dispatcher.js

dispatcher는 action을 받아, dispatcher에 등록된 action를 dispatch한다.

모든 store는 모든 action를 받는다.

각 애플리케이션에는 싱글톤 dispatcher가 하나만 존재한다.

예시:

  1. 유저가 todo를 입력하고 Enter를 입력
  2. view는 이 이벤트를 포착해서, todo 입력을 포함하는 add-todo action를 dispatch한다.
  3. 모든 store가 이 action을 받는다.

그래서 Action에 의한 Store 변경이라는 점이 명백하기 때문에, Action 이후 애플리케이션 상태가 예측가능해진다.

flux image

Store가 증가하고, Store는 다른 스토어 갱신이 끝날 때까지 View에 전달하지 않는다. 또한 Store는 다른 Store에 계층구조적으로 의존한다.

그래서 Store는 다른 Store의 의존에 신경쓰지 않은 상태로 순수하게 데이터 갱신만 생각하면 된다.

또한 애플리케이션 전체 관점으로도 데이터를 다룰 때는 Store만 갱신하면 된다. Store 갱신은 부작용이 없도록 데이터를 갱신을 하므로, 같은 데이터인데 어느쪽은 오래되었고, 어느 쪽은 새로운 상황 같은 것이 사라진다.

새로운 View, Action 등 코드를 작성해야하지만 복잡하고 예측 불가능한 상황이 일어나기 어려워지므로 충분히 이득이 상황이다. :::

정리

MVC

C -> (M <-> V)

Flux

Views ---> (actions) ----> Dispatcher ---> (registered callback) ---> Stores -------+
Ʌ                                                                                   |
|                                                                                   V
+-- (Controller-Views "change" event handlers) ---- (Stores emit "change" events) --+

그러나 Flux와 MVC는 완전히 다른 개념이 아니라, 지금까지 개념의 연장선이다.

단지 복잡화되기 쉬운 부분을 명확히 했을 분이다.

React와 Flux와 가상 DOM

React는 왜 가상 DOM일까?

어느정도의 설계와 속도 양립이 가능하기 때문.

이를 위한 방법으로 완성된 가상 DOM에 계속 Push한다

이 방법은 HTTP 리퀘스트 => HTML 반환 조작과 같다(!)

이를 위한 수단으로 선택한 것이 Flux이다.

결국 Flux와 가상 DOM의 조합으로 인해서

가상 DOM에 의해 퍼포먼스면에서 문제없이 늘 제로에서 상태를 구축이 가능하다.

대신, 데이터 바인드와 퍼포먼스, 이벤트 핸들러의 사정으로 이전의 HTML를 버리는 것은 불가능했다.

제로에서 구축해 HTML를 push하면, 화면이 한 번 사라지는데다가,

이벤트 핸들러 처리를 반드시 한 번 처리해야 한다.

하지만 가상 DOM에서는 이벤트 핸들러도, 동시에 적용되고 patch할 대상이므로 신경쓸 일은 아니다.

번외: Redux

Redux는 Flux의 파생이다.

하지만 차이가 있다.

Redux에는 3원칙이 있으며, Dispatcher는 없다.

3원칙:

또 State 객체를 직접 변환하지 않고 새로운 state 객체로 갈아끼우는 방법을 취한다.

Flux

Flux

Redux

Redux

읽을거리