이전에 리덕스의 기본적인 개념에 대해 공부했었습니다.
전역 상태관리를 위한 라이브러리로, Flux패턴을 따른다.
하나의 리액트앱에는 하나의 스토어가 있다.
state 값은 직접 바꾸지 않고, setState 함수를 이용해서만 변경한다.
순수함수 (pure function)
항상 같은 인풋은 같은 아웃풋을 반환하는 함수
따라서 데이터를 관리하는데 적합한 라이브러리는 아니다.
로컬 스토리지에 데이터를 저장하고 불러오는 대신 별도로 데이터를 관리하려면 별도의 라이브러리가 필요하다.
뷰와 모델, 즉 화면과 데이터를 연결하는 것을 뜻한다.
예를들어 JQuery나 DOM API는 직접 돔을 조작해 바꾸기 떄문에 화면과 데이터를 연결해 표시된 내용을 바꾸지 않는다.
모델 -> 뷰로만 데이터가 흐르는 것으로, 리액트가 이에 속한다.
예를들어, 모델, 즉 데이터인 state를 업데이트하면 해당 state를 표시하고 있는 화면의 영역(뷰)이 그에 맞게 리렌더링 되면서 업데이트된다.
Vue가 양방향 데이터 바인딩 방식을 사용
따라서 상위 컴포넌트에서 하위컴포넌트로 계속해서 props를 내려주거나, 하위 컴포넌트에서 바로 상위 컴포넌트의 state에 접근하여 변경하기 어려운 단점을 보완하기 위해 Redux를 사용하는 것이다.
관심사의 분리를 위해 탄생한 패턴
즉, 코드를 단순화하고 유지보수를 자유롭게 할 수 있도록 하기 위해 개발한 패턴이라고 할 수 있다.
같은 모델이어도 쓰이는 뷰가 여러가지일 경우 1:1매칭이 되지 않아 구조가 복잡해졌다.
이미지 추가 예정
이러한 단점을 보완하기 위해 새로운 패턴이 등장했는데, FLUX 패턴이다.
단방향 데이터 흐름으로 만든 패턴이다.
Dispatcher > stores > views
dispatcher를 통해 action을 발행한다.
어플리케이션의 데이터와 비지니스 로직을 가지고 있는 store
화면에 표시
사용자의 입력이나 UI 조작, 웹 요청 완료 같이 어떠한 상태변화를 일으킬 수 있는 형상
어떠한 조작을 일으킬 것인지에 대한 정보를 갖고 있는 자바스크립트 객체
action은 객체이지만 객체리터럴로 선언하지는 않는다.
별도의 액션 생성함수(action creator)를 통해 만든다.
액션 생성함수가 별도로 있는 것은 아니고, action을 리턴하는 함수를 액션 생성함수라고 부른다.
아직 와닿지 않는다.. 예시를 보자.
function showModal({ title }) {
return { type: 'SHOW_MODAL', title }
}
showModal({ title: '로그인' });
reducer는 action이 발생했을 때 state를 변화시키기 위한 함수이다.
즉 reducer는 새로운 state를 반환하는 함수
state와 action을 인자로 받아 이 action의 type에 따라 state를 변화시킨다.
function modal(state, action) {
switch(action.type) {
case 'SHOW_MODAL': // SHOW_MODAL 이라는 action이 발행되면
return {
...state,
showModal: true // 전역 state 중에서 showModal의 값을 true로 바꾼다.
};
case 'CLOSE_MODAL':
return {
...state,
showModal: false
};
default:
return state;
}
}
store는 앱에 오직 하나만 있고, 이 store를 사용해 app의 전체 상태를 관리한다.
store안에는 현재 상태들, 리듀서, 그리고 몇 가지의 내장함수를 포함하고 있다.
import { createStore } from 'redux'
import modalReducer from './ModalReducer';
const store = createStore(modalReducer);
디스패치는 스토어의 내장함수 중 하나로, 액션을 발생시키는 역할을 한다.
이 디스패치가 액션을 발생시켜 스토어에게 상태변화가 필요하다는 것을 알린다.
이렇게 액션이 호출되면 이 액션은 리듀서 함수를 호출시키고, 액션에 맞는 로직대로 상태를 변화시키는 과정을 거친다.
리액트 리덕스에서 제공하는 useDispatch 메서드를 임포트해 사용하면 생성한 액션을 발생시켜 액션 객체를 바로 내보낼 수 있다. 이렇게 되면 dispatch를 통해 reducer에서 액션타입에 따라 정의해둔 동작을 실행하게 되고, 바뀐 state가 store에 저장된다.
useSelector
connect함수를 이용하지 않고 리덕스 스토어의 상태를 조회할 수 있는 훅이다. 이 훅을 사용해 상태를 조회하면 상태값이 변경되지 않으면 리렌더링이 발생하지 않는다.
클라이언트에서 요청받고 응답하는 사이에 위와 같이 거쳐가는 여러함수들을 의미
예를 들면 CORS처리, 라우트 처리, 오류 처리 등등 응답과 요청 사이에 다양한 기능을 추가할 수 있다.
리덕스의 미들웨어는 action과 reducer사이, action을 dispatch하고 reducer로 state가 변경되기 전 그 사이에서 여러 작업을 한다.
로그를 남기거나 요청을 비동기로 처리하는 등의 작업을 할 수 있다.
redux toollkit과 함께 리덕스 미들웨어 중 redux-saga를 사용하는 방법에 대해서도 공부했는데, 해당 내용은 다음 포스팅에 정리해두었습니다.
👉 포스팅 보러가기
Redux-thunk와 Redux-saga는 대표적인 미들웨어들로, 비동기 요청을 처리하는 작업에 많이 쓰이는데, 이 들의 차이에 대해서는 다음 포스팅에 정리해두었습니다.
👉 포스팅 보러가기
Recoil은 atoms로 state를 관리하고 각 컴포넌트에서 필요한 atom을 구독한다.
또 useState을 사용하듯이 간편하게 읽어온 state를 업데이트 할 수 있기 때문에 러닝커브가 상대적으로 낮고 빠르게 도입할 수 있다.
만약 단순한 상태관리를 할 목적이라면 context API나 Recoil을 사용하는 것이 더 좋은 선택이라고 생각한다. 2주간의 프로젝트를 하면서 후반부에 전역상태관리를 도입해야했을 때 이러한 이유로 Recoil을 선택하기도 했었다.
다만, Redux-toolkit을 사용해보니 기존 리덕스보다 간편하게 사용할 수 있어 이러한 부분은 조금 해소가 될 것 같다.
여기까지가 직접 사용해보면서 느낀 장단점인데, 프로젝트 규모가 크지 않아 그 이상의 차이점에 대해서는 느끼기 어려웠다.
이 외에도 캐싱지원, 에러 핸들링 등의 측면에서 Redux와 비교해 Recoil이 아쉬운 점이 있다고 하는데, 아직 경험해보지 않아 와닿지는 않는다. 추가로 프로젝트에서 사용할 기회가 생긴다면 둘의 차이점을 비교해보아야겠다.
둘 다 전역 상태관리를 위한 도구이며, Redux 또한 Context API를 가지고 만든 라이브러리이다.
비동기적으로 전체 state를 갖고 와야할 때나, 복잡하게 state를 변경해야한다거나, 프로젝트 규모가 커질 때 context API를 사용하는 것은 쉽지 않다.
(리덕스와 contextAPI를 같이 쓰는 경우도 있기는 하다고 한다.)
리덕스는 Context API와 다르게 전역상태관리외에도 다양한 기능을 제공한다.
특히 redux-saga, redux-thunk와 같은 미들웨어와 redux-devtools 등 다양한 추가 라이브러리를 사용해 상태관리를 수월하게 하고 부가적인 작업을 효율적으로 처리할 수 있다.
즉, 이렇게 정리해볼 수 있을 것 같다.