React 상태관리 라이브러리에는 여러가지 라이브러리가 있지만, 그 중 '표준'이라고 부를 수 있는 것은 내장 API인 CONTEXT API를 제외한다면 바로 Redux가 있을 것이다.
이번 글에서는 Redux의 기반이 된 개념부터, 구조와 장단점 까지 Redux에 대해 샅샅이 살펴보고자 한다.
먼저 redux의 기초가 된 flux 개념을 알아보자.
Controller는 어플리케이션의 핵심 로직을 조종하는 두뇌로서, 유저의 입력을 받아 모델을 조작하고, Model은 Controller로부터 받은 명령에 따라 데이터를 제공하여 View에 업데이트하면, View는 변경된 데이터 및 내용을 사용자에게 보여주는 역할이다.
mvc 패턴은 각 구성요소가 양방향으로 통신하게 되어있다.
그러나 점점 소프트웨어들이 고도화됨에 따라 어플리케이션들이 복잡해지고 규모가 커졌다. 그에 따라 view에 의해 조작되어지는 model의 개수와 조작 빈도가 많아졌는데, 양방향성의 구조로 인해 어플리케이션 관리에 어려움이 생기기 시작했다.
facebook은, 유저의 메시지함에 안읽은 메시지가 없음에도 알림에는 계속 떠있는 버그가 있다는 사실을 발견했다. 이것이 mvc 패턴의 양방향성 특징 때문에 발생하는 것을 깨닫고는, 데이터가 단방향으로만 흐를 수 있는 아키텍처를 설계하였다.(cf.그러나 일부 개발자들은 facebook 측이 mvc 패턴을 잘못 해석했다며 문제를 제기하기도 하였다. )
그것이 바로 Flux
패턴이다.
MVC의 데이터 흐름의 양방향성이 원인이었던 만큼, Flux 패턴의 가장 큰 특징은 데이터 흐름의 단방향성이다.
데이터의 변화 내용을 담은 Action 객체를 dispatcher가 데이터를 저장한 store에 전달한다. store는 action에 맞게 데이터를 변경하여 view에 반영한다. view는 UI를 통해 데이터를 유저에게 보여주고, 유저에게 받은 입력을 통해 데이터가 변경되어야 하는 것이 있다면 마찬가지로 Action객체를 생성하여 Dispatcher에 전달한다.
결국 Dispatcher라는 하나의 입구를 통해서만 데이터가 조작될 수 있기 때문에 이전보다 통제성을 갖고 소프트웨어 구조를 짤 수 있게 되었다.
Flux 패턴을 기반으로 한 대표적인 상태 관리 라이브러리이다.
Redux는 React 이외에도 자바스크립트 생태계에서 사용할 수 있게 구성된 라이브러리이지만, 이름에서도 느껴지듯이 (React + Flux) React 환경에서 최적의 궁합을 보여준다. 보통 react 에서는 React-Redux라는 통합 라이브러리와 함께 사용된다.
생활코딩 이고잉 님이 Redux에 대해 설명할 때 사용한 그림이다. 제일 이해가 쉬운 그림인 것 같아 가져왔다.
state
라는 은행의 장부가 있다. 이 장부는 아무나 수정할 수 없고 오로지 reducer
를 통해서만 수정할 수 있다.
dispatch
는 창구의 직원과 같다. 고객들이 요구하는 action
들을 받아서 접수하고, state
에 변화가 필요한 일이 있다면 이를 reducer
에 전달한다. 외곽의 store는 마치 은행 건물과 같다고 이해하면 좋을 듯 하다.
getState
란 state
장부를 열람하는 메서드이다. dispatch
와 같이 창구 직원이라 생각하면 되는데, 특별히 장부 열람을 전담하는 직원이라고 생각하면 되겠다.
모든 State가 하나의 store 객체에 저장된다는 원칙이다.
State를 직접 변경하는 것은 금지되어있고, 직접 접근할 때는 읽기 전용이다.
State를 변경하고자 할 때에는 반드시 dispatch에 action을 담아 요청해야한다.
상태 변화는 반드시 순수함수를 통해서 이뤄져야한다는 원칙이다.
여기서 순수함수란, 같은 input을 넣었을 때 항상 같은 output이 나오는 함수라고 생각하면 된다. 그 반대 개념으로는 '사이드 이펙트가 있다'라고도 하는데, 이 경우 같은 input을 넣더라도 다른 output이 리턴될 수 있는 함수가 이에 해당한다.
function sideEffect (inputNumber) {
return Math.random() + inputNumber
}
sideEffect(1); // ? ; 같은 input을 넣어도 다른 output이 나올 수 있음
sideEffect(1); // ?
sideEffect(1); // ?
sideEffect(1); // ?
React에서 Redux를 최적의 형태로 쓸 수 있게 도와주는 React-redux 라이브러리가 별도로 존재한다. 다음엔 react-redux에서 제공하는 대표 기능들이다.
React 컴포넌트들이 Redux store에 접근할 수 있는 컴포넌트이다. 내부적으로 Context API의 기능을 활용하고 있다.
Redux Store의 값을 React hook 형태로 가져올 수 있는 함수이다.
상태 값의 변화를 줄 수 있는 action을 인자로 받는, React Hook 형태의 dispatch 함수이다.
상태관리 라이브러리를 사용하는 주된 이유, 바로 props drilling을 피할 수 있다는 점이다.
state가 필요한 컴포넌트만 참조하기 때문에 불필요한 리렌더링을 막고 코드도 깔끔해질 수 있다.
브라우저 확장 프로그램인 Redux DevTools를 통해 상태 이력을 확인하고, 되돌리거나 재실행하는 등의 개발에 있어서 필요한 다양한 기능들을 지원한다. 또한, 리듀서 실행 이력 데이터를 json 파일로 다운로드 받을 수 있다.
React 외에도 javascript 기반의 single page application에서 모두 사용할 수 있는 라이브러리이다.
또, 다양한 미들웨어와 결합하여 확장성있게 사용할 수 있다. (ex. redux thunk, redux saga)
상태 변경이 action을 싣은 dispatch로만 이뤄지다보니, 어떤 action에서 상태가 변경되었는지 역추적하기 용이하다. 이는 유지보수의 편의성을 높이고 불변성을 유지하는데 있어서 도움이 된다.
본격적으로 사용하기 전, 셋팅해야할 보일러플레이트 코드가 너무 많다.
action, store, reducer, dispatch 등을 사전에 혹은 사용하려는 컴포넌트에서 셋팅하는 작업이 필요하다. (이를 사용하는 사용자들의 러닝 커브도 단점이라면 단점이겠다.)
이러한 장황한 보일러플레이트를 극복하기 위해 redux toolkit
이 등장했고, 지금은 공식문서에서 권장하는 사용방식이다.
비동기 처리에 있어서 별도의 미들웨어 라이브러리가 필요하며, 이는 React query, SWR의 등장과 대조되어 불편함으로 다가온다.
그렇다면 언제 redux를 사용하면 좋을까?
redux 공식문서에서는 아래와 같이 소개하고 있다.
You have reasonable amounts of data changing over time
시간에 따라 변화하는 상당한 양의 데이터가 있을 때
You need a single source of truth for your state
상태에 대한 단 하나의 진실의 창구가 필요할 때
You find that keeping all your state in a top-level component is no longer sufficient
가장 높은 레벨의 컴포넌트에서 상태 관리하는 것이 더이상 효율적이지 않다는 것을 깨달을 때
위 기준에 맞는지 자신의 프로젝트를 점검해보고, 사용하면 좋을 것이다.
https://redux.js.org/understanding/thinking-in-redux/three-principles#state-is-read-only