부모 컴포넌트에서 자식으로 속성을 통한 단방향 데이터 흐름 은 리액트의 핵심 철학 중 하나입니다. 자식이 부모 컴포넌트로 데이터를 전달해야 할 떄는 자식이 이용할 콜백 함수를 통해 전달할 수 있습니다.
이 단방향 데이터 흐름은 명확하고 이해하기 쉬운 코드를 작성하는데 도움을 줍니다. 즉, 리액트 애플리케이션의 실행을 처음부터 끝까지 추적해보면 어떤 상황에 어떤 코드가 실행되는지 쉽게 확인할 수 있습니다.
이 아키텍처 패턴은 장점도 많지만, 해결해야 할 과제가 있습니다. 리액트 애플리케이션은 일반적으로 최상위 컴포넌트가 컨테이너로서 작동하며, 그 아래에 다수의 순수 컴포넌트가 인터페이스의 리프처럼 작동하는 다중 중첩 단계로 성장합니다. 상태가 계층의 최상위 수준에 유지되므로 속성을 통해 여러 단계로 전달해야 하는 콜백을 만들어야 하는 경우 가 많은데, 이 작업은 반복적이며 오류가 발생할 가능성이 높습니다.
리액트 라우터의 공동 개발자이자 커뮤니티에서 잘 알려진 개발자인 라이언 플로렌스는 데이터와 콜백을 속성을 통해 다단계로 전달하는 개념을 애플리케이션 드릴링이라고 비유했습니다. 여러 중첩 컴포넌트로 애플리케이션을 구성하려면 많은 드릴 작업이 필요하며, 리팩터링이 필요한 경우(컴포넌트를 다른곳으로 이동) 더 많은 드릴 작업을 다시 해야 합니다.
그런데 중첩된 리액트 컴포넌트가 UI를 구성하는 훌륭한 방법이라는 점은 확실하게 해둘 필요가 있습니다. 리액트 컴포넌트를 중첩해 구성하면 복잡성을 줄이고 관심사를 분리하는 데 도움이 되며, 코드를 확장하고 유지 관리하기도 편합니다. 리액트는 반응형 렌더링의 개념에 기반을 두고 작동하므로, 컴포넌트의 상태나 속성의 모든 변경에 반응해 DOM을 업데이트합니다(가상 DOM 구현을 통해 필요한 최소한의 변경을 계산). 즉, 아주 간단한 개념을 바탕으로 개발하면서 부수적으로 탁월한 성능까지 얻을 수 있습니다.
중첩된 컴포넌트 기반의 복잡한 애플리케이션을 개발할 때 데이터와 이러한 데이터를 조작하는 콜백을 해당하는 각 컴포넌트를 관리할 방법 이 필요해 졌고, 이를 위해 등장한 것이 바로 플럭스(Flux) 입니다.
플럭스(Flux)란?
플럭스(Flux)는 웹 애플리케이션을 개발하기 위한 아키텍처 가이드라인으로서 페이스북에서 만들었습니다. 리액트 전용은 아니며 리액트의 일부도 아니지만 리액트와 아주 잘 어울립니다.
플럭스의 핵심 개념은 애플리케이션에서 단방향 데이터 흐름을 지원하는 것입니다. 플럭스는 기본적으로 액션(action), 스토어(store), 디스패처(dispatcher)의 세 부분으로 구성됩니다.
스토어
앞에서 언급했듯이, 플럭스의 핵심은 데이터를 애플리케이션의 각 컴포넌트와 밀접하게 관리하는 것입니다. 데이터는 컴포넌트와 완전히 분리되지만 데이터가 변경될 때마다 다시 렌더링할 수 있도록 알림을 받습니다.
스토어가 하는 일이 바로 이것입니다. 스토어는 애플리케이션의 모든 상태(데이터와 UI 상태를 포함)를 유지하며 상태가 변경되면 이벤트를 발송(dispatch) 합니다. 뷰(리액트 컴포넌트)는 필요한 데이터를 포함하는 스토어를 구독하며 데이터가 변경되면 자신을 다시 렌더링합니다.
스토어는 완전히 폐쇄된 블랙박스라는 중요한 특징을 갖고 있습니다. 데이터에 접근하기 위한 공용 접근자 메서드(getter)를 제공하지만, 뷰는 물론이고 플럭스의 다른 부분에서도 스토어의 데이터를 변경, 갱신, 삽입할 수 없습니다. 스토어 자체만 데이터를 변경할 수 있습니다.
MVC 패러다임에 대해 알고 있다면 이 패러다임의 모델(model)과 비슷한 측면이 있지만, 스토어는 접근자 메서드만 제공한다는 점이 가장 큰 차이입니다. 즉, 외부에서는 스토어의 값을 변경할 수 없습니다.
그런데 애플리케이션의 다른 부분에서 스토어의 데이터를 변경할 수 없다면 스토어의 상태를 변경하려면 어떻게 해야할까요? 이에 대한 해답이 액션입니다.
액션
액션을 느슨하게 정의하면 “앱에서 일어나는 일”이라고 할 수 있습니다. 액션은 애플리케이션의 거의 모든 부분에서 생성될 수 있으며, 사용자 상호작용(예: 버튼 클릭, 댓글 달기, 검색 결과 요청 등)에서 주로 생성되지만 Ajax 요청, 타이머, 웹소켓 이벤트 등을 통해서도 생성됩니다.
모든 액션은 타입(고유한 이름)과 선택적인 페이로드를 포함합니다. 스토어는 액션이 발송되면 자신의 데이터를 업데이트합니다.
이로써 플럭스의 중요한 부분을 거의 모두 소개했습니다. 리액트 컴포넌트가 액션을 사용하려면(예: 사용자가 텍스트 필드에 이름을 입력) 액션이 스토어로 전달되며, 이 특정 액션과 관계가 있는 스토어가 자신의 데이터를 업데이트하고 변경 이벤트를 발송합니다. 그러면 마지막으로 뷰가 해당 스토어의 이벤트에 반응해 최신 데이터로 자신을 다시 렌더링합니다. 그런데 이 다이어그램에는 디스패처라는 마지막 조각이 빠져있습니다.
디스패처
디스패처는 액션을 스토어로 전달하는 과정을 조율하고 스토어의 액션 핸들러가 올바른 순서로 실행되도록 관리합니다.
디스패처는 플럭스 아키텍처에서 핵심적인 역할을 하지만 개발자는 디스패처가 하는 일에 대해 거의 고려할 필요가 없습니다. 디스패처로 사용할 인스턴스를 만들기만 하면 디스패처 구현이 모든 작업을 알아서 처리합니다.